This commit is contained in:
mgabdev 2020-04-07 21:06:59 -04:00
parent b5e3c2a94f
commit bb4fcdf32d
101 changed files with 1069 additions and 1886 deletions

View File

@ -10,6 +10,13 @@ module.exports = {
globals: {
ATTACHMENT_HOST: false,
_s: true,
PropTypes: true,
PureComponent: true,
React: {
Component: true,
},
connect: true,
},
parser: 'babel-eslint',
@ -98,8 +105,9 @@ module.exports = {
classes: 'always',
},
],
'prefer-const': 'error',
quotes: ['error', 'single'],
semi: 'error',
semi: 'off',
strict: 'off',
'valid-typeof': 'error',

View File

@ -1,7 +1,6 @@
import api from '../api';
import { CancelToken, isCancel } from 'axios';
import { throttle } from 'lodash';
import moment from 'moment';
import throttle from 'lodash.throttle'
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
import { urlRegex } from '../features/compose/util/url_regex'
import { tagHistory } from '../settings';
@ -133,7 +132,7 @@ export function mentionCompose(account, routerHistory) {
export function handleComposeSubmit(dispatch, getState, response, status) {
if (!dispatch || !getState) return;
const isScheduledStatus = response.data['scheduled_at'] !== undefined;
const isScheduledStatus = response.data.scheduled_at !== undefined;
if (isScheduledStatus) {
// dispatch(showAlertForError({
// response: {
@ -154,7 +153,7 @@ export function handleComposeSubmit(dispatch, getState, response, status) {
const timeline = getState().getIn(['timelines', timelineId]);
if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) {
let dequeueArgs = {};
const dequeueArgs = {};
if (timelineId === 'community') dequeueArgs.onlyMedia = getState().getIn(['settings', 'community', 'other', 'onlyMedia']);
dispatch(dequeueTimeline(timelineId, null, dequeueArgs));
dispatch(updateTimeline(timelineId, { ...response.data }));
@ -174,7 +173,7 @@ export function submitCompose(routerHistory, group) {
if (!me) return;
let status = getState().getIn(['compose', 'text'], '');
let statusMarkdown = getState().getIn(['compose', 'text_markdown'], '');
const statusMarkdown = getState().getIn(['compose', 'text_markdown'], '');
const media = getState().getIn(['compose', 'media_attachments']);
// : hack :
@ -183,10 +182,10 @@ export function submitCompose(routerHistory, group) {
const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
return hasProtocol ? match : `http://${match}`
})
statusMarkdown = statusMarkdown.replace(urlRegex, (match) =>{
const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
return hasProtocol ? match : `http://${match}`
})
// statusMarkdown = statusMarkdown.replace(urlRegex, (match) =>{
// const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
// return hasProtocol ? match : `http://${match}`
// })
dispatch(submitComposeRequest());
dispatch(closeModal());
@ -197,12 +196,13 @@ export function submitCompose(routerHistory, group) {
: `/api/v1/statuses/${id}`;
const method = id === null ? 'post' : 'put';
let scheduled_at = getState().getIn(['compose', 'scheduled_at'], null);
if (scheduled_at !== null) scheduled_at = moment.utc(scheduled_at).toDate();
const scheduled_at = getState().getIn(['compose', 'scheduled_at'], null);
// : todo :
// if (scheduled_at !== null) scheduled_at = moment.utc(scheduled_at).toDate();
api(getState)[method](endpoint, {
status,
statusMarkdown,
// statusMarkdown,
scheduled_at,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
quote_of_id: getState().getIn(['compose', 'quote_of_id'], null),

View File

@ -16,7 +16,6 @@ import { me } from '../initial_state';
export const NOTIFICATIONS_INITIALIZE = 'NOTIFICATIONS_INITIALIZE';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
@ -76,7 +75,6 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
return (dispatch, getState) => {
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const filters = getFilters(getState(), { contextType: 'notifications' });
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
let filtered = false;
@ -101,13 +99,6 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
});
}
if (playSound && !filtered) {
dispatch({
type: NOTIFICATIONS_UPDATE_NOOP,
meta: { sound: 'ribbit' },
});
}
if (isOnNotificationsPage) {
dispatch({
type: NOTIFICATIONS_UPDATE_QUEUE,
@ -115,8 +106,7 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
intlMessages,
intlLocale,
});
}
else {
} else {
dispatch(updateNotifications(notification, intlMessages, intlLocale));
}
}
@ -127,15 +117,13 @@ export function dequeueNotifications() {
const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList());
const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0);
if (totalQueuedNotificationsCount == 0) {
if (totalQueuedNotificationsCount === 0) {
return;
}
else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
} else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
queuedNotifications.forEach(block => {
dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale));
});
}
else {
} else {
dispatch(expandNotifications());
}
@ -168,9 +156,9 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
return;
}
console.log("activeFilter:", activeFilter)
console.log("excludeTypesFromSettings(getState()):", excludeTypesFromSettings(getState()))
console.log("excludeTypesFromFilter(activeFilter):", excludeTypesFromFilter(activeFilter))
console.log('activeFilter:', activeFilter)
console.log('excludeTypesFromSettings(getState()):', excludeTypesFromSettings(getState()))
console.log('excludeTypesFromFilter(activeFilter):', excludeTypesFromFilter(activeFilter))
// : todo :
// filter verified and following here too
@ -268,7 +256,7 @@ export function markReadNotifications() {
const last_read = getState().getIn(['notifications', 'lastRead']);
if (top_notification && top_notification > last_read) {
api(getState).post('/api/v1/notifications/mark_read', {id: top_notification}).then(response => {
api(getState).post('/api/v1/notifications/mark_read', { id: top_notification }).then(response => {
dispatch({
type: NOTIFICATIONS_MARK_READ,
notification: top_notification,

View File

@ -1,5 +1,5 @@
import api from '../api';
import { debounce } from 'lodash';
import debounce from 'lodash.debounce';
// import { showAlertForError } from './alerts';
import { me } from '../initial_state';

View File

@ -29,7 +29,7 @@ export const fetchGifCategories = () => {
}
}
export const fetchGifResults = () => {
export const fetchGifResults = (maxId) => {
return function (dispatch, getState) {
if (!me) return
@ -39,7 +39,7 @@ export const fetchGifResults = () => {
axios.get(`https://api.tenor.com/v1/search?q=${searchText}&media_filter=minimal&limit=30&key=${tenorkey}`)
.then((response) => {
console.log("response:", response)
console.log('response:', response)
dispatch(fetchGifResultsSuccess(response.data.results))
}).catch(function (error) {
dispatch(fetchGifResultsFail(error))

View File

@ -55,29 +55,24 @@ export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
let shouldDispatchDequeue = true;
if (totalQueuedItemsCount == 0) {
if (totalQueuedItemsCount === 0) {
return;
}
else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
} else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
queuedItems.forEach(status => {
dispatch(updateTimeline(timeline, status.toJS(), null));
});
}
else {
} else {
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timeline));
expandFunc();
}
else {
} else {
if (timeline === 'home') {
dispatch(clearTimeline(timeline));
dispatch(expandHomeTimeline(optionalExpandArgs));
}
else if (timeline === 'community') {
} else if (timeline === 'community') {
dispatch(clearTimeline(timeline));
dispatch(expandCommunityTimeline(optionalExpandArgs));
}
else {
} else {
shouldDispatchDequeue = false;
}
}

View File

@ -14,7 +14,7 @@ export const getLinks = response => {
return LinkHeader.parse(value);
};
let csrfHeader = {};
const csrfHeader = {};
function setCSRFHeader() {
const csrfToken = document.querySelector('meta[name=csrf-token]');

View File

@ -0,0 +1,28 @@
const HiddenIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 48 48',
title = 'Hidden',
}) => (
<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 23.632812 16.398438 L 30.503906 23.269531 L 30.539062 22.910156 C 30.539062 19.300781 27.605469 16.367188 23.996094 16.367188 Z M 23.632812 16.398438' />
<path d='M 23.996094 12.003906 C 30.015625 12.003906 34.902344 16.890625 34.902344 22.910156 C 34.902344 24.316406 34.617188 25.65625 34.125 26.890625 L 40.507812 33.269531 C 43.800781 30.523438 46.398438 26.964844 48 22.910156 C 44.214844 13.332031 34.914062 6.550781 23.996094 6.550781 C 20.941406 6.550781 18.019531 7.09375 15.300781 8.078125 L 20.015625 12.777344 C 21.246094 12.296875 22.585938 12.003906 23.996094 12.003906 Z M 23.996094 12.003906' />
<path d='M 2.179688 6.058594 L 7.15625 11.03125 L 8.148438 12.023438 C 4.546875 14.839844 1.703125 18.578125 0 22.910156 C 3.773438 32.484375 13.089844 39.269531 23.996094 39.269531 C 27.375 39.269531 30.605469 38.613281 33.558594 37.425781 L 34.488281 38.351562 L 40.84375 44.722656 L 43.625 41.953125 L 4.960938 3.277344 Z M 14.242188 18.109375 L 17.613281 21.480469 C 17.515625 21.949219 17.449219 22.417969 17.449219 22.910156 C 17.449219 26.519531 20.382812 29.453125 23.996094 29.453125 C 24.484375 29.453125 24.953125 29.386719 25.414062 29.289062 L 28.78125 32.660156 C 27.332031 33.378906 25.71875 33.816406 23.996094 33.816406 C 17.972656 33.816406 13.089844 28.929688 13.089844 22.910156 C 13.089844 21.1875 13.523438 19.570312 14.242188 18.109375 Z M 14.242188 18.109375' />
</g>
</svg>
)
export default HiddenIcon

View File

@ -93,6 +93,7 @@ class Account extends ImmutablePureComponent {
)
}
// : todo : cleanup
let buttonOptions
let buttonText

View File

@ -1,25 +1,25 @@
import unicodeMapping from '../emoji/emoji_unicode_mapping_light';
import unicodeMapping from '../emoji/emoji_unicode_mapping_light'
const assetHost = process.env.CDN_HOST || '';
const assetHost = process.env.CDN_HOST || ''
export default class AutosuggestEmoji extends PureComponent {
static propTypes = {
emoji: PropTypes.object.isRequired,
};
}
render () {
const { emoji } = this.props;
let url;
const { emoji } = this.props
let url
if (emoji.custom) {
url = emoji.imageUrl;
url = emoji.imageUrl
} else {
const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')];
const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')]
if (!mapping) return null;
if (!mapping) return null
url = `${assetHost}/emoji/${mapping.filename}.svg`;
url = `${assetHost}/emoji/${mapping.filename}.svg`
}
return (
@ -27,7 +27,7 @@ export default class AutosuggestEmoji extends PureComponent {
<img className='emojione' src={url} alt={emoji.native || emoji.colons} />
{emoji.colons}
</div>
);
)
}
}

View File

@ -2,8 +2,6 @@ import { Fragment } from 'react'
import ImmutablePropTypes from 'react-immutable-proptypes'
import classNames from 'classnames/bind'
import ImmutablePureComponent from 'react-immutable-pure-component'
import Textarea from 'react-textarea-autosize'
import ContentEditable from 'react-contenteditable'
import { isRtl } from '../../utils/rtl'
import { textAtCursorMatchesToken } from '../../utils/cursor_token_match'
import AutosuggestAccount from '../autosuggest_account'
@ -210,7 +208,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
id,
maxLength,
textarea,
prependIcon
prependIcon,
} = this.props
const { suggestionsHidden } = this.state
@ -307,6 +305,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
</div>
{children}
</div>
{ /* : todo : */ }
<div className='autosuggest-textarea__suggestions-wrapper'>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}

View File

@ -47,6 +47,7 @@ class Avatar extends ImmutablePureComponent {
const shouldAnimate = animate || !sameImg
const options = {
alt: !account ? 'Avatar' : account.get('display_name'),
className: [_s.default, _s.circle, _s.overflowHidden].join(' '),
onMouseEnter: shouldAnimate ? this.handleMouseEnter : undefined,
onMouseLeave: shouldAnimate ? this.handleMouseLeave : undefined,

View File

@ -1,4 +1,9 @@
export default class Block extends PureComponent {
static propTypes = {
children: PropTypes.node,
}
render() {
const { children } = this.props
@ -8,4 +13,5 @@ export default class Block extends PureComponent {
</div>
)
}
}

View File

@ -32,7 +32,7 @@ export default class ColumnHeader extends PureComponent {
title,
showBackBtn,
tabs,
actions
actions,
} = this.props
return (
@ -68,15 +68,15 @@ export default class ColumnHeader extends PureComponent {
{
actions.map((action, i) => (
<Button
radiusSmall
backgroundColor='tertiary'
onClick={() => action.onClick() }
backgroundColor='none'
color='secondary'
onClick={() => action.onClick()}
key={`column-header-action-btn-${i}`}
className={[_s.ml5, _s.px10].join(' ')}
iconClassName={_s.fillColorSecondary}
className={[_s.ml5, _s.fillColorBrand_onHover, _s.backgroundColorBrandLightOpaque_onHover, _s.px10].join(' ')}
icon={action.icon}
iconWidth='20px'
iconHeight='20px'
iconClassName={_s.inheritFill}
iconWidth='15px'
iconHeight='15px'
/>
))
}

View File

@ -22,33 +22,9 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => {
const status = getStatus(state, props)
let descendantsIds = Immutable.List()
if (status) {
// ALL descendants
descendantsIds = descendantsIds.withMutations(mutable => {
const ids = [status.get('id')]
while (ids.length > 0) {
let id = ids.shift();
const replies = state.getIn(['contexts', 'replies', id])
if (status.get('id') !== id) {
mutable.push(id)
}
if (replies) {
replies.reverse().forEach(reply => {
ids.unshift(reply)
});
}
}
})
}
return {
status,
descendantsIds,
}
}
@ -62,30 +38,22 @@ class Comment extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
descendantsIds: ImmutablePropTypes.list,
}
handleClick = () => {
// if (this.props.onClick) {
// this.props.onClick();
// return;
// }
// if (!this.context.router) return;
// this.context.router.history.push(
// `/${this._properStatus().getIn(['account', 'acct'])}/posts/${this._properStatus().get('id')}`
// )
indent: ImmutablePropTypes.number,
}
render() {
const { status } = this.props
const { status, indent } = this.props
console.log("status:", status)
const style = {
paddingLeft: `${indent * 40}px`,
}
// : todo : add media
return (
<div className={[_s.default, _s.px10, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}>
<div className={[_s.default].join(' ')}>
<div className={[_s.default].join(' ')} style={style}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<NavLink

View File

@ -1,159 +1,27 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { makeGetStatus } from '../selectors';
import CommentHeader from './comment_header'
import Avatar from './avatar'
import Button from './button'
import DisplayName from './display_name'
import DotTextSeperator from './dot_text_seperator'
import RelativeTimestamp from './relative_timestamp'
import Text from './text'
import StatusContent from './status_content'
import Comment from './comment'
const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
})
const makeMapStateToProps = () => {
const getStatus = makeGetStatus()
const mapStateToProps = (state, props) => {
const status = getStatus(state, props)
let descendantsIds = Immutable.List()
if (status) {
// ALL descendants
descendantsIds = descendantsIds.withMutations(mutable => {
const ids = [status.get('id')]
while (ids.length > 0) {
let id = ids.shift();
const replies = state.getIn(['contexts', 'replies', id])
if (status.get('id') !== id) {
mutable.push(id)
}
if (replies) {
replies.reverse().forEach(reply => {
ids.unshift(reply)
});
}
}
})
}
return {
status,
descendantsIds,
}
}
return mapStateToProps
}
export default
@injectIntl
@connect(makeMapStateToProps)
class Comment extends ImmutablePureComponent {
export default class CommentList extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
descendantsIds: ImmutablePropTypes.list,
}
handleClick = () => {
// if (this.props.onClick) {
// this.props.onClick();
// return;
// }
// if (!this.context.router) return;
// this.context.router.history.push(
// `/${this._properStatus().getIn(['account', 'acct'])}/posts/${this._properStatus().get('id')}`
// )
descendants: ImmutablePropTypes.list,
}
render() {
const { status } = this.props
console.log("status:", status)
const { descendants } = this.props
return (
<div className={[_s.default, _s.px10, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}>
<div className={[_s.default].join(' ')}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<NavLink
to={`/${status.getIn(['account', 'acct'])}`}
title={status.getIn(['account', 'acct'])}
className={[_s.default, _s.mr10, _s.pt5].join(' ')}
>
<Avatar account={status.get('account')} size={32} />
</NavLink>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.px10, _s.py5, _s.radiusSmall, _s.backgroundSubtle].join(' ')}>
<div className={_s.pt2}>
<CommentHeader status={status} />
</div>
<div className={[_s.py5].join(' ')}>
<StatusContent
status={status}
onClick={this.handleClick}
isComment
collapsable
<div>
{
descendants.map((descendant, i) => (
<Comment
key={`comment-${descendant.get('statusId')}-${i}`}
id={descendant.get('statusId')}
indent={descendant.get('indent')}
/>
</div>
</div>
<div className={[_s.default, _s.flexRow, _s.mt5].join(' ')}>
<Button
text
radiusSmall
backgroundColor='none'
color='tertiary'
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
>
<Text size='extraSmall' color='inherit' weight='bold'>
Like
</Text>
</Button>
<Button
text
radiusSmall
backgroundColor='none'
color='tertiary'
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
>
<Text size='extraSmall' color='inherit' weight='bold'>
Reply
</Text>
</Button>
<Button
text
radiusSmall
backgroundColor='none'
color='tertiary'
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
>
<Text size='extraSmall' color='inherit' weight='bold'>
···
</Text>
</Button>
</div>
</div>
</div>
</div>
))
}
</div>
)
}

View File

@ -1,6 +1,6 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import classNames from 'classnames/bind'
import { openPopover, closePopover } from '../actions/popover'
import Icon from './icon'

View File

@ -1,4 +1,4 @@
import { pick } from 'lodash';
import pick from 'lodash.pick'
import { emojiIndex } from 'emoji-mart';
import { search } from '../emoji_mart_search_light';

View File

@ -40,17 +40,6 @@ export default class FileInput extends PureComponent {
} = this.props
const { file } = this.state
const imageClasses = cx({
border2PX: 1,
borderDashed: 1,
borderColorSecondary: 1,
backgroundColorPrimary: 1,
px10: 1,
py10: 1,
radiusSmall: 1,
cursorPointer: 1,
})
return (
<div>
{
@ -63,7 +52,7 @@ export default class FileInput extends PureComponent {
}
<label
className={[_s.default, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}
className={[_s.default, _s.alignItemsCenter, _s.radiusSmall, _s.cursorPointer, _s.px10, _s.py10, _s.justifyContentCenter, _s.border2PX, _s.borderColorSecondary, _s.borderDashed].join(' ')}
htmlFor={`file-input-${title}`}
style={{
width,
@ -71,9 +60,7 @@ export default class FileInput extends PureComponent {
}}
>
<Image
className={imageClasses}
width={width}
height={height}
className={[_s.height100PC, _s.width100PC, _s.radiusSmall].join(' ')}
src={fileType === 'image' ? file : null}
/>
{

View File

@ -1,7 +1,7 @@
import Icon from './icon'
import Button from './button'
export default class FloatingActionButton extends Component {
export default class FloatingActionButton extends PureComponent {
static propTypes = {
onClick: PropTypes.func.isRequired,
message: PropTypes.string.isRequired,

View File

@ -30,6 +30,7 @@ export default
@connect(mapStateToProps)
@injectIntl
class GroupCollectionItem extends ImmutablePureComponent {
static propTypes = {
group: ImmutablePropTypes.map,
relationships: ImmutablePropTypes.map,
@ -50,14 +51,9 @@ class GroupCollectionItem extends ImmutablePureComponent {
</Fragment>
) : intl.formatMessage(messages.no_recent_activity)
const imageHeight = '180px'
const isMember = relationships.get('member')
const outsideClasses = cx({
default: 1,
width50PC: 1,
})
const isAdmin = relationships.get('admin')
const coverSrc = group.get('cover')
const navLinkClasses = cx({
default: 1,
@ -74,18 +70,30 @@ class GroupCollectionItem extends ImmutablePureComponent {
})
return (
<div className={outsideClasses}>
<div className={_s.default}>
<NavLink
to={`/groups/${group.get('id')}`}
className={navLinkClasses}
>
{
!!coverSrc &&
<Image
src={group.get('cover')}
src={coverSrc}
alt={group.get('title')}
height={imageHeight}
className={_s.height158PX}
/>
}
{
!coverSrc && (isMember || isAdmin) &&
<div className={[_s.default, _s.height40PX, _s.backgroundSubtle, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')} />
}
{
(isMember || isAdmin) &&
<div className={[_s.default, _s.flexRow, _s.positionAbsolute, _s.top0, _s.right0, _s.pt10, _s.mr10].join(' ')}>
{
isMember &&
<Text
badge
className={_s.backgroundColorWhite}
@ -94,6 +102,9 @@ class GroupCollectionItem extends ImmutablePureComponent {
>
{intl.formatMessage(messages.member)}
</Text>
}
{
isAdmin &&
<Text
badge
className={[_s.backgroundColorBlack, _s.ml5].join(' ')}
@ -102,7 +113,9 @@ class GroupCollectionItem extends ImmutablePureComponent {
>
{intl.formatMessage(messages.admin)}
</Text>
}
</div>
}
<div className={[_s.default, _s.px10, _s.my10].join(' ')}>
<Text color='primary' size='medium' weight='bold'>
@ -119,8 +132,6 @@ class GroupCollectionItem extends ImmutablePureComponent {
<Text color='secondary' size='small' className={_s.ml5}>
{subtitle}
</Text>
<DotTextSeperator />
<Text color='secondary' size='small' className={_s.ml5}></Text>
</div>
</div>
@ -128,4 +139,5 @@ class GroupCollectionItem extends ImmutablePureComponent {
</div>
)
}
}

View File

@ -25,6 +25,7 @@ export default
@connect(mapStateToProps)
@injectIntl
class GroupListItem extends ImmutablePureComponent {
static propTypes = {
group: ImmutablePropTypes.map,
relationships: ImmutablePropTypes.map,
@ -82,16 +83,22 @@ class GroupListItem extends ImmutablePureComponent {
mb10: !slim,
})
const coverSrc = group.get('cover')
return (
<NavLink
to={`/groups/${group.get('id')}`}
className={containerClasses}
>
{
(!!coverSrc || slim) &&
<Image
src={group.get('cover')}
src={coverSrc}
alt={group.get('title')}
className={imageClasses}
/>
}
<div className={textContainerClasses}>
<Text color='brand' weight='bold'>
@ -115,4 +122,5 @@ class GroupListItem extends ImmutablePureComponent {
</NavLink>
)
}
}

View File

@ -45,6 +45,7 @@ export default class HashtagItem extends ImmutablePureComponent {
text
backgroundColor='none'
color='none'
title='Remove'
icon='caret-down'
iconWidth='8px'
iconHeight='8px'

View File

@ -25,6 +25,7 @@ import GlobeIcon from '../assets/globe_icon'
import GroupIcon from '../assets/group_icon'
import GroupAddIcon from '../assets/group_add_icon'
import HappyIcon from '../assets/happy_icon'
import HiddenIcon from '../assets/hidden_icon'
import HomeIcon from '../assets/home_icon'
import InvestorIcon from '../assets/investor_icon'
import ItalicIcon from '../assets/italic_icon'
@ -64,70 +65,71 @@ import VerifiedIcon from '../assets/verified_icon'
import WarningIcon from '../assets/warning_icon'
const ICONS = {
'add': AddIcon,
'angle-right': AngleRightIcon,
'apps': AppsIcon,
'audio': AudioIcon,
'audio-mute': AudioMuteIcon,
'back': BackIcon,
'blockquote': BlockquoteIcon,
'bold': BoldIcon,
'calendar': CalendarIcon,
'chat': ChatIcon,
'close': CloseIcon,
'code': CodeIcon,
'comment': CommentIcon,
'copy': CopyIcon,
'dissenter': DissenterIcon,
'donor': DonorIcon,
'ellipsis': EllipsisIcon,
'email': EmailIcon,
'error': ErrorIcon,
'fullscreen': FullscreenIcon,
'gab-logo': GabLogoIcon,
'gif': GifIcon,
'globe': GlobeIcon,
'group': GroupIcon,
'group-add': GroupAddIcon,
'happy': HappyIcon,
'home': HomeIcon,
'investor': InvestorIcon,
'italic': ItalicIcon,
'like': LikeIcon,
'liked': LikedIcon,
'link': LinkIcon,
'list': ListIcon,
'list-add': ListAddIcon,
'loading': LoadingIcon,
'lock': LockIcon,
'lock-filled': LockFilledIcon,
'media': MediaIcon,
'minimize-fullscreen': MinimizeFullscreenIcon,
'missing': MissingIcon,
'more': MoreIcon,
'notifications': NotificationsIcon,
'ol-list': OLListIcon,
'pause': PauseIcon,
'pin': PinIcon,
'play': PlayIcon,
'poll': PollIcon,
'pro': ProIcon,
'repost': RepostIcon,
'rich-text': RichTextIcon,
'search': SearchIcon,
'search-alt': SearchAltIcon,
'share': ShareIcon,
'shop': ShopIcon,
'strikethrough': StrikethroughIcon,
'subtract': SubtractIcon,
'text-size': TextSizeIcon,
'trends': TrendsIcon,
'ul-list': ULListIcon,
'underline': UnderlineIcon,
'unlock-filled': UnlockFilledIcon,
'verified': VerifiedIcon,
'warning': WarningIcon,
'': CircleIcon,
'add': AddIcon,
'angle-right': AngleRightIcon,
'apps': AppsIcon,
'audio': AudioIcon,
'audio-mute': AudioMuteIcon,
'back': BackIcon,
'blockquote': BlockquoteIcon,
'bold': BoldIcon,
'calendar': CalendarIcon,
'chat': ChatIcon,
'close': CloseIcon,
'code': CodeIcon,
'comment': CommentIcon,
'copy': CopyIcon,
'dissenter': DissenterIcon,
'donor': DonorIcon,
'ellipsis': EllipsisIcon,
'email': EmailIcon,
'error': ErrorIcon,
'fullscreen': FullscreenIcon,
'gab-logo': GabLogoIcon,
'gif': GifIcon,
'globe': GlobeIcon,
'group': GroupIcon,
'group-add': GroupAddIcon,
'hidden': HiddenIcon,
'happy': HappyIcon,
'home': HomeIcon,
'investor': InvestorIcon,
'italic': ItalicIcon,
'like': LikeIcon,
'liked': LikedIcon,
'link': LinkIcon,
'list': ListIcon,
'list-add': ListAddIcon,
'loading': LoadingIcon,
'lock': LockIcon,
'lock-filled': LockFilledIcon,
'media': MediaIcon,
'minimize-fullscreen': MinimizeFullscreenIcon,
'missing': MissingIcon,
'more': MoreIcon,
'notifications': NotificationsIcon,
'ol-list': OLListIcon,
'pause': PauseIcon,
'pin': PinIcon,
'play': PlayIcon,
'poll': PollIcon,
'pro': ProIcon,
'repost': RepostIcon,
'rich-text': RichTextIcon,
'search': SearchIcon,
'search-alt': SearchAltIcon,
'share': ShareIcon,
'shop': ShopIcon,
'strikethrough': StrikethroughIcon,
'subtract': SubtractIcon,
'text-size': TextSizeIcon,
'trends': TrendsIcon,
'ul-list': ULListIcon,
'underline': UnderlineIcon,
'unlock-filled': UnlockFilledIcon,
'verified': VerifiedIcon,
'warning': WarningIcon,
'': CircleIcon,
}
export default class Icon extends PureComponent {
@ -148,4 +150,5 @@ export default class Icon extends PureComponent {
return <Asset {...options} />
}
}

View File

@ -1,21 +1,18 @@
import classNames from 'classnames/bind'
// : testing :
// : todo :
const placeholderSource = 'https://source.unsplash.com/random'
const imageUnavailable = 'https://source.unsplash.com/random'
const cx = classNames.bind(_s)
export default class Image extends PureComponent {
static propTypes = {
alt: PropTypes.string,
alt: PropTypes.string.isRequired,
src: PropTypes.string,
className: PropTypes.string,
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
fit: PropTypes.oneOf(['contain', 'cover', 'tile', 'none']),
nullable: PropTypes.bool,
lazy: PropTypes.bool,
}
static defaultProps = {
@ -32,29 +29,41 @@ export default class Image extends PureComponent {
}
render() {
const { src, fit, className, nullable, ...otherProps } = this.props
const {
src,
fit,
className,
nullable,
lazy,
...otherProps
} = this.props
const { error } = this.state
let source = src || placeholderSource
const classes = cx(className, {
default: 1,
objectFitCover: fit === 'cover'
objectFitCover: !!src && fit === 'cover',
backgroundSubtle2: 1,
})
//If error and not our own image
if (error && nullable) {
return null
} else if (error) {
source = imageUnavailable
}
if (!src) {
return (
<div className={classes} />
)
}
return (
<img
className={classes}
{...otherProps}
src={source}
src={src}
onError={this.handleOnError}
/>
)
}
}

View File

@ -22,7 +22,7 @@ const mapDispatchToProps = (dispatch) => ({
export default
@connect(makeMapStateToProps, mapDispatchToProps)
class IntersectionObserverArticle extends Component {
class IntersectionObserverArticle extends React.Component {
static propTypes = {
intersectionObserverWrapper: PropTypes.object.isRequired,

View File

@ -1,4 +1,3 @@
import moment from 'moment'
import {
FormattedMessage,
defineMessages,
@ -46,7 +45,7 @@ class LinkFooter extends PureComponent {
render() {
const { onOpenHotkeys, intl } = this.props
const currentYear = moment().format('YYYY')
const currentYear = new Date().getFullYear()
const linkFooterItems = [
{

View File

@ -7,7 +7,9 @@ import { decode } from 'blurhash';
import { autoPlayGif, displayMedia } from '../../initial_state';
import { isIOS } from '../../utils/is_mobile';
import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio';
import Button from '../button';
import Button from '../button'
import SensitiveMediaItem from '../../components/sensitive_media_item'
import Text from '../text'
const messages = defineMessages({
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
@ -16,6 +18,7 @@ const messages = defineMessages({
});
const cx = classNames.bind(_s)
class Item extends ImmutablePureComponent {
static propTypes = {
@ -58,7 +61,7 @@ class Item extends ImmutablePureComponent {
}
}
hoverToPlay () {
hoverToPlay() {
const { attachment } = this.props;
return autoPlayGif === false && attachment.get('type') === 'gifv';
}
@ -79,23 +82,23 @@ class Item extends ImmutablePureComponent {
e.stopPropagation();
}
componentDidMount () {
componentDidMount() {
if (this.props.attachment.get('blurhash')) {
this._decode();
}
}
componentDidUpdate (prevProps) {
componentDidUpdate(prevProps) {
if (prevProps.attachment.get('blurhash') !== this.props.attachment.get('blurhash') && this.props.attachment.get('blurhash')) {
this._decode();
}
}
_decode () {
_decode() {
const hash = this.props.attachment.get('blurhash');
const pixels = decode(hash, 32, 32);
if (pixels) {
if (pixels && this.canvas) {
const ctx = this.canvas.getContext('2d');
const imageData = new ImageData(pixels, 32, 32);
@ -111,7 +114,7 @@ class Item extends ImmutablePureComponent {
this.setState({ loaded: true });
}
render () {
render() {
const { attachment, index, size, standalone, displayWidth, visible, dimensions } = this.props;
const ar = attachment.getIn(['meta', 'small', 'aspect']);
@ -148,12 +151,12 @@ class Item extends ImmutablePureComponent {
if (attachment.get('type') === 'unknown') {
return (
<div className={[_s.default].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, borderRadius, width: `${width}%` }}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url')} target='_blank' style={{ cursor: 'pointer' }}>
<canvas width={32} height={32} ref={this.setCanvasRef} className='media-gallery__preview' />
<div className={[_s.default, _s.positionAbsolute].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, borderRadius, width: `${width}%` }}>
<a className={[_s.default, _s.heigh100PC, _s.width100PC, _s.cursorPointer].join(' ')} href={attachment.get('remote_url')} target='_blank' rel='noreferrer noopener'>
<canvas width={32} height={32} ref={this.setCanvasRef} className={[_s.default, _s.heigh100PC, _s.width100PC].join(' ')} />
</a>
</div>
);
)
} else if (attachment.get('type') === 'image') {
const previewUrl = attachment.get('preview_url');
const previewWidth = attachment.getIn(['meta', 'small', 'width']);
@ -213,14 +216,19 @@ class Item extends ImmutablePureComponent {
playsInline
/>
<span className='media-gallery__gifv__label'>GIF</span>
<div className={[_s.default, _s.positionAbsolute, _s.z2, _s.radiusSmall, _s.backgroundColorOpaque, _s.px5, _s.py5, _s.mr10, _s.mb10, _s.bottom0, _s.right0].join(' ')}>
<Text size='extraSmall' color='white' weight='medium'>GIF</Text>
</div>
</div>
);
}
return (
<div className={[_s.defeault, _s.positionAbsolute].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}>
<canvas width={0} height={0} ref={this.setCanvasRef} className={_s.displayNone} />
{
!visible && !this.state.loaded &&
<canvas width={32} height={32} ref={this.setCanvasRef} className={[_s.default, _s.heigh100PC, _s.width100PC].join(' ')} />
}
{visible && thumbnail}
</div>
);
@ -256,9 +264,11 @@ class MediaGallery extends PureComponent {
width: this.props.defaultWidth,
};
componentWillReceiveProps (nextProps) {
componentWillReceiveProps(nextProps) {
if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
this.setState({
visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all',
})
} else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
this.setState({ visible: nextProps.visible });
}
@ -287,21 +297,19 @@ class MediaGallery extends PureComponent {
}
}
render () {
render() {
const {
media,
intl,
sensitive,
height,
defaultWidth,
reduced
reduced,
} = this.props
const { visible } = this.state
const width = this.state.width || defaultWidth;
let children, spoilerButton;
const style = {};
const size = media.take(4).size;
@ -312,7 +320,7 @@ class MediaGallery extends PureComponent {
const panoSize_px = `${Math.floor(width / maximumAspectRatio)}px`;
let itemsDimensions = [];
if (size == 1 && width) {
if (size === 1 && width && visible) {
const aspectRatio = media.getIn([0, 'meta', 'small', 'aspect']);
if (isPanoramic(aspectRatio)) {
@ -322,13 +330,13 @@ class MediaGallery extends PureComponent {
} else {
style.height = Math.floor(width / aspectRatio);
}
} else if (size > 1 && width) {
} else if (size > 1 && width && visible) {
const ar1 = media.getIn([0, 'meta', 'small', 'aspect']);
const ar2 = media.getIn([1, 'meta', 'small', 'aspect']);
const ar3 = media.getIn([2, 'meta', 'small', 'aspect']);
const ar4 = media.getIn([3, 'meta', 'small', 'aspect']);
if (size == 2) {
if (size === 2) {
if (isPortrait(ar1) && isPortrait(ar2)) {
style.height = width - (width / maximumAspectRatio);
} else if (isPanoramic(ar1) && isPanoramic(ar2)) {
@ -378,7 +386,7 @@ class MediaGallery extends PureComponent {
{ w: 50, h: '100%', l: '2px', br: ['tr', 'br'] },
];
}
} else if (size == 3) {
} else if (size === 3) {
if (isPanoramic(ar1) && isPanoramic(ar2) && isPanoramic(ar3)) {
style.height = panoSize * 3;
} else if (isPortrait(ar1) && isPortrait(ar2) && isPortrait(ar3)) {
@ -391,7 +399,7 @@ class MediaGallery extends PureComponent {
if (isPanoramic(ar1) && isNonConformingRatio(ar2) && isNonConformingRatio(ar3)) {
itemsDimensions = [
{ w: 100, h: `50%`, b: '2px', br: ['tl', 'tr'] },
{ w: 100, h: '50%', b: '2px', br: ['tl', 'tr'] },
{ w: 50, h: '50%', t: '2px', r: '2px', br: ['bl'] },
{ w: 50, h: '50%', t: '2px', l: '2px', br: ['br'] },
];
@ -403,7 +411,7 @@ class MediaGallery extends PureComponent {
];
} else if (isPortrait(ar1) && isNonConformingRatio(ar2) && isNonConformingRatio(ar3)) {
itemsDimensions = [
{ w: 50, h: `100%`, r: '2px', br: ['tl', 'bl'] },
{ w: 50, h: '100%', r: '2px', br: ['tl', 'bl'] },
{ w: 50, h: '50%', b: '2px', l: '2px', br: ['tr'] },
{ w: 50, h: '50%', t: '2px', l: '2px', br: ['br'] },
];
@ -411,7 +419,7 @@ class MediaGallery extends PureComponent {
itemsDimensions = [
{ w: 50, h: '50%', b: '2px', r: '2px', br: ['tl'] },
{ w: 50, h: '50%', l: '-2px', b: '-2px', pos: 'absolute', float: 'none', br: ['bl'] },
{ w: 50, h: `100%`, r: '-2px', t: '0px', b: '0px', pos: 'absolute', float: 'none', br: ['tr', 'br'] },
{ w: 50, h: '100%', r: '-2px', t: '0px', b: '0px', pos: 'absolute', float: 'none', br: ['tr', 'br'] },
];
} else if (
(isNonConformingRatio(ar1) && isPortrait(ar2) && isNonConformingRatio(ar3)) ||
@ -419,7 +427,7 @@ class MediaGallery extends PureComponent {
) {
itemsDimensions = [
{ w: 50, h: '50%', b: '2px', r: '2px', br: ['tl'] },
{ w: 50, h: `100%`, l: '2px', float: 'right', br: ['tr', 'br'] },
{ w: 50, h: '100%', l: '2px', float: 'right', br: ['tr', 'br'] },
{ w: 50, h: '50%', t: '2px', r: '2px', br: ['bl'] },
];
} else if (
@ -444,10 +452,10 @@ class MediaGallery extends PureComponent {
itemsDimensions = [
{ w: 50, h: '50%', b: '2px', r: '2px', br: ['tl'] },
{ w: 50, h: '50%', b: '2px', l: '2px', br: ['tr'] },
{ w: 100, h: `50%`, t: '2px', br: ['bl', 'br'] },
{ w: 100, h: '50%', t: '2px', br: ['bl', 'br'] },
];
}
} else if (size == 4) {
} else if (size === 4) {
if (
(isPortrait(ar1) && isPortrait(ar2) && isPortrait(ar3) && isPortrait(ar4)) ||
(isPortrait(ar1) && isPortrait(ar2) && isPortrait(ar3) && isNonConformingRatio(ar4)) ||
@ -491,7 +499,7 @@ class MediaGallery extends PureComponent {
{ w: 67, h: '100%', r: '2px' },
{ w: 33, h: '33%', b: '4px', l: '2px' },
{ w: 33, h: '33%', l: '2px' },
{ w: 33, h: '33%', t: '4px', l: '2px' }
{ w: 33, h: '33%', t: '4px', l: '2px' },
];
} else {
itemsDimensions = [
@ -512,7 +520,11 @@ class MediaGallery extends PureComponent {
style.height = width / 2
}
children = media.take(4).map((attachment, i) => (
if (!visible) {
style.height = 'auto'
}
const children = media.take(4).map((attachment, i) => (
<Item
key={attachment.get('id')}
onClick={this.handleClick}
@ -523,28 +535,16 @@ class MediaGallery extends PureComponent {
visible={visible}
dimensions={itemsDimensions[i]}
/>
));
if (visible) {
spoilerButton = <Button title={intl.formatMessage(messages.toggle_visible)} icon='eye-slash' overlay onClick={this.handleOpen} />;
} else {
spoilerButton = (
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
<span className='spoiler-button__overlay__label'>
{intl.formatMessage(sensitive ? messages.warning : messages.hidden)}
</span>
</button>
);
}
))
const containerClasses = cx({
default: 1,
displayBlock: 1,
overflowHidden: 1,
borderColorSecondary: size === 1,
borderTop1PX: size === 1,
borderBottom1PX: size === 1,
px5: size > 1,
borderColorSecondary: size === 1 && visible,
borderTop1PX: size === 1 && visible,
borderBottom1PX: size === 1 && visible,
px5: size > 1 && visible,
})
return (
@ -554,14 +554,30 @@ class MediaGallery extends PureComponent {
ref={this.handleRef}
>
{ /* : todo :
<div className={classNames('spoiler-button', { 'spoiler-button--minified': visible })}>
{spoilerButton}
</div> */ }
{
!visible && sensitive &&
<SensitiveMediaItem onClick={this.handleOpen} />
}
{
visible &&
<div className={[_s.default, _s.displayBlock, _s.width100PC, _s.height100PC, _s.overflowHidden].join(' ')}>
{children}
</div>
}
{
visible && sensitive &&
<div className={[_s.positionAbsolute, _s.z2, _s.top0, _s.right0, _s.mt10, _s.mr10].join(' ')}>
<Button
title={intl.formatMessage(messages.toggle_visible)}
icon='hidden'
backgroundColor='none'
className={[_s.px10, _s.backgroundColorBlackOpaque_onHover].join(' ')}
onClick={this.handleOpen}
/>
</div>
}
</div>
);
}

View File

@ -208,11 +208,10 @@ class GifResultsCollection extends PureComponent {
render() {
const { results, handleSelectGifResult } = this.props
// : todo :
const count = results.length
const columnIndex = 10
console.log("results:", results)
return (
<div className={[_s.default, _s.height100PC, _s.flexRow, _s.width100PC].join(' ')}>
<GifResultsCollectionColumn

View File

@ -48,8 +48,7 @@ class ModalBase extends PureComponent {
activeElement = this.state.revealed ? document.activeElement : null
handleKeyUp = (e) => {
if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
&& !!this.props.children) {
if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27) && !!this.props.children) {
this.handleOnClose()
}
}
@ -57,9 +56,9 @@ class ModalBase extends PureComponent {
handleOnClose = (e) => {
const { onOpenModal, composeText, composeId, onClose, intl, type, onCancelReplyCompose } = this.props
if (this.dialog !== e.target) return
if (!!e && this.dialog !== e.target) return
if (!composeId && composeText && type == 'COMPOSE') {
if (!composeId && composeText && type === 'COMPOSE') {
onOpenModal('CONFIRM', {
message: intl.formatMessage(messages.delete),
confirm: intl.formatMessage(messages.confirm),

View File

@ -69,8 +69,8 @@ const MODAL_COMPONENTS = {
}
const mapStateToProps = state => ({
type: state.get('modal').modalType,
props: state.get('modal').modalProps,
type: state.getIn(['modal', 'modalType']),
props: state.getIn(['modal', 'modalProps'], {}),
})
const mapDispatchToProps = (dispatch) => ({

View File

@ -61,4 +61,5 @@ class ProUpgradeModal extends ImmutablePureComponent {
</ModalLayout>
)
}
}

View File

@ -2,7 +2,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { Manager, Reference, Popper } from 'react-popper'
import classnames from 'classnames/bind'
import Overlay from 'react-overlays/lib/Overlay'
import spring from 'react-motion/lib/spring'
import Motion from '../../features/ui/util/optional_motion'
import { openPopover, closePopover } from '../../actions/popover'
@ -14,7 +13,7 @@ const cx = classnames.bind(_s)
let id = 0
const mapStateToProps = state => ({
isModalOpen: state.get('modal').modalType === 'ACTIONS',
isModalOpen: state.getIn(['modal', 'modalType']) === 'ACTIONS',
popoverPlacement: state.getIn(['popover', 'placement']),
openPopoverType: state.getIn(['popover', 'popoverType']),
})
@ -123,7 +122,7 @@ class PopoverBase extends ImmutablePureComponent {
displayNone: !visible,
})
console.log("targetRef:", targetRef)
console.log('targetRef:', targetRef)
return (

View File

@ -32,8 +32,8 @@ const POPOVER_COMPONENTS = {
}
const mapStateToProps = state => ({
type: state.get('popover').popoverType,
props: state.get('popover').popoverProps,
type: state.getIn(['popover', 'popoverType']),
props: state.getIn(['popover', 'popoverProps'], {}),
})
const mapDispatchToProps = (dispatch) => ({
@ -45,6 +45,7 @@ const mapDispatchToProps = (dispatch) => ({
export default
@connect(mapStateToProps, mapDispatchToProps)
class PopoverRoot extends PureComponent {
static propTypes = {
type: PropTypes.string,
props: PropTypes.object,
@ -172,4 +173,5 @@ class PopoverRoot extends PureComponent {
</PopoverBase>
)
}
}

View File

@ -42,7 +42,7 @@ const mapStateToProps = state => {
const mapDispatchToProps = (dispatch, { intl }) => ({
openProfileOptionsPopover(props) {
console.log("props:", props)
console.log('props:', props)
dispatch(openPopover('PROFILE_OPTIONS', props))
},
@ -112,9 +112,9 @@ class ProfileHeader extends ImmutablePureComponent {
// : todo :
makeInfo() {
const { account, intl } = this.props;
const { account, intl } = this.props
let info = [];
const info = []
if (!account || !me) return info;
@ -130,8 +130,8 @@ class ProfileHeader extends ImmutablePureComponent {
info.push(<span key='domain_blocked' className='relationship-tag'>{intl.formatMessage(messages.domainBlocked)}</span>);
}
return info;
};
return info
}
setOpenMoreNodeRef = (n) => {
this.openMoreNode = n
@ -175,11 +175,10 @@ class ProfileHeader extends ImmutablePureComponent {
let buttonOptions = {}
if (!account) {
console.log("no account")
}
else {
console.log('no account')
} else {
if (!account.get('relationship')) {
console.log("no relationship")
console.log('no relationship')
// Wait until the relationship is loaded
} else if (account.getIn(['relationship', 'requested'])) {
buttonText = intl.formatMessage(messages.requested)
@ -209,7 +208,7 @@ class ProfileHeader extends ImmutablePureComponent {
backgroundColor: 'tertiary',
}
} else {
console.log("no nothin")
console.log('no nothin')
}
// if (account.get('id') !== me && account.get('relationship', null) !== null) {
@ -240,8 +239,8 @@ class ProfileHeader extends ImmutablePureComponent {
// }
}
console.log("buttonOptions:", buttonText, buttonOptions)
console.log("account: ", account)
console.log('buttonOptions:', buttonText, buttonOptions)
console.log('account: ', account)
// : todo : "follows you", "mutual follow"
@ -253,6 +252,7 @@ class ProfileHeader extends ImmutablePureComponent {
!headerMissing &&
<div className={[_s.default, _s.height350PX, _s.width100PC, _s.radiusSmall, _s.overflowHidden].join(' ')}>
<Image
alt='Cover Photo'
className={_s.height350PX}
src={headerSrc}
/>

View File

@ -30,7 +30,7 @@ const makeMapStateToProps = () => {
const ids = [status.get('id')]
while (ids.length > 0) {
let id = ids.shift();
const id = ids.shift();
const replies = state.getIn(['contexts', 'replies', id])
if (status.get('id') !== id) {

View File

@ -66,52 +66,44 @@ const getUnitDelay = units => {
export const timeAgoString = (intl, date, now, year) => {
const delta = now - date.getTime()
let relativeTime
if (delta < 10 * SECOND) {
relativeTime = intl.formatMessage(messages.just_now)
return intl.formatMessage(messages.just_now)
} else if (delta < 7 * DAY) {
if (delta < MINUTE) {
relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) })
return intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) })
} else if (delta < HOUR) {
relativeTime = intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) })
return intl.formatMessage(messages.minutes, { number: Math.floor(delta / MINUTE) })
} else if (delta < DAY) {
relativeTime = intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) })
return intl.formatMessage(messages.hours, { number: Math.floor(delta / HOUR) })
} else {
relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) })
return intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) })
}
} else if (date.getFullYear() === year) {
relativeTime = intl.formatDate(date, shortDateFormatOptions)
} else {
relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' })
return intl.formatDate(date, shortDateFormatOptions)
}
return relativeTime
return intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' })
}
const timeRemainingString = (intl, date, now) => {
const delta = date.getTime() - now
let relativeTime
if (delta < 10 * SECOND) {
relativeTime = intl.formatMessage(messages.moments_remaining)
return intl.formatMessage(messages.moments_remaining)
} else if (delta < MINUTE) {
relativeTime = intl.formatMessage(messages.seconds_remaining, { number: Math.floor(delta / SECOND) })
return intl.formatMessage(messages.seconds_remaining, { number: Math.floor(delta / SECOND) })
} else if (delta < HOUR) {
relativeTime = intl.formatMessage(messages.minutes_remaining, { number: Math.floor(delta / MINUTE) })
return intl.formatMessage(messages.minutes_remaining, { number: Math.floor(delta / MINUTE) })
} else if (delta < DAY) {
relativeTime = intl.formatMessage(messages.hours_remaining, { number: Math.floor(delta / HOUR) })
} else {
relativeTime = intl.formatMessage(messages.days_remaining, { number: Math.floor(delta / DAY) })
return intl.formatMessage(messages.hours_remaining, { number: Math.floor(delta / HOUR) })
}
return relativeTime
return intl.formatMessage(messages.days_remaining, { number: Math.floor(delta / DAY) })
}
export default
@injectIntl
class RelativeTimestamp extends Component {
class RelativeTimestamp extends React.Component {
static propTypes = {
intl: PropTypes.object.isRequired,

View File

@ -1,4 +1,4 @@
import { throttle } from 'lodash'
import throttle from 'lodash.throttle'
import { List as ImmutableList } from 'immutable'
import IntersectionObserverArticle from './intersection_observer_article'
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'
@ -199,7 +199,15 @@ export default class ScrollableList extends PureComponent {
}
render() {
const { children, scrollKey, showLoading, isLoading, hasMore, emptyMessage, onLoadMore } = this.props;
const {
children,
scrollKey,
showLoading,
isLoading,
hasMore,
emptyMessage,
onLoadMore,
} = this.props
const childrenCount = React.Children.count(children);
const trackScroll = true; //placeholder

View File

@ -0,0 +1,50 @@
import { injectIntl, defineMessages } from 'react-intl'
import Button from './button'
import Text from './text'
const messages = defineMessages({
warning: { id: 'status.sensitive_warning_2', defaultMessage: 'The following media includes potentially sensitive content.' },
view: { id: 'view', defaultMessage: 'View' },
});
export default
@injectIntl
class SensitiveMediaItem extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
onClick: PropTypes.func.isRequired,
}
render() {
const {
intl,
onClick,
} = this.props
return (
<div className={[_s.default, _s.px15, _s.pt5].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.radiusSmall, _s.backgroundColorSecondary3, _s.py10, _s.px15, _s.height100PC, _s.width100PC].join(' ')}>
<div className={[_s.default, _s.justifyContentCenter, _s.flexNormal].join(' ')}>
<Text color='secondary'>
{intl.formatMessage(messages.warning)}
</Text>
</div>
<div className={[_s.default, _s.justifyContentCenter, _s.marginLeftAuto].join(' ')}>
<Button
onClick={onClick}
color='tertiary'
backgroundColor='none'
className={_s.backgroundSubtle2Dark_onHover}
>
<Text color='inherit' weight='bold' size='medium'>
{intl.formatMessage(messages.view)}
</Text>
</Button>
</div>
</div>
</div>
)
}
}

View File

@ -22,7 +22,7 @@ const messages = defineMessages({
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
lists: { id: 'column.lists', defaultMessage: 'Lists', },
lists: { id: 'column.lists', defaultMessage: 'Lists' },
apps: { id: 'tabs_bar.apps', defaultMessage: 'Apps' },
more: { id: 'sidebar.more', defaultMessage: 'More' },
pro: { id: 'promo.gab_pro', defaultMessage: 'Upgrade to GabPRO' },
@ -37,7 +37,7 @@ const mapStateToProps = state => {
return {
account: getAccount(state, me),
moreOpen: state.get('popover').popoverType === 'SIDEBAR_MORE',
moreOpen: state.getIn(['popover', 'popoverType']) === 'SIDEBAR_MORE',
notificationCount: state.getIn(['notifications', 'unread']),
homeItemsQueueCount: state.getIn(['timelines', 'home', 'totalQueuedItemsCount']),
showCommunityTimeline: state.getIn(['settings', 'community', 'shows', 'inSidebar']),
@ -95,7 +95,7 @@ class Sidebar extends ImmutablePureComponent {
notificationCount,
homeItemsQueueCount,
showCommunityTimeline,
moreOpen
moreOpen,
} = this.props
// : todo :
@ -104,7 +104,7 @@ class Sidebar extends ImmutablePureComponent {
const acct = account.get('acct')
const isPro = account.get('is_pro')
console.log("showCommunityTimeline:", showCommunityTimeline)
console.log('showCommunityTimeline:', showCommunityTimeline)
const menuItems = [
{

View File

@ -1,10 +1,12 @@
import classNames from 'classnames/bind'
import Button from './button'
import Icon from './icon'
import Image from './image'
const cx = classNames.bind(_s)
export default class SidebarSectionItem extends PureComponent {
static contextTypes = {
router: PropTypes.object,
}
@ -45,7 +47,7 @@ export default class SidebarSectionItem extends PureComponent {
count,
onClick,
href,
buttonRef
buttonRef,
} = this.props
const { hovering } = this.state
@ -65,6 +67,7 @@ export default class SidebarSectionItem extends PureComponent {
radiusSmall: 1,
mt2: 1,
border1PX: 1,
outlineNone: 1,
borderColorTransparent: !shouldShowActive,
borderColorSecondary: shouldShowActive,
backgroundTransparent: !shouldShowActive,
@ -77,7 +80,6 @@ export default class SidebarSectionItem extends PureComponent {
fontSize15PX: 1,
text: 1,
textOverflowEllipsis: 1,
outlineNone: 1,
colorSecondary: !hovering && !active && !me && !shouldShowActive,
colorPrimary: shouldShowActive || me,
})
@ -111,14 +113,15 @@ export default class SidebarSectionItem extends PureComponent {
buttonRef={buttonRef}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
className={[_s.default, _s.noUnderline, _s.cursorPointer, _s.width100PC, _s.backgroundTransparent].join(' ')}
className={[_s.default, _s.noUnderline, _s.outlineNone, _s.cursorPointer, _s.width100PC, _s.backgroundTransparent].join(' ')}
>
<div className={containerClasses}>
<div className={[_s.default]}>
{ icon && <Icon id={icon} className={iconClasses} width={iconSize} height={iconSize} /> }
{ image &&
<img
className={[_s.default, _s.circle].join(' ')}
<Image
alt={title}
className={_s.circle}
width={iconSize}
height={iconSize}
src={image}
@ -137,4 +140,5 @@ export default class SidebarSectionItem extends PureComponent {
</Button>
)
}
}

View File

@ -272,7 +272,7 @@ class Status extends ImmutablePureComponent {
group,
promoted,
borderless,
isChild
isChild,
} = this.props
let media = null
@ -287,7 +287,7 @@ class Status extends ImmutablePureComponent {
// status = status.get('reblog');
// }
let { status, ...other } = this.props;
const { status, ...other } = this.props;
// console.log("replies:", this.props.replies)

View File

@ -169,11 +169,12 @@ export default class Card extends ImmutablePureComponent {
</div>
)
// : todo : use <Image />
let embed = ''
let thumbnail = interactive ?
<img src={cardImg} className={[_s.default, _s.objectFitCover, _s.positionAbsolute, _s.width100PC, _s.height100PC, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')} />
const thumbnail = interactive ?
<img alt={''} src={cardImg} className={[_s.default, _s.objectFitCover, _s.positionAbsolute, _s.width100PC, _s.height100PC, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')} />
:
<img src={cardImg} className={[_s.default, _s.objectFitCover, _s.width330PX, _s.height220PX].join(' ')} />
<img alt={''} src={cardImg} className={[_s.default, _s.objectFitCover, _s.width330PX, _s.height220PX].join(' ')} />
if (interactive) {
if (embedded) {
@ -199,7 +200,7 @@ export default class Card extends ImmutablePureComponent {
className={[_s.default, _s.cursorPointer, _s.backgroundColorOpaque, _s.radiusSmall, _s.py15, _s.px15].join(' ')}
onClick={this.handleEmbedClick}
>
<Icon id={iconVariant} className={[_s.fillColorWhite].join(' ')}/>
<Icon id={iconVariant} className={[_s.fillColorWhite].join(' ')} />
</button>
</div>
}

View File

@ -224,12 +224,17 @@ class StatusContent extends ImmutablePureComponent {
displayNone: hidden,
})
const containerClasses = cx({
statusContent: 1,
px15: !isComment,
})
return (
<div
className={[].join(' ')}
ref={this.setRef}
tabIndex='0'
className={[_s.px15, _s.statusContent].join(' ')}
className={containerClasses}
style={directionStyle}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
@ -317,11 +322,18 @@ class StatusContent extends ImmutablePureComponent {
)
}
const containerClasses = cx({
statusContent: 1,
px15: !isComment,
mb15: !isComment,
mt5: isComment,
})
return (
<div
tabIndex='0'
ref={this.setRef}
className={[_s.px15, _s.mb15, _s.statusContent].join(' ')}
className={containerClasses}
style={directionStyle}
dangerouslySetInnerHTML={content}
lang={status.get('language')}

View File

@ -10,7 +10,7 @@ export default class TimelineQueueButtonHeader extends PureComponent {
onClick: PropTypes.func.isRequired,
count: PropTypes.number,
itemType: PropTypes.string,
floating: PropTypes.bool
floating: PropTypes.bool,
}
static defaultProps = {

View File

@ -49,7 +49,7 @@ export default class TrendingItem extends PureComponent {
author,
publishDate,
isLast,
wide
wide,
} = this.props
const { hovering } = this.state
@ -70,13 +70,14 @@ export default class TrendingItem extends PureComponent {
})
const correctedAuthor = author.replace('www.', '')
const correctedDescription = description.length > 120 ? `${description.substring(0, 120).trim()}...` : description
const correctedDescription = description.length >= 120 ? `${description.substring(0, 120).trim()}...` : description
const image = (
<Image
nullable
width='116px'
height='78px'
alt={title}
src={imageUrl}
className={[_s.radiusSmall, _s.overflowHidden, _s.mb10].join(' ')}
/>
@ -87,6 +88,7 @@ export default class TrendingItem extends PureComponent {
noClasses
href={url}
target='_blank'
rel='noopener noreferrer'
className={containerClasses}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
@ -104,7 +106,7 @@ export default class TrendingItem extends PureComponent {
{
!!correctedDescription &&
<div className={[_s.default, _s.heightMax60PX, _s.overflowHidden, _s.py5].join(' ')}>
<div className={[_s.default, _s.heightMax56PX, _s.overflowHidden, _s.pt5, _s.mb5].join(' ')}>
<Text size='small' color='secondary'>
{correctedDescription}
</Text>

View File

@ -1,6 +1,6 @@
import { defineMessages, injectIntl } from 'react-intl'
import { is } from 'immutable'
import { throttle } from 'lodash'
import throttle from 'lodash.throttle'
import classNames from 'classnames/bind'
import { decode } from 'blurhash'
import { isFullscreen, requestFullscreen, exitFullscreen } from '../utils/fullscreen'

View File

@ -28,7 +28,7 @@ import { boostModal, deleteModal } from '../initial_state';
// import { showAlertForError } from '../actions/alerts';
import {
createRemovedAccount,
groupRemoveStatus
groupRemoveStatus,
} from '../actions/groups';
import { makeGetStatus } from '../selectors';
import Status from '../components/status';
@ -58,7 +58,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onReply (status, router) {
dispatch((_, getState) => {
let state = getState();
const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.replyMessage),
@ -73,7 +73,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onQuote (status, router) {
dispatch((_, getState) => {
let state = getState();
const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.quoteMessage),
@ -107,12 +107,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
},
onFavorite (status) {
console.log("onFavorite...", status)
console.log('onFavorite...', status)
if (status.get('favourited')) {
console.log("unfav...")
console.log('unfav...')
dispatch(unfavorite(status));
} else {
console.log("fav...")
console.log('fav...')
dispatch(favorite(status));
}
},

View File

@ -1,6 +1,7 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { createSelector } from 'reselect';
import { debounce, sample } from 'lodash';
import { sample } from 'lodash';
import debounce from 'lodash.debounce'
import { dequeueTimeline } from '../actions/timelines';
import { scrollTopTimeline } from '../actions/timelines';
import { fetchStatus } from '../actions/statuses';

View File

@ -84,7 +84,7 @@ class AccountGallery extends ImmutablePureComponent {
isLoading,
hasMore,
intl,
account
account,
} = this.props
if (!account) return null
@ -109,7 +109,7 @@ class AccountGallery extends ImmutablePureComponent {
}
{ /*
attachments.size == 0 &&
attachments.size === 0 &&
<ColumnIndicator type='empty' message={intl.formatMessage(messages.none)} />
*/ }

View File

@ -1,7 +1,7 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import { fetchBlocks, expandBlocks } from '../actions/blocks'
import AccountContainer from '../containers/account_container'
import ColumnIndicator from '../components/column_indicator'

View File

@ -1,7 +1,7 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import { unblockDomain, fetchDomainBlocks, expandDomainBlocks } from '../actions/domain_blocks'
import ColumnIndicator from '../components/column_indicator'
import List from '../components/list'

View File

@ -1,8 +1,8 @@
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { length } from 'stringz'
import ImmutablePropTypes from 'react-immutable-proptypes'
import classNames from 'classnames/bind'
import { length } from 'stringz'
import CharacterCounter from '../../../../components/character_counter'
import UploadForm from '../upload_form'
import AutosuggestTextbox from '../../../../components/autosuggest_textbox'
@ -15,7 +15,7 @@ import StatusVisibilityButton from '../../components/status_visibility_button'
import EmojiPickerButton from '../../components/emoji_picker_button'
import PollFormContainer from '../../containers/poll_form_container'
import SchedulePostButton from '../schedule_post_button'
import QuotedStatusPreviewContainer from '../../containers/quoted_status_preview_container'
import StatusContainer from '../../../../containers/status_container'
import Button from '../../../../components/button'
import Avatar from '../../../../components/avatar'
import { isMobile } from '../../../../utils/is_mobile'
@ -29,7 +29,7 @@ const messages = defineMessages({
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
publish: { id: 'compose_form.publish', defaultMessage: 'Gab' },
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}' },
schedulePost: { id: 'compose_form.schedule_post', defaultMessage: 'Schedule Post' }
schedulePost: { id: 'compose_form.schedule_post', defaultMessage: 'Schedule Post' },
});
const cx = classNames.bind(_s)
@ -79,6 +79,7 @@ class ComposeForm extends ImmutablePureComponent {
scheduledAt: PropTypes.instanceOf(Date),
setScheduledAt: PropTypes.func.isRequired,
replyToId: PropTypes.string,
hasPoll: PropTypes.bool,
};
static defaultProps = {
@ -104,7 +105,7 @@ class ComposeForm extends ImmutablePureComponent {
handleClick = (e) => {
if (!this.form) return false;
if (e.target) {
if (e.target.classList.contains('react-datepicker__time-list-item')) return;
if (e.target.classList.contains('react-datepicker__time-list-item')) return false
}
if (!this.form.contains(e.target)) {
this.handleClickOutside();
@ -162,11 +163,11 @@ class ComposeForm extends ImmutablePureComponent {
}
componentDidMount() {
document.addEventListener("click", this.handleClick, false);
document.addEventListener('click', this.handleClick, false);
}
componentWillUnmount() {
document.removeEventListener("click", this.handleClick, false);
document.removeEventListener('click', this.handleClick, false);
}
componentDidUpdate(prevProps) {
@ -201,19 +202,19 @@ class ComposeForm extends ImmutablePureComponent {
}
setForm = (c) => {
this.form = c;
this.form = c
}
setSpoilerText = (c) => {
this.spoilerText = c;
this.spoilerText = c
}
handleEmojiPick = (data) => {
const { text } = this.props;
const position = this.autosuggestTextarea.textbox.selectionStart;
const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
const { text } = this.props
const position = this.autosuggestTextarea.textbox.selectionStart
const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1])
this.props.onPickEmoji(position, data, needsSpace);
this.props.onPickEmoji(position, data, needsSpace)
}
render() {
@ -230,11 +231,13 @@ class ComposeForm extends ImmutablePureComponent {
edit,
scheduledAt,
spoiler,
replyToId
replyToId,
hasPoll,
isUploading,
} = this.props
const disabled = this.props.isSubmitting;
const text = [this.props.spoilerText, countableText(this.props.text)].join('');
const disabledButton = disabled || this.props.isUploading || this.props.isChangingUpload || length(text) > maxPostCharacterCount || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
const disabledButton = disabled || isUploading || this.props.isChangingUpload || length(text) > maxPostCharacterCount || (text.length !== 0 && text.trim().length === 0 && !anyMedia);
const shouldAutoFocus = autoFocus && !showSearch && !isMobile(window.innerWidth)
const parentContainerClasses = cx({
@ -255,7 +258,8 @@ class ComposeForm extends ImmutablePureComponent {
const actionsContainerClasses = cx({
default: 1,
flexRow: 1,
alignItemsCenter: 1,
alignItemsCenter: !shouldCondense,
alignItemsStart: shouldCondense,
mt10: !shouldCondense,
px15: !shouldCondense,
})
@ -313,19 +317,25 @@ class ComposeForm extends ImmutablePureComponent {
autoFocus={shouldAutoFocus}
small={shouldCondense}
textarea
>
/>
{
(isUploading || anyMedia) &&
<div className={[_s.default, _s.px15].join(' ')}>
<UploadForm replyToId={replyToId} />
{
!edit &&
<PollFormContainer replyToId={replyToId} />
}
</div>
}
</AutosuggestTextbox>
{
!edit && hasPoll &&
<div className={[_s.default, _s.px15].join(' ')}>
<PollFormContainer replyToId={replyToId} />
</div>
}
{ /* quoteOfId && <QuotedStatusPreviewContainer id={quoteOfId} /> */}
{
/* : todo : quoteOfId && <StatusContainer id={quoteOfId} /> */
}
<div className={actionsContainerClasses}>
<div className={[_s.default, _s.flexRow, _s.marginRightAuto].join(' ')}>
@ -359,7 +369,7 @@ class ComposeForm extends ImmutablePureComponent {
<CharacterCounter max={maxPostCharacterCount} text={text} />
}
{
{ /* : todo : show send on shouldCondense if any text */
!shouldCondense &&
<Button
className={[_s.fontSize15PX, _s.px15].join(' ')}

View File

@ -7,7 +7,7 @@ const messages = defineMessages({
})
const mapStateToProps = state => ({
active: state.get('popover').popoverType === 'EMOJI_PICKER',
active: state.getIn(['popover', 'popoverType']) === 'EMOJI_PICKER',
})
const mapDispatchToProps = dispatch => ({

View File

@ -9,7 +9,7 @@ const messages = defineMessages({
})
const mapStateToProps = (state) => ({
active: !!state.getIn(['compose', 'gif']) || state.get('modal').modalType === 'GIF_PICKER',
active: !!state.getIn(['compose', 'gif']) || state.getIn(['modal', 'modalType']) === 'GIF_PICKER',
})
const mapDispatchToProps = (dispatch) => ({

View File

@ -1,23 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import DisplayName from '../../../components/display_name';
import StatusContent from '../../../components/status_content';
// : todo : do we need this? make work inside of status/status content
export default class QuotedStatusPreview extends PureComponent {
static propTypes = {
status: ImmutablePropTypes.map,
account: ImmutablePropTypes.map,
}
render() {
const { status, account } = this.props;
return (
<div className='compose-form__quote-preview'>
<DisplayName account={account} />
<StatusContent status={status} expanded={false} />
</div>
);
}
}

View File

@ -28,6 +28,11 @@ class StatusVisibilityButton extends PureComponent {
intl: PropTypes.object.isRequired,
small: PropTypes.bool,
onOpenPopover: PropTypes.func.isRequired,
value: PropTypes.oneOf([
'private',
'unlisted',
'public',
]),
}
handleOnClick = () => {
@ -42,7 +47,7 @@ class StatusVisibilityButton extends PureComponent {
const {
intl,
small,
value
value,
} = this.props
let icon;

View File

@ -24,7 +24,7 @@ class UploadForm extends ImmutablePureComponent {
const {
mediaIds,
isUploading,
uploadProgress
uploadProgress,
} = this.props
return (

View File

@ -71,7 +71,7 @@ class Compose extends ImmutablePureComponent {
render () {
const { showSearch, isSearchPage, intl } = this.props;
let header = '';
const header = '';
return (
<div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}>

View File

@ -35,10 +35,11 @@ const mapStateToProps = (state, { replyToId }) => {
isUploading: !isMatch ? false : state.getIn(['compose', 'is_uploading']),
showSearch: !isMatch ? false : state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
anyMedia: !isMatch ? false : state.getIn(['compose', 'media_attachments']).size > 0,
isModalOpen: !isMatch ? false : state.get('modal').modalType === 'COMPOSE',
isModalOpen: !isMatch ? false : state.getIn(['modal', 'modalType']) === 'COMPOSE',
quoteOfId: !isMatch ? null : state.getIn(['compose', 'quote_of_id']),
scheduledAt: !isMatch ? null : state.getIn(['compose', 'scheduled_at']),
account: state.getIn(['accounts', me]),
hasPoll: !isMatch ? false : state.getIn(['compose', 'poll']),
}
}
@ -84,7 +85,7 @@ const mapDispatchToProps = (dispatch) => ({
function mergeProps(stateProps, dispatchProps, ownProps) {
return Object.assign({}, ownProps, {
...stateProps,
...dispatchProps
...dispatchProps,
})
}

View File

@ -1,8 +0,0 @@
import QuotedStatusPreview from '../components/quoted_status_preview';
const mapStateToProps = (state, { id }) => ({
status: state.getIn(['statuses', id]),
account: state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
});
export default connect(mapStateToProps)(QuotedStatusPreview);

View File

@ -1,9 +1,9 @@
import { urlRegex } from './url_regex';
import { urlRegex } from './url_regex'
const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx';
const urlPlaceholder = 'xxxxxxxxxxxxxxxxxxxxxxx'
export function countableText(inputText) {
return inputText
.replace(urlRegex, urlPlaceholder)
.replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3');
};
}

View File

@ -1,7 +1,7 @@
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 debounce from 'lodash.debounce'
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
import ColumnIndicator from '../../components/column_indicator';
import AccountAuthorize from './components/account_authorize';

View File

@ -1,6 +1,6 @@
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import { defineMessages, injectIntl } from 'react-intl'
import {
fetchFollowers,

View File

@ -1,6 +1,6 @@
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import { defineMessages, injectIntl } from 'react-intl'
import {
fetchFollowing,

View File

@ -1,6 +1,6 @@
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash';
import debounce from 'lodash.debounce'
import {
fetchMembers,
expandMembers,

View File

@ -1,6 +1,6 @@
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash';
import debounce from 'lodash.debounce'
import ColumnIndicator from '../components/column_indicator';
import {
fetchRemovedAccounts,

View File

@ -12,6 +12,7 @@ const mapStateToProps = (state, { activeTab }) => ({
export default
@connect(mapStateToProps)
class GroupsCollection extends ImmutablePureComponent {
static propTypes = {
activeTab: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired,
@ -35,14 +36,32 @@ class GroupsCollection extends ImmutablePureComponent {
return <ColumnIndicator type='loading' />
}
const halfCount = parseInt(groupIds.size / 2)
console.log("halfCount", halfCount)
return (
<div className={[_s.default, _s.flexRow, _s.flexWrap].join(' ')}>
<div className={[_s.default, _s.flexNormal].join(' ')}>
<ScrollableList scrollKey='group-collection-column-1'>
{
groupIds.map((groupId, i) => (
groupIds.slice(0, halfCount).map((groupId, i) => (
<GroupCollectionItem key={`group-collection-item-${i}`} id={groupId} />
))
}
</ScrollableList>
</div>
<div className={[_s.default, _s.flexNormal].join(' ')}>
<ScrollableList scrollKey='group-collection-column-2'>
{
groupIds.slice(halfCount, groupIds.size).map((groupId, i) => (
<GroupCollectionItem key={`group-collection-item-${i}`} id={groupId} />
))
}
</ScrollableList>
</div>
</div>
)
}
}

View File

@ -1,5 +1,5 @@
import { FormattedMessage } from 'react-intl'
import { isEqual } from 'lodash'
import isEqual from 'lodash.isequal'
import { expandHashtagTimeline, clearTimeline } from '../actions/timelines'
import { connectHashtagStream } from '../actions/streaming'
import StatusListContainer from '../containers/status_list_container'
@ -21,7 +21,7 @@ class HashtagTimeline extends PureComponent {
}
title = () => {
let title = [this.props.params.id]
const title = [this.props.params.id]
if (this.additionalFor('any')) {
title.push(' ',
@ -29,7 +29,7 @@ class HashtagTimeline extends PureComponent {
key='any'
id='hashtag.column_header.tag_mode.any'
values={{
additional: this.additionalFor('any')
additional: this.additionalFor('any'),
}}
defaultMessage='or {additional}'
/>
@ -42,7 +42,7 @@ class HashtagTimeline extends PureComponent {
key='all'
id='hashtag.column_header.tag_mode.all'
values={{
additional: this.additionalFor('all')
additional: this.additionalFor('all'),
}}
defaultMessage='and {additional}'
/>
@ -55,7 +55,7 @@ class HashtagTimeline extends PureComponent {
key='none'
id='hashtag.column_header.tag_mode.none'
values={{
additional: this.additionalFor('none')
additional: this.additionalFor('none'),
}}
defaultMessage='without {additional}'
/>
@ -76,13 +76,13 @@ class HashtagTimeline extends PureComponent {
}
_subscribe (dispatch, id, tags = {}) {
let any = (tags.any || []).map(tag => tag.value)
let all = (tags.all || []).map(tag => tag.value)
let none = (tags.none || []).map(tag => tag.value);
const any = (tags.any || []).map(tag => tag.value)
const all = (tags.all || []).map(tag => tag.value)
const none = (tags.none || []).map(tag => tag.value);
[id, ...any].map(tag => {
this.disconnects.push(dispatch(connectHashtagStream(id, tag, status => {
let tags = status.tags.map(tag => tag.name)
const tags = status.tags.map(tag => tag.name)
return all.filter(tag => tags.includes(tag)).length === all.length &&
none.filter(tag => tags.includes(tag)).length === 0

View File

@ -5,7 +5,7 @@ import { closeOnboarding } from '../actions/onboarding';
// : todo :
class FrameWelcome extends Component {
class FrameWelcome extends React.Component {
static propTypes = {
domain: PropTypes.string.isRequired,
onNext: PropTypes.func.isRequired,
@ -45,7 +45,7 @@ class FrameWelcome extends Component {
}
}
class FrameFederation extends Component {
class FrameFederation extends React.Component {
static propTypes = {
onNext: PropTypes.func.isRequired,
}
@ -81,7 +81,7 @@ class FrameFederation extends Component {
}
};
class FrameInteractions extends Component {
class FrameInteractions extends React.Component {
static propTypes = {
onNext: PropTypes.func.isRequired,
};

View File

@ -1,7 +1,7 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import { FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import { fetchFavoritedStatuses, expandFavoritedStatuses } from '../actions/favorites'
import { meUsername } from '../initial_state'
import StatusList from '../components/status_list'

View File

@ -1,7 +1,7 @@
import { injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import { fetchMutes, expandMutes } from '../actions/mutes'
import AccountContainer from '../containers/account_container'
import ColumnIndicator from '../components/column_indicator'

View File

@ -3,7 +3,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import { createSelector } from 'reselect'
import { List as ImmutableList } from 'immutable'
import { debounce } from 'lodash'
import debounce from 'lodash.debounce'
import {
expandNotifications,
scrollTopNotifications,

View File

@ -50,7 +50,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onReply (status, router) {
dispatch((_, getState) => {
let state = getState();
const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.replyMessage),

View File

@ -33,7 +33,7 @@ import StatusContainer from '../../containers/status_container'
import { textForScreenReader, defaultMediaVisibility } from '../../components/status/status'
import ColumnIndicator from '../../components/column_indicator'
import Block from '../../components/block'
import Comment from '../../components/comment'
import CommentList from '../../components/comment_list'
const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -57,22 +57,51 @@ const makeMapStateToProps = () => {
username: props.params.username,
})
// : todo : if is comment (i.e. if any ancestorsIds) use comment not status
let ancestorsIds = Immutable.List()
let descendantsIds = Immutable.List();
let descendantsIds = Immutable.List()
if (status) {
ancestorsIds = ancestorsIds.withMutations(mutable => {
let id = status.get('in_reply_to_id');
// ancestorsIds = ancestorsIds.withMutations(mutable => {
// let id = status.get('in_reply_to_id');
while (id) {
mutable.unshift(id);
id = state.getIn(['contexts', 'inReplyTos', id]);
// while (id) {
// mutable.unshift(id);
// id = state.getIn(['contexts', 'inReplyTos', id]);
// }
// });
// // ONLY Direct descendants
// descendantsIds = state.getIn(['contexts', 'replies', status.get('id')])
let indent = -1
descendantsIds = descendantsIds.withMutations(mutable => {
const ids = [status.get('id')]
while (ids.length > 0) {
let id = ids.shift();
const replies = state.getIn(['contexts', 'replies', id])
if (status.get('id') !== id) {
mutable.push(Immutable.Map({
statusId: id,
indent: indent,
}))
}
if (replies) {
replies.reverse().forEach(reply => {
ids.unshift(reply)
});
// ONLY Direct descendants
descendantsIds = state.getIn(['contexts', 'replies', status.get('id')])
indent++
indent = Math.min(2, indent)
}
}
})
}
console.log("descendantsIds:", descendantsIds)
return {
status,
@ -338,6 +367,7 @@ class Status extends ImmutablePureComponent {
renderChildren(list) {
console.log("list:", list)
return null
// : todo : comments
return list.map(id => (
<Comment
@ -383,9 +413,9 @@ class Status extends ImmutablePureComponent {
return <ColumnIndicator type='loading' />
}
if (ancestorsIds && ancestorsIds.size > 0) {
ancestors = this.renderChildren(ancestorsIds)
}
// if (ancestorsIds && ancestorsIds.size > 0) {
// ancestors = this.renderChildren(ancestorsIds)
// }
if (descendantsIds && descendantsIds.size > 0) {
descendants = this.renderChildren(descendantsIds)
@ -434,7 +464,7 @@ class Status extends ImmutablePureComponent {
<div className={[_s.default, _s.mr10, _s.ml10, _s.mb10, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}/>
}
{descendants}
<CommentList descendants={descendantsIds} />
</Block>
</div>
)

View File

@ -3,7 +3,7 @@ import BundleColumnError from '../../../components/bundle_column_error'
import Bundle from './bundle'
import { me } from '../../../initial_state'
export default class WrappedRoute extends Component {
export default class WrappedRoute extends PureComponent {
static propTypes = {
component: PropTypes.func.isRequired,
page: PropTypes.func.isRequired,

View File

@ -5,11 +5,7 @@ import { default as GabSocial, store } from './containers/gabsocial';
import ReactDOM from 'react-dom';
import ready from './ready';
const perf = require('./performance');
function main() {
perf.start('main()');
// : todo :
// if (window.history && history.replaceState) {
// const { pathname, search, hash } = window.location;
@ -30,7 +26,6 @@ function main() {
require('offline-plugin/runtime').install();
store.dispatch(registerPushNotifications.register());
}
perf.stop('main()');
});
}

View File

@ -1,58 +0,0 @@
'use strict';
const createAudio = sources => {
const audio = new Audio();
sources.forEach(({ type, src }) => {
const source = document.createElement('source');
source.type = type;
source.src = src;
audio.appendChild(source);
});
return audio;
};
const play = audio => {
if (!audio.paused) {
audio.pause();
if (typeof audio.fastSeek === 'function') {
audio.fastSeek(0);
} else {
audio.currentTime = 0;
}
}
// audio.play();
};
export default function soundsMiddleware() {
const soundCache = {
boop: createAudio([
{
src: '/sounds/boop.ogg',
type: 'audio/ogg',
},
{
src: '/sounds/boop.mp3',
type: 'audio/mpeg',
},
]),
ribbit: createAudio([
{
src: '/sounds/ribbit.ogg',
type: 'audio/ogg',
},
{
src: '/sounds/ribbit.mp3',
type: 'audio/mpeg',
},
]),
};
return () => next => action => {
if (action.meta && action.meta.sound && soundCache[action.meta.sound]) {
play(soundCache[action.meta.sound]);
}
return next(action);
};
};

View File

@ -36,31 +36,31 @@ class GroupsPage extends PureComponent {
render() {
const { children, isPro, onOpenGroupCreateModal } = this.props
let tabs = [
const actions = []
const tabs = [
{
title: 'Featured',
to: '/groups'
to: '/groups',
},
{
title: 'New',
to: '/groups/new'
to: '/groups/new',
},
{
title: 'My Groups',
to: '/groups/browse/member'
to: '/groups/browse/member',
},
]
let actions = []
if (isPro) {
actions = [{
icon: 'group-add',
actions.push({
icon: 'add',
onClick: onOpenGroupCreateModal,
}]
})
tabs.push({
title: 'Admin',
to: '/groups/browse/admin'
to: '/groups/browse/admin',
})
}
@ -82,4 +82,5 @@ class GroupsPage extends PureComponent {
</DefaultLayout>
)
}
}

View File

@ -31,8 +31,8 @@ class ListsPage extends PureComponent {
title='Lists'
actions={[
{
icon: 'list-add',
onClick: onOpenListCreateModal
icon: 'add',
onClick: onOpenListCreateModal,
},
]}
layout={(
@ -48,4 +48,5 @@ class ListsPage extends PureComponent {
</DefaultLayout>
)
}
}

View File

@ -13,7 +13,7 @@ import ProfileLayout from '../layouts/profile_layout'
const mapStateToProps = (state, { params: { username } }) => {
const accounts = state.getIn(['accounts'])
const account = accounts.find(acct => username.toLowerCase() == acct.getIn(['acct'], '').toLowerCase())
const account = accounts.find(acct => username.toLowerCase() === acct.getIn(['acct'], '').toLowerCase())
const accountId = !!account ? account.get('id') : -1
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false)
@ -33,6 +33,7 @@ const mapStateToProps = (state, { params: { username } }) => {
export default
@connect(mapStateToProps)
class ProfilePage extends ImmutablePureComponent {
static propTypes = {
children: PropTypes.node,
params: PropTypes.object.isRequired,
@ -59,7 +60,7 @@ class ProfilePage extends ImmutablePureComponent {
const {
account,
children,
unavailable
unavailable,
} = this.props
return (
@ -86,4 +87,5 @@ class ProfilePage extends ImmutablePureComponent {
</ProfileLayout>
)
}
}

View File

@ -1,33 +0,0 @@
'use strict';
//
// Tools for performance debugging, only enabled in development mode.
// Open up Chrome Dev Tools, then Timeline, then User Timing to see output.
// Also see config/webpack/loaders/mark.js for the webpack loader marks.
//
let marky;
if (process.env.NODE_ENV === 'development') {
if (typeof performance !== 'undefined' && performance.setResourceTimingBufferSize) {
// Increase Firefox's performance entry limit; otherwise it's capped to 150.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
performance.setResourceTimingBufferSize(Infinity);
}
marky = require('marky');
// allows us to easily do e.g. ReactPerf.printWasted() while debugging
//window.ReactPerf = require('react-addons-perf');
//window.ReactPerf.start();
}
export function start(name) {
if (process.env.NODE_ENV === 'development') {
marky.mark(name);
}
}
export function stop(name) {
if (process.env.NODE_ENV === 'development') {
marky.stop(name);
}
}

View File

@ -36,7 +36,7 @@ import {
COMPOSE_POLL_OPTION_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE,
COMPOSE_SCHEDULED_AT_CHANGE,
COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY
COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY,
} from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines';
import { STORE_HYDRATE } from '../actions/store';

View File

@ -1,17 +1,24 @@
import { MODAL_OPEN, MODAL_CLOSE } from '../actions/modal';
import Immutable from 'immutable'
import {
MODAL_OPEN,
MODAL_CLOSE,
} from '../actions/modal'
const initialState = {
const initialState = Immutable.Map({
modalType: null,
modalProps: {},
};
modalProps: null,
})
export default function modal(state = initialState, action) {
switch(action.type) {
case MODAL_OPEN:
return { modalType: action.modalType, modalProps: action.modalProps };
return state.withMutations(map => {
map.set('modalType', action.modalType)
map.set('modalProps', action.modalProps)
})
case MODAL_CLOSE:
return initialState;
return initialState
default:
return state;
return state
}
};
}

View File

@ -6,16 +6,16 @@ import {
const initialState = Immutable.Map({
popoverType: null,
placement: null,
popoverProps: null,
})
export default function popoverMenu(state = initialState, action) {
switch (action.type) {
case POPOVER_OPEN:
return {
popoverType: action.popoverType,
popoverProps: action.popoverProps,
}
return state.withMutations(map => {
map.set('popoverType', action.popoverType)
map.set('popoverProps', action.popoverProps)
})
case POPOVER_CLOSE:
return initialState
default:

View File

@ -7,7 +7,7 @@ import {
GIF_RESULTS_FETCH_FAIL,
GIF_CATEGORIES_FETCH_REQUEST,
GIF_CATEGORIES_FETCH_SUCCESS,
GIF_CATEGORIES_FETCH_FAIL
GIF_CATEGORIES_FETCH_FAIL,
} from '../actions/tenor'
import { Map as ImmutableMap } from 'immutable'

View File

@ -1,6 +1,6 @@
import IntlMessageFormat from 'intl-messageformat';
import locales from './web_push_locales';
import { unescape } from 'lodash';
import unescape from 'lodash.unescape'
const MAX_NOTIFICATIONS = 5;
const GROUP_TAG = 'tag';

View File

@ -3,13 +3,11 @@ import thunk from 'redux-thunk';
import appReducer from '../reducers';
import loadingBarMiddleware from '../middleware/loading_bar';
import errorsMiddleware from '../middleware/errors';
import soundsMiddleware from '../middleware/sounds';
export default function configureStore() {
return createStore(appReducer, compose(applyMiddleware(
thunk,
loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }),
errorsMiddleware(),
soundsMiddleware()
), window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f));
};

View File

@ -58,7 +58,7 @@ export const getWindowDimension = () => {
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
let userTouching = false
let listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
function touchListener() {
userTouching = true

View File

@ -33,7 +33,6 @@ function main ( ) {
//(Rjc) 2019-05-24 defined but never used
// const React = require('react');
const ReactDOM = require('react-dom');
const Rellax = require('rellax');
const createHistory = require('history').createBrowserHistory;
const scrollToDetailedStatus = () => {
@ -106,12 +105,6 @@ function main ( ) {
scrollToDetailedStatus();
}
const parallaxComponents = document.querySelectorAll('.parallax');
if (parallaxComponents.length > 0 ) {
new Rellax('.parallax', { speed: -1 });
}
if (document.body.classList.contains('with-modals')) {
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
const scrollbarWidthStyle = document.createElement('style');

View File

@ -328,6 +328,10 @@ body {
background-color: #36e991;
}
.backgroundColorBrandLightOpaque_onHover:hover {
background-color: rgba(54,233,145, 0.125)
}
.backgroundColorBrand {
background-color: #21cf7a;
}
@ -392,6 +396,10 @@ body {
fill: #21cf7a;
}
.fillColorBrand_onHover:hover {
fill: #21cf7a;
}
.fillColorSecondary {
fill: #666;
}
@ -472,8 +480,8 @@ body {
max-height: 80vh;
}
.heightMax60PX {
max-height: 60px;
.heightMax56PX {
max-height: 56px;
}
.heightMin50VH {
@ -512,6 +520,10 @@ body {
height: 1px;
}
.height40PX {
height: 40px;
}
.height50PX {
height: 50px;
}
@ -858,6 +870,10 @@ body {
margin-bottom: 10px;
}
.mb5 {
margin-bottom: 5px;
}
.mt10 {
margin-top: 10px;
}

View File

@ -85,8 +85,6 @@ class REST::StatusSerializer < ActiveModel::Serializer
def favourites_count
if instance_options && instance_options[:unfavourite]
# Decrement counter
# https://github.com/tootsuite/mastodon/issues/3166
object.favourites_count - 1
else
object.favourites_count

View File

@ -26,7 +26,6 @@ module.exports = (api) => {
case 'production':
envOptions.debug = false;
config.plugins.push(...[
'lodash',
[
'transform-react-remove-prop-types',
{

View File

@ -14,12 +14,6 @@ const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
const entryPath = join(settings.source_path, settings.source_entry_path);
const packPaths = sync(join(entryPath, extensionGlob));
console.log("localePackPaths", localePackPaths);
console.log("packPaths:", packPaths);
console.log("env:", env);
console.log("settings:", settings);
console.log("output:", output);
module.exports = {
entry: Object.assign(
packPaths.reduce((map, entry) => {
@ -70,11 +64,10 @@ module.exports = {
plugins: [
new webpack.ProvidePlugin({
React: 'react',
Component: ['react', 'Component'],
PureComponent: ['react', 'PureComponent'],
connect: ['react-redux', 'connect'],
PropTypes: 'prop-types',
_s: `${resolve(settings.source_path)}/styles/global.css`
_s: `${resolve(settings.source_path)}/styles/global.css`,
}),
new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new webpack.NormalModuleReplacementPlugin(

View File

@ -10,7 +10,6 @@
"build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
"manage:translations": "node ./config/webpack/translationRunner.js",
"start": "node ./streaming/index.js",
"storybook": "start-storybook",
"test": "${npm_execpath} run test:lint && ${npm_execpath} run test:jest",
"test:lint": "eslint --ext=js .",
"test:jest": "cross-env NODE_ENV=test jest --coverage",
@ -20,9 +19,6 @@
"type": "git",
"url": "https://code.gab.com/gab/social/gab-social"
},
"sideEffects": [
"*.scss"
],
"browserslist": [
"last 2 versions",
"IE >= 11",
@ -62,6 +58,71 @@
},
"private": true,
"dependencies": {
"@clusterws/cws": "^0.14.0",
"array-includes": "^3.0.3",
"axios": "^0.19.0",
"blurhash": "^1.0.0",
"classnames": "^2.2.5",
"detect-passive-events": "^1.0.2",
"dotenv": "^8.0.0",
"draft-js": "^0.11.4",
"emoji-mart": "Gargron/emoji-mart#build",
"es6-symbol": "^3.1.1",
"escape-html": "^1.0.3",
"exif-js": "^2.3.0",
"express": "^4.17.1",
"glob": "^7.1.1",
"http-link-header": "^1.0.2",
"immutable": "^3.8.2",
"intersection-observer": "^0.5.1",
"intl": "^1.2.5",
"intl-messageformat": "^2.2.0",
"is-nan": "^1.2.1",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "^4.5.0",
"lodash.isobject": "^3.0.2",
"lodash.pick": "^4.4.0",
"lodash.sample": "^4.2.1",
"lodash.throttle": "^4.1.1",
"lodash.unescape": "^4.0.1",
"npmlog": "^4.1.2",
"object-assign": "^4.1.1",
"object-fit-images": "^3.2.3",
"object.values": "^1.1.0",
"offline-plugin": "^5.0.7",
"pg": "^6.4.0",
"prop-types": "^15.5.10",
"punycode": "^2.1.0",
"rails-ujs": "^5.2.3",
"react": "^16.13.1",
"react-datepicker": "^2.14.1",
"react-dom": "^16.7.0",
"react-hotkeys": "^1.1.4",
"react-immutable-proptypes": "^2.1.0",
"react-immutable-pure-component": "^1.1.1",
"react-intl": "^2.9.0",
"react-motion": "^0.5.2",
"react-popper": "^1.3.7",
"react-redux": "^6.0.1",
"react-redux-loading-bar": "^4.0.8",
"react-router-dom": "^4.1.1",
"react-router-scroll-4": "^1.0.0-beta.1",
"react-stickynode": "^2.1.1",
"react-swipeable-views": "^0.13.0",
"redis": "^2.7.1",
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"redux-thunk": "^2.2.0",
"requestidlecallback": "^0.3.0",
"reselect": "^4.0.0",
"stringz": "^1.0.0",
"substring-trie": "^1.0.2",
"throng": "^4.0.0",
"tiny-queue": "^0.2.1",
"uuid": "^3.1.0",
"websocket.js": "^0.1.12"
},
"devDependencies": {
"@babel/core": "^7.3.4",
"@babel/plugin-proposal-class-properties": "^7.3.4",
"@babel/plugin-proposal-decorators": "^7.3.0",
@ -74,111 +135,19 @@
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.3.4",
"@clusterws/cws": "^0.14.0",
"array-includes": "^3.0.3",
"autoprefixer": "^9.5.1",
"axios": "^0.19.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.8.0",
"babel-loader": "^8.0.5",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-preval": "^3.0.1",
"babel-plugin-react-intl": "^3.1.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-runtime": "^6.26.0",
"blurhash": "^1.0.0",
"classnames": "^2.2.5",
"compression-webpack-plugin": "^2.0.0",
"cron": "^1.8.2",
"cross-env": "^5.1.4",
"css-loader": "^2.1.1",
"cssnano": "^4.1.10",
"detect-passive-events": "^1.0.2",
"dotenv": "^8.0.0",
"draft-js": "^0.11.4",
"emoji-mart": "Gargron/emoji-mart#build",
"es6-symbol": "^3.1.1",
"escape-html": "^1.0.3",
"exif-js": "^2.3.0",
"express": "^4.17.1",
"file-loader": "^3.0.1",
"glob": "^7.1.1",
"http-link-header": "^1.0.2",
"immutable": "^3.8.2",
"imports-loader": "^0.8.0",
"intersection-observer": "^0.5.1",
"intl": "^1.2.5",
"intl-messageformat": "^2.2.0",
"intl-relativeformat": "^2.2.0",
"is-nan": "^1.2.1",
"js-yaml": "^3.13.1",
"lodash": "^4.7.11",
"lodash.isobject": "^3.0.2",
"mark-loader": "^0.1.6",
"marky": "^1.2.1",
"mini-css-extract-plugin": "^0.9.0",
"mkdirp": "^0.5.1",
"moment": "^2.24.0",
"npmlog": "^4.1.2",
"object-assign": "^4.1.1",
"object-fit-images": "^3.2.3",
"object.values": "^1.1.0",
"offline-plugin": "^5.0.7",
"path-complete-extname": "^1.0.0",
"pg": "^6.4.0",
"postcss-loader": "^3.0.0",
"postcss-object-fit-images": "^1.1.2",
"prop-types": "^15.5.10",
"punycode": "^2.1.0",
"rails-ujs": "^5.2.3",
"react": "^16.12.0",
"react-contenteditable": "^3.3.3",
"react-datepicker": "^2.14.1",
"react-dom": "^16.7.0",
"react-hotkeys": "^1.1.4",
"react-immutable-proptypes": "^2.1.0",
"react-immutable-pure-component": "^1.1.1",
"react-intl": "^2.9.0",
"react-masonry-infinite": "^1.2.2",
"react-motion": "^0.5.2",
"react-notification": "^6.8.4",
"react-overlays": "^0.8.3",
"react-popper": "^1.3.7",
"react-redux": "^6.0.1",
"react-redux-loading-bar": "^4.0.8",
"react-router-dom": "^4.1.1",
"react-router-scroll-4": "^1.0.0-beta.1",
"react-stickynode": "^2.1.1",
"react-swipeable-views": "^0.13.0",
"react-textarea-autosize": "^7.1.0",
"react-toggle": "^4.0.1",
"redis": "^2.7.1",
"redux": "^4.0.1",
"redux-immutable": "^4.0.0",
"redux-thunk": "^2.2.0",
"rellax": "^1.7.1",
"requestidlecallback": "^0.3.0",
"reselect": "^4.0.0",
"rimraf": "^2.6.3",
"sanitize-html": "^1.22.0",
"sass": "^1.20.3",
"sass-loader": "^7.0.3",
"sass-resources-loader": "^2.0.1",
"stringz": "^1.0.0",
"style-loader": "^1.1.3",
"substring-trie": "^1.0.2",
"throng": "^4.0.0",
"tiny-queue": "^0.2.1",
"uglifyjs-webpack-plugin": "^2.1.2",
"uuid": "^3.1.0",
"webpack": "^4.41.5",
"webpack-assets-manifest": "^3.1.1",
"webpack-bundle-analyzer": "^3.1.0",
"webpack-cli": "^3.3.2",
"webpack-merge": "^4.2.1",
"websocket.js": "^0.1.12"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"babel-jest": "^24.8.0",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.7.1",
"eslint": "^5.11.1",
@ -186,12 +155,27 @@
"eslint-plugin-jsx-a11y": "~6.2.1",
"eslint-plugin-promise": "~4.1.1",
"eslint-plugin-react": "~7.12.1",
"file-loader": "^3.0.1",
"imports-loader": "^0.8.0",
"jest": "^24.8.0",
"node-sass": "^4.13.1",
"js-yaml": "^3.13.1",
"mark-loader": "^0.1.6",
"mini-css-extract-plugin": "^0.9.0",
"mkdirp": "^0.5.1",
"path-complete-extname": "^1.0.0",
"postcss-loader": "^3.0.0",
"postcss-object-fit-images": "^1.1.2",
"raf": "^3.4.1",
"react-intl-translations-manager": "^5.0.3",
"react-test-renderer": "^16.7.0",
"rimraf": "^2.6.3",
"uglifyjs-webpack-plugin": "^2.1.2",
"webpack": "^4.41.5",
"webpack-assets-manifest": "^3.1.1",
"webpack-bundle-analyzer": "^3.1.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.1",
"webpack-merge": "^4.2.1",
"yargs": "^12.0.5"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More