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

View File

@ -1,7 +1,6 @@
import api from '../api'; import api from '../api';
import { CancelToken, isCancel } from 'axios'; import { CancelToken, isCancel } from 'axios';
import { throttle } from 'lodash'; import throttle from 'lodash.throttle'
import moment from 'moment';
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light'; import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
import { urlRegex } from '../features/compose/util/url_regex' import { urlRegex } from '../features/compose/util/url_regex'
import { tagHistory } from '../settings'; import { tagHistory } from '../settings';
@ -133,7 +132,7 @@ export function mentionCompose(account, routerHistory) {
export function handleComposeSubmit(dispatch, getState, response, status) { export function handleComposeSubmit(dispatch, getState, response, status) {
if (!dispatch || !getState) return; if (!dispatch || !getState) return;
const isScheduledStatus = response.data['scheduled_at'] !== undefined; const isScheduledStatus = response.data.scheduled_at !== undefined;
if (isScheduledStatus) { if (isScheduledStatus) {
// dispatch(showAlertForError({ // dispatch(showAlertForError({
// response: { // response: {
@ -154,7 +153,7 @@ export function handleComposeSubmit(dispatch, getState, response, status) {
const timeline = getState().getIn(['timelines', timelineId]); const timeline = getState().getIn(['timelines', timelineId]);
if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) { 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']); if (timelineId === 'community') dequeueArgs.onlyMedia = getState().getIn(['settings', 'community', 'other', 'onlyMedia']);
dispatch(dequeueTimeline(timelineId, null, dequeueArgs)); dispatch(dequeueTimeline(timelineId, null, dequeueArgs));
dispatch(updateTimeline(timelineId, { ...response.data })); dispatch(updateTimeline(timelineId, { ...response.data }));
@ -174,7 +173,7 @@ export function submitCompose(routerHistory, group) {
if (!me) return; if (!me) return;
let status = getState().getIn(['compose', 'text'], ''); 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']); const media = getState().getIn(['compose', 'media_attachments']);
// : hack : // : hack :
@ -183,10 +182,10 @@ export function submitCompose(routerHistory, group) {
const hasProtocol = match.startsWith('https://') || match.startsWith('http://') const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
return hasProtocol ? match : `http://${match}` return hasProtocol ? match : `http://${match}`
}) })
statusMarkdown = statusMarkdown.replace(urlRegex, (match) =>{ // statusMarkdown = statusMarkdown.replace(urlRegex, (match) =>{
const hasProtocol = match.startsWith('https://') || match.startsWith('http://') // const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
return hasProtocol ? match : `http://${match}` // return hasProtocol ? match : `http://${match}`
}) // })
dispatch(submitComposeRequest()); dispatch(submitComposeRequest());
dispatch(closeModal()); dispatch(closeModal());
@ -197,12 +196,13 @@ export function submitCompose(routerHistory, group) {
: `/api/v1/statuses/${id}`; : `/api/v1/statuses/${id}`;
const method = id === null ? 'post' : 'put'; const method = id === null ? 'post' : 'put';
let scheduled_at = getState().getIn(['compose', 'scheduled_at'], null); const scheduled_at = getState().getIn(['compose', 'scheduled_at'], null);
if (scheduled_at !== null) scheduled_at = moment.utc(scheduled_at).toDate(); // : todo :
// if (scheduled_at !== null) scheduled_at = moment.utc(scheduled_at).toDate();
api(getState)[method](endpoint, { api(getState)[method](endpoint, {
status, status,
statusMarkdown, // statusMarkdown,
scheduled_at, scheduled_at,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
quote_of_id: getState().getIn(['compose', 'quote_of_id'], 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_INITIALIZE = 'NOTIFICATIONS_INITIALIZE';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; 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_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE'; export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
@ -76,7 +75,6 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
return (dispatch, getState) => { return (dispatch, getState) => {
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const filters = getFilters(getState(), { contextType: 'notifications' }); const filters = getFilters(getState(), { contextType: 'notifications' });
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
let filtered = false; 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) { if (isOnNotificationsPage) {
dispatch({ dispatch({
type: NOTIFICATIONS_UPDATE_QUEUE, type: NOTIFICATIONS_UPDATE_QUEUE,
@ -115,8 +106,7 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
intlMessages, intlMessages,
intlLocale, intlLocale,
}); });
} } else {
else {
dispatch(updateNotifications(notification, intlMessages, intlLocale)); dispatch(updateNotifications(notification, intlMessages, intlLocale));
} }
} }
@ -127,15 +117,13 @@ export function dequeueNotifications() {
const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList()); const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList());
const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0); const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0);
if (totalQueuedNotificationsCount == 0) { if (totalQueuedNotificationsCount === 0) {
return; return;
} } else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
queuedNotifications.forEach(block => { queuedNotifications.forEach(block => {
dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale)); dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale));
}); });
} } else {
else {
dispatch(expandNotifications()); dispatch(expandNotifications());
} }
@ -168,9 +156,9 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
return; return;
} }
console.log("activeFilter:", activeFilter) console.log('activeFilter:', activeFilter)
console.log("excludeTypesFromSettings(getState()):", excludeTypesFromSettings(getState())) console.log('excludeTypesFromSettings(getState()):', excludeTypesFromSettings(getState()))
console.log("excludeTypesFromFilter(activeFilter):", excludeTypesFromFilter(activeFilter)) console.log('excludeTypesFromFilter(activeFilter):', excludeTypesFromFilter(activeFilter))
// : todo : // : todo :
// filter verified and following here too // filter verified and following here too
@ -268,7 +256,7 @@ export function markReadNotifications() {
const last_read = getState().getIn(['notifications', 'lastRead']); const last_read = getState().getIn(['notifications', 'lastRead']);
if (top_notification && top_notification > last_read) { 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({ dispatch({
type: NOTIFICATIONS_MARK_READ, type: NOTIFICATIONS_MARK_READ,
notification: top_notification, notification: top_notification,

View File

@ -1,5 +1,5 @@
import api from '../api'; import api from '../api';
import { debounce } from 'lodash'; import debounce from 'lodash.debounce';
// import { showAlertForError } from './alerts'; // import { showAlertForError } from './alerts';
import { me } from '../initial_state'; 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) { return function (dispatch, getState) {
if (!me) return 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}`) axios.get(`https://api.tenor.com/v1/search?q=${searchText}&media_filter=minimal&limit=30&key=${tenorkey}`)
.then((response) => { .then((response) => {
console.log("response:", response) console.log('response:', response)
dispatch(fetchGifResultsSuccess(response.data.results)) dispatch(fetchGifResultsSuccess(response.data.results))
}).catch(function (error) { }).catch(function (error) {
dispatch(fetchGifResultsFail(error)) dispatch(fetchGifResultsFail(error))

View File

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

View File

@ -14,7 +14,7 @@ export const getLinks = response => {
return LinkHeader.parse(value); return LinkHeader.parse(value);
}; };
let csrfHeader = {}; const csrfHeader = {};
function setCSRFHeader() { function setCSRFHeader() {
const csrfToken = document.querySelector('meta[name=csrf-token]'); 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 buttonOptions
let buttonText 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 { export default class AutosuggestEmoji extends PureComponent {
static propTypes = { static propTypes = {
emoji: PropTypes.object.isRequired, emoji: PropTypes.object.isRequired,
}; }
render () { render () {
const { emoji } = this.props; const { emoji } = this.props
let url; let url
if (emoji.custom) { if (emoji.custom) {
url = emoji.imageUrl; url = emoji.imageUrl
} else { } 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 ( return (
@ -27,7 +27,7 @@ export default class AutosuggestEmoji extends PureComponent {
<img className='emojione' src={url} alt={emoji.native || emoji.colons} /> <img className='emojione' src={url} alt={emoji.native || emoji.colons} />
{emoji.colons} {emoji.colons}
</div> </div>
); )
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -22,33 +22,9 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => { const mapStateToProps = (state, props) => {
const status = getStatus(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 { return {
status, status,
descendantsIds,
} }
} }
@ -62,30 +38,22 @@ class Comment extends ImmutablePureComponent {
static propTypes = { static propTypes = {
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
descendantsIds: ImmutablePropTypes.list, indent: ImmutablePropTypes.number,
}
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')}`
// )
} }
render() { render() {
const { status } = this.props const { status, indent } = this.props
console.log("status:", status) console.log("status:", status)
const style = {
paddingLeft: `${indent * 40}px`,
}
// : todo : add media
return ( return (
<div className={[_s.default, _s.px10, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}> <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(' ')}> <div className={[_s.default, _s.flexRow].join(' ')}>
<NavLink <NavLink

View File

@ -1,159 +1,27 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { makeGetStatus } from '../selectors'; import Comment from './comment'
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'
const messages = defineMessages({ export default class CommentList extends ImmutablePureComponent {
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 {
static propTypes = { static propTypes = {
status: ImmutablePropTypes.map.isRequired, descendants: ImmutablePropTypes.list,
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')}`
// )
} }
render() { render() {
const { status } = this.props const { descendants } = this.props
console.log("status:", status)
return ( return (
<div className={[_s.default, _s.px10, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}> <div>
<div className={[_s.default].join(' ')}> {
descendants.map((descendant, i) => (
<div className={[_s.default, _s.flexRow].join(' ')}> <Comment
<NavLink key={`comment-${descendant.get('statusId')}-${i}`}
to={`/${status.getIn(['account', 'acct'])}`} id={descendant.get('statusId')}
title={status.getIn(['account', 'acct'])} indent={descendant.get('indent')}
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> ))
</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> </div>
) )
} }

View File

@ -1,6 +1,6 @@
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import { debounce } from 'lodash' import debounce from 'lodash.debounce'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import { openPopover, closePopover } from '../actions/popover' import { openPopover, closePopover } from '../actions/popover'
import Icon from './icon' 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 { emojiIndex } from 'emoji-mart';
import { search } from '../emoji_mart_search_light'; import { search } from '../emoji_mart_search_light';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -66,52 +66,44 @@ const getUnitDelay = units => {
export const timeAgoString = (intl, date, now, year) => { export const timeAgoString = (intl, date, now, year) => {
const delta = now - date.getTime() const delta = now - date.getTime()
let relativeTime
if (delta < 10 * SECOND) { if (delta < 10 * SECOND) {
relativeTime = intl.formatMessage(messages.just_now) return intl.formatMessage(messages.just_now)
} else if (delta < 7 * DAY) { } else if (delta < 7 * DAY) {
if (delta < MINUTE) { 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) { } 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) { } 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 { } 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) { } else if (date.getFullYear() === year) {
relativeTime = intl.formatDate(date, shortDateFormatOptions) return intl.formatDate(date, shortDateFormatOptions)
} else {
relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' })
} }
return relativeTime return intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' })
} }
const timeRemainingString = (intl, date, now) => { const timeRemainingString = (intl, date, now) => {
const delta = date.getTime() - now const delta = date.getTime() - now
let relativeTime
if (delta < 10 * SECOND) { if (delta < 10 * SECOND) {
relativeTime = intl.formatMessage(messages.moments_remaining) return intl.formatMessage(messages.moments_remaining)
} else if (delta < MINUTE) { } 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) { } 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) { } else if (delta < DAY) {
relativeTime = intl.formatMessage(messages.hours_remaining, { number: Math.floor(delta / HOUR) }) return intl.formatMessage(messages.hours_remaining, { number: Math.floor(delta / HOUR) })
} else {
relativeTime = intl.formatMessage(messages.days_remaining, { number: Math.floor(delta / DAY) })
} }
return relativeTime return intl.formatMessage(messages.days_remaining, { number: Math.floor(delta / DAY) })
} }
export default export default
@injectIntl @injectIntl
class RelativeTimestamp extends Component { class RelativeTimestamp extends React.Component {
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired, 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 { List as ImmutableList } from 'immutable'
import IntersectionObserverArticle from './intersection_observer_article' import IntersectionObserverArticle from './intersection_observer_article'
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper' import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'
@ -199,7 +199,15 @@ export default class ScrollableList extends PureComponent {
} }
render() { 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 childrenCount = React.Children.count(children);
const trackScroll = true; //placeholder 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' }, mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' }, filters: { id: 'navigation_bar.filters', defaultMessage: 'Muted words' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, 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' }, apps: { id: 'tabs_bar.apps', defaultMessage: 'Apps' },
more: { id: 'sidebar.more', defaultMessage: 'More' }, more: { id: 'sidebar.more', defaultMessage: 'More' },
pro: { id: 'promo.gab_pro', defaultMessage: 'Upgrade to GabPRO' }, pro: { id: 'promo.gab_pro', defaultMessage: 'Upgrade to GabPRO' },
@ -37,7 +37,7 @@ const mapStateToProps = state => {
return { return {
account: getAccount(state, me), account: getAccount(state, me),
moreOpen: state.get('popover').popoverType === 'SIDEBAR_MORE', moreOpen: state.getIn(['popover', 'popoverType']) === 'SIDEBAR_MORE',
notificationCount: state.getIn(['notifications', 'unread']), notificationCount: state.getIn(['notifications', 'unread']),
homeItemsQueueCount: state.getIn(['timelines', 'home', 'totalQueuedItemsCount']), homeItemsQueueCount: state.getIn(['timelines', 'home', 'totalQueuedItemsCount']),
showCommunityTimeline: state.getIn(['settings', 'community', 'shows', 'inSidebar']), showCommunityTimeline: state.getIn(['settings', 'community', 'shows', 'inSidebar']),
@ -95,7 +95,7 @@ class Sidebar extends ImmutablePureComponent {
notificationCount, notificationCount,
homeItemsQueueCount, homeItemsQueueCount,
showCommunityTimeline, showCommunityTimeline,
moreOpen moreOpen,
} = this.props } = this.props
// : todo : // : todo :
@ -104,7 +104,7 @@ class Sidebar extends ImmutablePureComponent {
const acct = account.get('acct') const acct = account.get('acct')
const isPro = account.get('is_pro') const isPro = account.get('is_pro')
console.log("showCommunityTimeline:", showCommunityTimeline) console.log('showCommunityTimeline:', showCommunityTimeline)
const menuItems = [ const menuItems = [
{ {

View File

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

View File

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

View File

@ -169,11 +169,12 @@ export default class Card extends ImmutablePureComponent {
</div> </div>
) )
// : todo : use <Image />
let embed = '' let embed = ''
let thumbnail = interactive ? const 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(' ')} /> <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 (interactive) {
if (embedded) { 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(' ')} className={[_s.default, _s.cursorPointer, _s.backgroundColorOpaque, _s.radiusSmall, _s.py15, _s.px15].join(' ')}
onClick={this.handleEmbedClick} onClick={this.handleEmbedClick}
> >
<Icon id={iconVariant} className={[_s.fillColorWhite].join(' ')}/> <Icon id={iconVariant} className={[_s.fillColorWhite].join(' ')} />
</button> </button>
</div> </div>
} }

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import { is } from 'immutable' import { is } from 'immutable'
import { throttle } from 'lodash' import throttle from 'lodash.throttle'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import { decode } from 'blurhash' import { decode } from 'blurhash'
import { isFullscreen, requestFullscreen, exitFullscreen } from '../utils/fullscreen' 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 { showAlertForError } from '../actions/alerts';
import { import {
createRemovedAccount, createRemovedAccount,
groupRemoveStatus groupRemoveStatus,
} from '../actions/groups'; } from '../actions/groups';
import { makeGetStatus } from '../selectors'; import { makeGetStatus } from '../selectors';
import Status from '../components/status'; import Status from '../components/status';
@ -58,7 +58,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onReply (status, router) { onReply (status, router) {
dispatch((_, getState) => { dispatch((_, getState) => {
let state = getState(); const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) { if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.replyMessage), message: intl.formatMessage(messages.replyMessage),
@ -73,7 +73,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onQuote (status, router) { onQuote (status, router) {
dispatch((_, getState) => { dispatch((_, getState) => {
let state = getState(); const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) { if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.quoteMessage), message: intl.formatMessage(messages.quoteMessage),
@ -107,12 +107,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}, },
onFavorite (status) { onFavorite (status) {
console.log("onFavorite...", status) console.log('onFavorite...', status)
if (status.get('favourited')) { if (status.get('favourited')) {
console.log("unfav...") console.log('unfav...')
dispatch(unfavorite(status)); dispatch(unfavorite(status));
} else { } else {
console.log("fav...") console.log('fav...')
dispatch(favorite(status)); dispatch(favorite(status));
} }
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ const messages = defineMessages({
}) })
const mapStateToProps = (state) => ({ 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) => ({ 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, intl: PropTypes.object.isRequired,
small: PropTypes.bool, small: PropTypes.bool,
onOpenPopover: PropTypes.func.isRequired, onOpenPopover: PropTypes.func.isRequired,
value: PropTypes.oneOf([
'private',
'unlisted',
'public',
]),
} }
handleOnClick = () => { handleOnClick = () => {
@ -42,7 +47,7 @@ class StatusVisibilityButton extends PureComponent {
const { const {
intl, intl,
small, small,
value value,
} = this.props } = this.props
let icon; let icon;

View File

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

View File

@ -71,7 +71,7 @@ class Compose extends ImmutablePureComponent {
render () { render () {
const { showSearch, isSearchPage, intl } = this.props; const { showSearch, isSearchPage, intl } = this.props;
let header = ''; const header = '';
return ( return (
<div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}> <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']), isUploading: !isMatch ? false : state.getIn(['compose', 'is_uploading']),
showSearch: !isMatch ? false : state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), showSearch: !isMatch ? false : state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
anyMedia: !isMatch ? false : state.getIn(['compose', 'media_attachments']).size > 0, 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']), quoteOfId: !isMatch ? null : state.getIn(['compose', 'quote_of_id']),
scheduledAt: !isMatch ? null : state.getIn(['compose', 'scheduled_at']), scheduledAt: !isMatch ? null : state.getIn(['compose', 'scheduled_at']),
account: state.getIn(['accounts', me]), account: state.getIn(['accounts', me]),
hasPoll: !isMatch ? false : state.getIn(['compose', 'poll']),
} }
} }
@ -84,7 +85,7 @@ const mapDispatchToProps = (dispatch) => ({
function mergeProps(stateProps, dispatchProps, ownProps) { function mergeProps(stateProps, dispatchProps, ownProps) {
return Object.assign({}, ownProps, { return Object.assign({}, ownProps, {
...stateProps, ...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) { export function countableText(inputText) {
return inputText return inputText
.replace(urlRegex, urlPlaceholder) .replace(urlRegex, urlPlaceholder)
.replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3'); .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 { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash'; import debounce from 'lodash.debounce'
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts'; import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
import ColumnIndicator from '../../components/column_indicator'; import ColumnIndicator from '../../components/column_indicator';
import AccountAuthorize from './components/account_authorize'; import AccountAuthorize from './components/account_authorize';

View File

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

View File

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

View File

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

View File

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

View File

@ -12,6 +12,7 @@ const mapStateToProps = (state, { activeTab }) => ({
export default export default
@connect(mapStateToProps) @connect(mapStateToProps)
class GroupsCollection extends ImmutablePureComponent { class GroupsCollection extends ImmutablePureComponent {
static propTypes = { static propTypes = {
activeTab: PropTypes.string.isRequired, activeTab: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -35,14 +36,32 @@ class GroupsCollection extends ImmutablePureComponent {
return <ColumnIndicator type='loading' /> return <ColumnIndicator type='loading' />
} }
const halfCount = parseInt(groupIds.size / 2)
console.log("halfCount", halfCount)
return ( return (
<div className={[_s.default, _s.flexRow, _s.flexWrap].join(' ')}> <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} /> <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> </div>
) )
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import { injectIntl, FormattedMessage } from 'react-intl' import { injectIntl, FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash' import debounce from 'lodash.debounce'
import { fetchMutes, expandMutes } from '../actions/mutes' import { fetchMutes, expandMutes } from '../actions/mutes'
import AccountContainer from '../containers/account_container' import AccountContainer from '../containers/account_container'
import ColumnIndicator from '../components/column_indicator' 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 { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import { createSelector } from 'reselect' import { createSelector } from 'reselect'
import { List as ImmutableList } from 'immutable' import { List as ImmutableList } from 'immutable'
import { debounce } from 'lodash' import debounce from 'lodash.debounce'
import { import {
expandNotifications, expandNotifications,
scrollTopNotifications, scrollTopNotifications,

View File

@ -50,7 +50,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onReply (status, router) { onReply (status, router) {
dispatch((_, getState) => { dispatch((_, getState) => {
let state = getState(); const state = getState();
if (state.getIn(['compose', 'text']).trim().length !== 0) { if (state.getIn(['compose', 'text']).trim().length !== 0) {
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
message: intl.formatMessage(messages.replyMessage), 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 { textForScreenReader, defaultMediaVisibility } from '../../components/status/status'
import ColumnIndicator from '../../components/column_indicator' import ColumnIndicator from '../../components/column_indicator'
import Block from '../../components/block' import Block from '../../components/block'
import Comment from '../../components/comment' import CommentList from '../../components/comment_list'
const messages = defineMessages({ const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -57,22 +57,51 @@ const makeMapStateToProps = () => {
username: props.params.username, username: props.params.username,
}) })
// : todo : if is comment (i.e. if any ancestorsIds) use comment not status
let ancestorsIds = Immutable.List() let ancestorsIds = Immutable.List()
let descendantsIds = Immutable.List(); let descendantsIds = Immutable.List()
if (status) { if (status) {
ancestorsIds = ancestorsIds.withMutations(mutable => { // ancestorsIds = ancestorsIds.withMutations(mutable => {
let id = status.get('in_reply_to_id'); // let id = status.get('in_reply_to_id');
while (id) { // while (id) {
mutable.unshift(id); // mutable.unshift(id);
id = state.getIn(['contexts', 'inReplyTos', 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)
}); });
indent++
// ONLY Direct descendants indent = Math.min(2, indent)
descendantsIds = state.getIn(['contexts', 'replies', status.get('id')])
} }
}
})
}
console.log("descendantsIds:", descendantsIds)
return { return {
status, status,
@ -338,6 +367,7 @@ class Status extends ImmutablePureComponent {
renderChildren(list) { renderChildren(list) {
console.log("list:", list) console.log("list:", list)
return null
// : todo : comments // : todo : comments
return list.map(id => ( return list.map(id => (
<Comment <Comment
@ -383,9 +413,9 @@ class Status extends ImmutablePureComponent {
return <ColumnIndicator type='loading' /> return <ColumnIndicator type='loading' />
} }
if (ancestorsIds && ancestorsIds.size > 0) { // if (ancestorsIds && ancestorsIds.size > 0) {
ancestors = this.renderChildren(ancestorsIds) // ancestors = this.renderChildren(ancestorsIds)
} // }
if (descendantsIds && descendantsIds.size > 0) { if (descendantsIds && descendantsIds.size > 0) {
descendants = this.renderChildren(descendantsIds) 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(' ')}/> <div className={[_s.default, _s.mr10, _s.ml10, _s.mb10, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}/>
} }
{descendants} <CommentList descendants={descendantsIds} />
</Block> </Block>
</div> </div>
) )

View File

@ -3,7 +3,7 @@ import BundleColumnError from '../../../components/bundle_column_error'
import Bundle from './bundle' import Bundle from './bundle'
import { me } from '../../../initial_state' import { me } from '../../../initial_state'
export default class WrappedRoute extends Component { export default class WrappedRoute extends PureComponent {
static propTypes = { static propTypes = {
component: PropTypes.func.isRequired, component: PropTypes.func.isRequired,
page: 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 ReactDOM from 'react-dom';
import ready from './ready'; import ready from './ready';
const perf = require('./performance');
function main() { function main() {
perf.start('main()');
// : todo : // : todo :
// if (window.history && history.replaceState) { // if (window.history && history.replaceState) {
// const { pathname, search, hash } = window.location; // const { pathname, search, hash } = window.location;
@ -30,7 +26,6 @@ function main() {
require('offline-plugin/runtime').install(); require('offline-plugin/runtime').install();
store.dispatch(registerPushNotifications.register()); 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() { render() {
const { children, isPro, onOpenGroupCreateModal } = this.props const { children, isPro, onOpenGroupCreateModal } = this.props
let tabs = [ const actions = []
const tabs = [
{ {
title: 'Featured', title: 'Featured',
to: '/groups' to: '/groups',
}, },
{ {
title: 'New', title: 'New',
to: '/groups/new' to: '/groups/new',
}, },
{ {
title: 'My Groups', title: 'My Groups',
to: '/groups/browse/member' to: '/groups/browse/member',
}, },
] ]
let actions = []
if (isPro) { if (isPro) {
actions = [{ actions.push({
icon: 'group-add', icon: 'add',
onClick: onOpenGroupCreateModal, onClick: onOpenGroupCreateModal,
}] })
tabs.push({ tabs.push({
title: 'Admin', title: 'Admin',
to: '/groups/browse/admin' to: '/groups/browse/admin',
}) })
} }
@ -82,4 +82,5 @@ class GroupsPage extends PureComponent {
</DefaultLayout> </DefaultLayout>
) )
} }
} }

View File

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

View File

@ -13,7 +13,7 @@ import ProfileLayout from '../layouts/profile_layout'
const mapStateToProps = (state, { params: { username } }) => { const mapStateToProps = (state, { params: { username } }) => {
const accounts = state.getIn(['accounts']) 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 accountId = !!account ? account.get('id') : -1
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false) const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false)
@ -33,6 +33,7 @@ const mapStateToProps = (state, { params: { username } }) => {
export default export default
@connect(mapStateToProps) @connect(mapStateToProps)
class ProfilePage extends ImmutablePureComponent { class ProfilePage extends ImmutablePureComponent {
static propTypes = { static propTypes = {
children: PropTypes.node, children: PropTypes.node,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
@ -59,7 +60,7 @@ class ProfilePage extends ImmutablePureComponent {
const { const {
account, account,
children, children,
unavailable unavailable,
} = this.props } = this.props
return ( return (
@ -86,4 +87,5 @@ class ProfilePage extends ImmutablePureComponent {
</ProfileLayout> </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_OPTION_REMOVE,
COMPOSE_POLL_SETTINGS_CHANGE, COMPOSE_POLL_SETTINGS_CHANGE,
COMPOSE_SCHEDULED_AT_CHANGE, COMPOSE_SCHEDULED_AT_CHANGE,
COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY,
} from '../actions/compose'; } from '../actions/compose';
import { TIMELINE_DELETE } from '../actions/timelines'; import { TIMELINE_DELETE } from '../actions/timelines';
import { STORE_HYDRATE } from '../actions/store'; 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, modalType: null,
modalProps: {}, modalProps: null,
}; })
export default function modal(state = initialState, action) { export default function modal(state = initialState, action) {
switch(action.type) { switch(action.type) {
case MODAL_OPEN: 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: case MODAL_CLOSE:
return initialState; return initialState
default: default:
return state; return state
} }
}; }

View File

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

View File

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

View File

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

View File

@ -3,13 +3,11 @@ import thunk from 'redux-thunk';
import appReducer from '../reducers'; import appReducer from '../reducers';
import loadingBarMiddleware from '../middleware/loading_bar'; import loadingBarMiddleware from '../middleware/loading_bar';
import errorsMiddleware from '../middleware/errors'; import errorsMiddleware from '../middleware/errors';
import soundsMiddleware from '../middleware/sounds';
export default function configureStore() { export default function configureStore() {
return createStore(appReducer, compose(applyMiddleware( return createStore(appReducer, compose(applyMiddleware(
thunk, thunk,
loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }), loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }),
errorsMiddleware(), errorsMiddleware(),
soundsMiddleware()
), window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f)); ), 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 const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
let userTouching = false let userTouching = false
let listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
function touchListener() { function touchListener() {
userTouching = true userTouching = true

View File

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

View File

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

View File

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

View File

@ -26,7 +26,6 @@ module.exports = (api) => {
case 'production': case 'production':
envOptions.debug = false; envOptions.debug = false;
config.plugins.push(...[ config.plugins.push(...[
'lodash',
[ [
'transform-react-remove-prop-types', '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 entryPath = join(settings.source_path, settings.source_entry_path);
const packPaths = sync(join(entryPath, extensionGlob)); 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 = { module.exports = {
entry: Object.assign( entry: Object.assign(
packPaths.reduce((map, entry) => { packPaths.reduce((map, entry) => {
@ -70,11 +64,10 @@ module.exports = {
plugins: [ plugins: [
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
React: 'react', React: 'react',
Component: ['react', 'Component'],
PureComponent: ['react', 'PureComponent'], PureComponent: ['react', 'PureComponent'],
connect: ['react-redux', 'connect'], connect: ['react-redux', 'connect'],
PropTypes: 'prop-types', 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.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new webpack.NormalModuleReplacementPlugin( new webpack.NormalModuleReplacementPlugin(

View File

@ -10,7 +10,6 @@
"build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack", "build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
"manage:translations": "node ./config/webpack/translationRunner.js", "manage:translations": "node ./config/webpack/translationRunner.js",
"start": "node ./streaming/index.js", "start": "node ./streaming/index.js",
"storybook": "start-storybook",
"test": "${npm_execpath} run test:lint && ${npm_execpath} run test:jest", "test": "${npm_execpath} run test:lint && ${npm_execpath} run test:jest",
"test:lint": "eslint --ext=js .", "test:lint": "eslint --ext=js .",
"test:jest": "cross-env NODE_ENV=test jest --coverage", "test:jest": "cross-env NODE_ENV=test jest --coverage",
@ -20,9 +19,6 @@
"type": "git", "type": "git",
"url": "https://code.gab.com/gab/social/gab-social" "url": "https://code.gab.com/gab/social/gab-social"
}, },
"sideEffects": [
"*.scss"
],
"browserslist": [ "browserslist": [
"last 2 versions", "last 2 versions",
"IE >= 11", "IE >= 11",
@ -62,6 +58,71 @@
}, },
"private": true, "private": true,
"dependencies": { "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/core": "^7.3.4",
"@babel/plugin-proposal-class-properties": "^7.3.4", "@babel/plugin-proposal-class-properties": "^7.3.4",
"@babel/plugin-proposal-decorators": "^7.3.0", "@babel/plugin-proposal-decorators": "^7.3.0",
@ -74,111 +135,19 @@
"@babel/preset-env": "^7.3.4", "@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0", "@babel/preset-react": "^7.0.0",
"@babel/runtime": "^7.3.4", "@babel/runtime": "^7.3.4",
"@clusterws/cws": "^0.14.0",
"array-includes": "^3.0.3",
"autoprefixer": "^9.5.1", "autoprefixer": "^9.5.1",
"axios": "^0.19.0", "babel-eslint": "^10.0.1",
"babel-jest": "^24.8.0",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"babel-plugin-lodash": "^3.3.4", "babel-plugin-lodash": "^3.3.4",
"babel-plugin-preval": "^3.0.1", "babel-plugin-preval": "^3.0.1",
"babel-plugin-react-intl": "^3.1.0", "babel-plugin-react-intl": "^3.1.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24", "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"blurhash": "^1.0.0",
"classnames": "^2.2.5",
"compression-webpack-plugin": "^2.0.0", "compression-webpack-plugin": "^2.0.0",
"cron": "^1.8.2",
"cross-env": "^5.1.4", "cross-env": "^5.1.4",
"css-loader": "^2.1.1", "css-loader": "^2.1.1",
"cssnano": "^4.1.10", "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": "^3.8.0",
"enzyme-adapter-react-16": "^1.7.1", "enzyme-adapter-react-16": "^1.7.1",
"eslint": "^5.11.1", "eslint": "^5.11.1",
@ -186,12 +155,27 @@
"eslint-plugin-jsx-a11y": "~6.2.1", "eslint-plugin-jsx-a11y": "~6.2.1",
"eslint-plugin-promise": "~4.1.1", "eslint-plugin-promise": "~4.1.1",
"eslint-plugin-react": "~7.12.1", "eslint-plugin-react": "~7.12.1",
"file-loader": "^3.0.1",
"imports-loader": "^0.8.0",
"jest": "^24.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", "raf": "^3.4.1",
"react-intl-translations-manager": "^5.0.3", "react-intl-translations-manager": "^5.0.3",
"react-test-renderer": "^16.7.0", "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-dev-server": "^3.10.1",
"webpack-merge": "^4.2.1",
"yargs": "^12.0.5" "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