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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,8 +2,6 @@ import { Fragment } from 'react'
import ImmutablePropTypes from 'react-immutable-proptypes'
import classNames from 'classnames/bind'
import ImmutablePureComponent from 'react-immutable-pure-component'
import Textarea from 'react-textarea-autosize'
import ContentEditable from 'react-contenteditable'
import { isRtl } from '../../utils/rtl'
import { textAtCursorMatchesToken } from '../../utils/cursor_token_match'
import AutosuggestAccount from '../autosuggest_account'
@@ -83,39 +81,39 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
if (e.which === 229 || e.isComposing) return;
switch (e.key) {
case 'Escape':
if (suggestions.size === 0 || suggestionsHidden) {
document.querySelector('.ui').parentElement.focus();
} else {
e.preventDefault();
this.setState({ suggestionsHidden: true });
}
case 'Escape':
if (suggestions.size === 0 || suggestionsHidden) {
document.querySelector('.ui').parentElement.focus();
} else {
e.preventDefault();
this.setState({ suggestionsHidden: true });
}
break;
case 'ArrowDown':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
}
break;
case 'ArrowDown':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
}
break;
case 'ArrowUp':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
}
break;
case 'ArrowUp':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
}
break;
case 'Enter':
case 'Tab':
// Select suggestion
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
e.stopPropagation();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
}
break;
case 'Enter':
case 'Tab':
// Select suggestion
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
e.stopPropagation();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
}
break;
break;
}
if (e.defaultPrevented || !this.props.onKeyDown) return;
@@ -210,7 +208,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
id,
maxLength,
textarea,
prependIcon
prependIcon,
} = this.props
const { suggestionsHidden } = this.state
@@ -270,7 +268,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
onPaste={this.onPaste}
aria-autocomplete='list'
/> */ }
{ /*
<Textarea
className={_s.default}
@@ -307,6 +305,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
</div>
{children}
</div>
{ /* : todo : */ }
<div className='autosuggest-textarea__suggestions-wrapper'>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,8 +32,8 @@ const POPOVER_COMPONENTS = {
}
const mapStateToProps = state => ({
type: state.get('popover').popoverType,
props: state.get('popover').popoverProps,
type: state.getIn(['popover', 'popoverType']),
props: state.getIn(['popover', 'popoverProps'], {}),
})
const mapDispatchToProps = (dispatch) => ({
@@ -45,6 +45,7 @@ const mapDispatchToProps = (dispatch) => ({
export default
@connect(mapStateToProps, mapDispatchToProps)
class PopoverRoot extends PureComponent {
static propTypes = {
type: PropTypes.string,
props: PropTypes.object,
@@ -95,22 +96,22 @@ class PopoverRoot extends PureComponent {
let element
switch (e.key) {
case 'ArrowDown':
element = items[index + 1]
if (element) element.focus()
break
case 'ArrowUp':
element = items[index - 1]
if (element) element.focus()
break
case 'Home':
element = items[0]
if (element) element.focus()
break
case 'End':
element = items[items.length - 1]
if (element) element.focus()
break
case 'ArrowDown':
element = items[index + 1]
if (element) element.focus()
break
case 'ArrowUp':
element = items[index - 1]
if (element) element.focus()
break
case 'Home':
element = items[0]
if (element) element.focus()
break
case 'End':
element = items[items.length - 1]
if (element) element.focus()
break
}
}
@@ -172,4 +173,5 @@ class PopoverRoot extends PureComponent {
</PopoverBase>
)
}
}

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
import { throttle } from 'lodash'
import throttle from 'lodash.throttle'
import { List as ImmutableList } from 'immutable'
import IntersectionObserverArticle from './intersection_observer_article'
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper'
@@ -139,14 +139,14 @@ export default class ScrollableList extends PureComponent {
this.lastScrollWasSynthetic = false;
}
}, 150, {
trailing: true,
});
trailing: true,
});
handleWheel = throttle(() => {
this.scrollToTopOnMouseIdle = false;
}, 150, {
trailing: true,
});
trailing: true,
});
getSnapshotBeforeUpdate(prevProps) {
const someItemInserted = React.Children.count(prevProps.children) > 0 &&
@@ -199,7 +199,15 @@ export default class ScrollableList extends PureComponent {
}
render() {
const { children, scrollKey, showLoading, isLoading, hasMore, emptyMessage, onLoadMore } = this.props;
const {
children,
scrollKey,
showLoading,
isLoading,
hasMore,
emptyMessage,
onLoadMore,
} = this.props
const childrenCount = React.Children.count(children);
const trackScroll = true; //placeholder

View File

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

View File

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

View File

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

View File

@@ -272,7 +272,7 @@ class Status extends ImmutablePureComponent {
group,
promoted,
borderless,
isChild
isChild,
} = this.props
let media = null
@@ -287,7 +287,7 @@ class Status extends ImmutablePureComponent {
// status = status.get('reblog');
// }
let { status, ...other } = this.props;
const { status, ...other } = this.props;
// console.log("replies:", this.props.replies)
@@ -403,7 +403,7 @@ class Status extends ImmutablePureComponent {
borderColorSecondary: !borderless,
border1PX: !borderless,
pb10: isChild && status.get('media_attachments').size === 0,
pb5: isChild && status.get('media_attachments').size > 1,
pb5: isChild && status.get('media_attachments').size > 1,
cursorPointer: isChild,
backgroundSubtle_onHover: isChild,
})
@@ -445,7 +445,7 @@ class Status extends ImmutablePureComponent {
<Status status={status.get('quoted_status')} isChild />
</div>
}
{
!isChild &&
<StatusActionBar status={status} {...other} />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash';
import debounce from 'lodash.debounce'
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
import ColumnIndicator from '../../components/column_indicator';
import AccountAuthorize from './components/account_authorize';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -51,14 +51,14 @@ export function getScreenBreakpoint(width) {
export const getWindowDimension = () => {
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
const height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
return { width, height }
}
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
let userTouching = false
let listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false
function touchListener() {
userTouching = true

View File

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

View File

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

View File

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