This commit is contained in:
mgabdev 2020-03-08 19:02:28 -04:00
parent 2f9dbfa91d
commit 0b94033011
16 changed files with 324 additions and 167 deletions

View File

@ -129,17 +129,6 @@ export function mentionCompose(account, routerHistory) {
};
};
export function directCompose(account, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_DIRECT,
account: account,
});
ensureComposeIsVisible(getState, routerHistory);
};
};
export function handleComposeSubmit(dispatch, getState, response, status) {
if (!dispatch || !getState) return;

View File

@ -106,6 +106,7 @@ export default class Button extends PureComponent {
colorPrimary: color === COLORS.primary,
colorSecondary: color === COLORS.secondary,
colorTertiary: color === COLORS.tertiary,
colorWhite: color === COLORS.white,
colorBrand: color === COLORS.brand,

View File

@ -3,31 +3,127 @@ 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 { me } from '../initial_state'
import { makeGetStatus } from '../selectors';
import CommentHeader from './comment_header'
import Avatar from './avatar'
import DisplayName from './display_name'
import Button from './button'
import DisplayName from './display_name'
import DotTextSeperator from './dot_text_seperator'
import RelativeTimestamp from './relative_timestamp'
import Text from './text'
import StatusContent from './status_content'
const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
})
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const mapStateToProps = (state, props) => ({
status: getStatus(state, props),
});
return mapStateToProps;
};
export default
@injectIntl
@connect(makeMapStateToProps)
class Comment extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
}
render() {
handleClick = () => {
// if (this.props.onClick) {
// this.props.onClick();
// return;
// }
//
// if (!this.context.router) return;
// this.context.router.history.push(
// `/${this._properStatus().getIn(['account', 'acct'])}/posts/${this._properStatus().get('id')}`
// )
}
render() {
const { status } = this.props
console.log("status:", status)
return (
<div>
<div className={[_s.default, _s.paddingHorizontal10PX, _s.marginBottom10PX, _s.paddingVertical5PX].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.marginRight10PX, _s.paddingTop5PX].join(' ')}
>
<Avatar account={status.get('account')} size={32} />
</NavLink>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.paddingHorizontal10PX, _s.paddingVertical5PX, _s.radiusSmall, _s.backgroundSubtle].join(' ')}>
<div className={_s.paddingTop2PX}>
<CommentHeader status={status} />
</div>
<div className={[_s.paddingVertical5PX].join(' ')}>
<StatusContent
status={status}
onClick={this.handleClick}
isComment
collapsable
/>
</div>
</div>
<div className={[_s.default, _s.flexRow, _s.marginTop5PX].join(' ')}>
<Button
text
radiusSmall
backgroundColor='none'
color='tertiary'
className={[_s.paddingHorizontal5PX, _s.backgroundSubtle_onHover, _s.paddingVertical2PX, _s.marginRight5PX].join(' ')}
>
<Text size='extraSmall' color='inherit' weight='bold'>
Like
</Text>
</Button>
<Button
text
radiusSmall
backgroundColor='none'
color='tertiary'
className={[_s.paddingHorizontal5PX, _s.backgroundSubtle_onHover, _s.paddingVertical2PX, _s.marginRight5PX].join(' ')}
>
<Text size='extraSmall' color='inherit' weight='bold'>
Reply
</Text>
</Button>
<Button
text
radiusSmall
backgroundColor='none'
color='tertiary'
className={[_s.paddingHorizontal5PX, _s.backgroundSubtle_onHover, _s.paddingVertical2PX, _s.marginRight5PX].join(' ')}
>
<Text size='extraSmall' color='inherit' weight='bold'>
···
</Text>
</Button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,120 @@
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 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'
export default
@injectIntl
class CommentHeader extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
}
render() {
const { status } = this.props
const repostCount = 12 // status.get('reblogs_count')
const favoriteCount = 2 // status.get('favourites_count') // : todo :
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
return (
<div className={[_s.default, _s.alignItemsStart, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.alignItemsCenter].join(' ')}>
<NavLink
className={[_s.default, _s.flexRow, _s.alignItemsStart, _s.noUnderline].join(' ')}
to={`/${status.getIn(['account', 'acct'])}`}
title={status.getIn(['account', 'acct'])}
>
<DisplayName account={status.get('account')} small />
</NavLink>
{
status.get('revised_at') !== null &&
<Fragment>
<DotTextSeperator />
<Button
text
underlineOnHover
backgroundColor='none'
color='tertiary'
onClick={this.handleOpenStatusEdits}
className={_s.marginLeft5PX}
>
<Text size='extraSmall' color='inherit'>
Edited
</Text>
</Button>
</Fragment>
}
{
favoriteCount > 0 &&
<Fragment>
<DotTextSeperator />
<Button
text
underlineOnHover
backgroundColor='none'
color='tertiary'
to={statusUrl}
className={_s.marginLeft5PX}
>
<Text size='extraSmall' color='inherit'>
{favoriteCount}
&nbsp;Likes
</Text>
</Button>
</Fragment>
}
{
repostCount > 0 &&
<Fragment>
<DotTextSeperator />
<Button
text
underlineOnHover
backgroundColor='none'
color='tertiary'
to={statusUrl}
className={_s.marginLeft5PX}
>
<Text size='extraSmall' color='inherit'>
{repostCount}
&nbsp;Reposts
</Text>
</Button>
</Fragment>
}
<DotTextSeperator />
<Button
text
underlineOnHover
backgroundColor='none'
color='tertiary'
to={statusUrl}
className={_s.marginLeft5PX}
>
<Text size='extraSmall' color='inherit'>
<RelativeTimestamp timestamp={status.get('created_at')} />
</Text>
</Button>
</div>
</div>
)
}
}

View File

@ -26,6 +26,7 @@ class DisplayName extends ImmutablePureComponent {
openUserInfoPopover: PropTypes.func.isRequired,
closeUserInfoPopover: PropTypes.func.isRequired,
multiline: PropTypes.bool,
small: PropTypes.bool,
large: PropTypes.bool,
noHover: PropTypes.bool,
noUsername: PropTypes.bool,
@ -47,7 +48,8 @@ class DisplayName extends ImmutablePureComponent {
multiline,
large,
noHover,
noUsername
noUsername,
small
} = this.props
if (!account) return null
@ -70,10 +72,11 @@ class DisplayName extends ImmutablePureComponent {
whiteSpaceNoWrap: 1,
fontWeightBold: 1,
colorPrimary: 1,
lineHeight125: 1,
marginRight2PX: 1,
lineHeight125: !small,
fontSize14PX: small,
fontSize15PX: !large,
fontSize24PX: large,
fontSize24PX: large && !small,
})
const usernameClasses = cx({
@ -88,11 +91,14 @@ class DisplayName extends ImmutablePureComponent {
lineHeight15: multiline,
lineHeight125: !multiline,
marginLeft5PX: !multiline,
fontSize14PX: small,
fontSize15PX: !large,
fontSize16PX: large
fontSize16PX: large && !small,
})
const iconSize = !!large ? '19px' : '16px'
const iconSize =
!!large ? '19px' :
!!small ? '14px' : '16px'
// : todo :
return (

View File

@ -4,7 +4,7 @@ export default class DotTextSeperator extends PureComponent {
render() {
return (
<Text size='small' color='secondary' className={_s.marginLeft5PX}></Text>
<Text size='small' color='secondary' className={_s.marginLeft5PX}>·</Text>
)
}

View File

@ -13,7 +13,7 @@ import Avatar from '../avatar';
import StatusQuote from '../status_quote';
import RelativeTimestamp from '../relative_timestamp';
import DisplayName from '../display_name';
import StatusContent from '../status_content';
import StatusContent from '../status_content'
import StatusActionBar from '../status_action_bar';
import Block from '../block';
import Icon from '../icon';
@ -104,6 +104,7 @@ class Status extends ImmutablePureComponent {
promoted: PropTypes.bool,
onOpenProUpgradeModal: PropTypes.func,
intl: PropTypes.object.isRequired,
borderless: PropTypes.bool,
};
// Avoid checking props that are functions (and whose equality will always
@ -279,7 +280,8 @@ class Status extends ImmutablePureComponent {
unread,
showThread,
group,
promoted
promoted,
borderless
} = this.props
let media = null
@ -450,12 +452,21 @@ class Status extends ImmutablePureComponent {
const containerClasses = cx({
default: 1,
marginBottom15PX: 1,
marginBottom15PX: !borderless,
backgroundColorPrimary: 1,
paddingBottom15PX: featured,
borderBottom1PX: featured,
borderColorSecondary: featured,
})
const innerContainerClasses = cx({
default: 1,
overflowHidden: 1,
radiusSmall: !borderless,
borderColorSecondary: !borderless,
border1PX: !borderless,
})
return (
<HotKeys handlers={handlers}>
<div
@ -465,7 +476,7 @@ class Status extends ImmutablePureComponent {
aria-label={textForScreenReader(intl, status, rebloggedByText)}
ref={this.handleRef}
>
<Block>
<div className={innerContainerClasses}>
{prepend}
@ -497,11 +508,11 @@ class Status extends ImmutablePureComponent {
) */ }
<StatusActionBar status={status} account={account} {...other} />
{/*<div className={[_s.default, _s.borderTop1PX, _s.borderColorSecondary, _s.paddingTop10PX, _s.paddingHorizontal15PX, _s.marginBottom10PX].join(' ')}>
<div className={[_s.default, _s.borderTop1PX, _s.borderColorSecondary, _s.paddingTop10PX, _s.paddingHorizontal15PX, _s.marginBottom10PX].join(' ')}>
<ComposeFormContainer statusId={status.get('id')} shouldCondense />
</div>*/}
</div>
</div>
</Block>
</div>
</div>
</HotKeys>
);

View File

@ -132,7 +132,7 @@ class StatusActionBar extends ImmutablePureComponent {
const repostCount = status.get('reblogs_count')
const repostTitle = !publicStatus ? formatMessage(messages.cannot_repost) : formatMessage(messages.repost)
const favoriteCount = status.get('favorites_count') // : todo :
const favoriteCount = status.get('favourites_count') // : todo :
const shareButton = ('share' in navigator) && status.get('visibility') === 'public' && (
<IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />

View File

@ -33,6 +33,7 @@ class StatusContent extends ImmutablePureComponent {
onClick: PropTypes.func,
collapsable: PropTypes.bool,
intl: PropTypes.object.isRequired,
isComment: PropTypes.bool,
};
state = {
@ -155,7 +156,7 @@ class StatusContent extends ImmutablePureComponent {
}
render () {
const { status, intl } = this.props;
const { status, intl, isComment } = this.props;
if (status.get('content').length === 0) return null;
@ -208,7 +209,7 @@ class StatusContent extends ImmutablePureComponent {
const hasMarginBottom = !!status.get('card') || !!status.get('poll') || status.get('media_attachments').size > 0
const containerClasses = cx({
paddingHorizontal15PX: 1,
paddingHorizontal15PX: !isComment,
marginBottom15PX: hasMarginBottom,
})

View File

@ -2,7 +2,6 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import {
replyCompose,
mentionCompose,
directCompose,
quoteCompose,
} from '../actions/compose';
import {
@ -150,10 +149,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
dispatch(editStatus(status));
},
onDirect (account, router) {
dispatch(directCompose(account, router));
},
onMention (account, router) {
dispatch(mentionCompose(account, router));
},

View File

@ -11,7 +11,6 @@ import {
} from '../../../actions/accounts';
import {
mentionCompose,
directCompose,
} from '../../../actions/compose';
import { initMuteModal } from '../../../actions/mutes';
import { initReport } from '../../../actions/reports';
@ -78,11 +77,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
onMention (account, router) {
dispatch(mentionCompose(account, router));
},
onDirect (account, router) {
dispatch(directCompose(account, router));
},
onRepostToggle (account) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
dispatch(followAccount(account.get('id'), false));

View File

@ -2,7 +2,6 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import {
replyCompose,
mentionCompose,
directCompose,
} from '../../../actions/compose';
import {
reblog,
@ -116,10 +115,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}
},
onDirect (account, router) {
dispatch(directCompose(account, router));
},
onMention (account, router) {
dispatch(mentionCompose(account, router));
},

View File

@ -1,5 +1,4 @@
import Immutable from 'immutable';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
@ -8,15 +7,14 @@ import { fetchStatus } from '../../actions/statuses';
import {
favorite,
unfavorite,
reblog,
unreblog,
repost,
unrepost,
pin,
unpin,
} from '../../actions/interactions';
import {
replyCompose,
mentionCompose,
directCompose,
} from '../../actions/compose';
import { blockAccount } from '../../actions/accounts';
import {
@ -31,12 +29,11 @@ import { initReport } from '../../actions/reports';
import { openModal } from '../../actions/modal';
import { boostModal, deleteModal, me } from '../../initial_state';
import { makeGetStatus } from '../../selectors';
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../../utils/fullscreen';
import StatusContainer from '../../containers/status_container';
import { ColumnHeader } from '../../components/column_header';
import { textForScreenReader, defaultMediaVisibility } from '../../components/status/status';
import Icon from '../../components/icon';
import ColumnIndicator from '../../components/column_indicator';
import Block from '../../components/block'
import Comment from '../../components/comment'
const messages = defineMessages({
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
@ -53,15 +50,15 @@ const messages = defineMessages({
});
const makeMapStateToProps = () => {
const getStatus = makeGetStatus();
const getStatus = makeGetStatus()
const mapStateToProps = (state, props) => {
const status = getStatus(state, {
id: props.params.statusId,
username: props.params.username,
});
})
let ancestorsIds = Immutable.List();
let ancestorsIds = Immutable.List()
let descendantsIds = Immutable.List();
if (status) {
@ -74,24 +71,8 @@ const makeMapStateToProps = () => {
}
});
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);
});
}
}
});
// ONLY Direct descendants
descendantsIds = state.getIn(['contexts', 'replies', status.get('id')])
}
return {
@ -136,10 +117,6 @@ class Status extends ImmutablePureComponent {
this.props.dispatch(fetchStatus(this.props.params.statusId));
}
componentDidMount() {
attachFullscreenListener(this.onFullScreenChange);
}
componentWillReceiveProps(nextProps) {
if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
this._scrolledIntoView = false;
@ -214,10 +191,6 @@ class Status extends ImmutablePureComponent {
}
}
handleDirectClick = (account, router) => {
this.props.dispatch(directCompose(account, router));
}
handleMentionClick = (account, router) => {
this.props.dispatch(mentionCompose(account, router));
}
@ -372,16 +345,16 @@ class Status extends ImmutablePureComponent {
}
renderChildren(list) {
console.log("list:", list)
// : todo : comments
return list.map(id => (
<StatusContainer
key={id}
<Comment
key={`comment-${id}`}
id={id}
onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown}
contextType='thread'
/>
));
))
}
setRef = c => {
@ -389,11 +362,9 @@ class Status extends ImmutablePureComponent {
}
componentDidUpdate() {
if (this._scrolledIntoView) {
return;
}
if (this._scrolledIntoView) return
const { status, ancestorsIds } = this.props;
const { status, ancestorsIds } = this.props
if (status && ancestorsIds && ancestorsIds.size > 0) {
const element = this.node.querySelectorAll('.focusable')[ancestorsIds.size - 1];
@ -405,28 +376,27 @@ class Status extends ImmutablePureComponent {
}
}
componentWillUnmount() {
detachFullscreenListener(this.onFullScreenChange);
}
onFullScreenChange = () => {
this.setState({ fullscreen: isFullscreen() });
}
render() {
let ancestors, descendants;
const { status, ancestorsIds, descendantsIds, intl, domain } = this.props;
const {
status,
ancestorsIds,
descendantsIds,
intl,
domain
} = this.props
let ancestors, descendants
if (status === null) {
return (<ColumnIndicator type='loading' />);
return <ColumnIndicator type='loading' />
}
if (ancestorsIds && ancestorsIds.size > 0) {
ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
ancestors = this.renderChildren(ancestorsIds)
}
if (descendantsIds && descendantsIds.size > 0) {
descendants = <div>{this.renderChildren(descendantsIds)}</div>;
descendants = this.renderChildren(descendantsIds)
}
const handlers = {
@ -441,68 +411,41 @@ class Status extends ImmutablePureComponent {
toggleSensitive: this.handleHotkeyToggleSensitive,
};
/* me &&
<ColumnHeader
extraButton={(
<button
className='column-header__button'
title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)}
aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)}
onClick={this.handleToggleAll}
aria-pressed={
status.get('hidden') ? 'false' : 'true'}
>
<Icon id={status.get('hidden') ? 'eye-slash' : 'eye'
}
/>
</button>
)}
/>
*/
return (
<div ref={this.setRef}>
{ancestors}
<Block>
{
/* ancestors */
}
<HotKeys handlers={handlers}>
<div className={_s.outlineNone} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
{ /* <DetailedStatus
status={status}
onOpenVideo={this.handleOpenVideo}
onOpenMedia={this.handleOpenMedia}
onToggleHidden={this.handleToggleHidden}
domain={domain}
showMedia={this.state.showMedia}
onToggleMediaVisibility={this.handleToggleMediaVisibility}
/> */ }
<HotKeys handlers={handlers}>
<div className={_s.outlineNone} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
<StatusContainer
id={status.get('id')}
contextType={'timelineId'}
showThread
borderless={descendantsIds && descendantsIds.size > 0}
// onOpenVideo={this.handleOpenVideo}
// onOpenMedia={this.handleOpenMedia}
// onToggleHidden={this.handleToggleHidden}
// domain={domain}
// showMedia={this.state.showMedia}
// onToggleMediaVisibility={this.handleToggleMediaVisibility}
/>
</div>
</HotKeys>
<StatusContainer
id={status.get('id')}
contextType={'timelineId'}
showThread
/>
{
descendantsIds && descendantsIds.size > 0 &&
<div className={[_s.default, _s.marginRight10PX, _s.marginLeft10PX, _s.marginBottom10PX, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}/>
}
{/*<ActionBar
status={status}
onReply={this.handleReplyClick}
onFavorite={this.handleFavoriteClick}
onRepost={this.handleRepostClick}
onDelete={this.handleDeleteClick}
onDirect={this.handleDirectClick}
onMention={this.handleMentionClick}
onMute={this.handleMuteClick}
onMuteConversation={this.handleConversationMuteClick}
onBlock={this.handleBlockClick}
onReport={this.handleReport}
onPin={this.handlePin}
onEmbed={this.handleEmbed}
/>*/}
</div>
</HotKeys>
{descendants}
{descendants}
</Block>
</div>
);
)
}
}

View File

@ -504,7 +504,7 @@ class UI extends PureComponent {
{children}
</SwitchingArea>
<NotificationsContainer />
{ /* <NotificationsContainer /> */ }
<ModalRoot />
<PopoverRoot />
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />

View File

@ -327,6 +327,10 @@ body {
color: #fff;
}
.colorTertiary {
color: #777;
}
.colorSecondary {
color: #4B4F55;
}
@ -797,6 +801,10 @@ body {
padding-top: 5px;
}
.paddingTop2PX {
padding-top: 2px;
}
.paddingBottom10PX {
padding-bottom: 10px;
}

View File

@ -8,10 +8,7 @@ const AssetsManifestPlugin = require('webpack-assets-manifest');
const extname = require('path-complete-extname');
const { env, settings, output } = require('./configuration');
const rules = require('./rules');
const localePackPaths = [
'/Users/m3/Documents/dev/gab-social/tmp/packs/locale_en.js',
];
//require('./generateLocalePacks');
const localePackPaths = require('./generateLocalePacks');
const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
const entryPath = join(settings.source_path, settings.source_entry_path);
@ -77,7 +74,7 @@ module.exports = {
PureComponent: ['react', 'PureComponent'],
connect: ['react-redux', 'connect'],
PropTypes: 'prop-types',
_s: '/Users/m3/Documents/dev/gab-social/app/javascript/styles/global.css', // : todo :
_s: `${resolve(settings.source_path)}/styles/global.css`
}),
new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new webpack.NormalModuleReplacementPlugin(