comments yo
This commit is contained in:
mgabdev 2020-05-05 01:16:01 -04:00
parent 498f163880
commit 01bb440385
19 changed files with 234 additions and 73 deletions

View File

@ -167,7 +167,6 @@ export function handleComposeSubmit(dispatch, getState, response, status) {
};
if (response.data.visibility === 'public') {
// console.log("response.data.in_reply_to_id:", response.data.in_reply_to_id)
insertIfOnline('home');
insertIfOnline('community');
insertIfOnline('public');
@ -184,8 +183,12 @@ export function submitCompose(group, replyToId = null) {
// : hack :
//Prepend http:// to urls in status that don't have protocol
status = status.replace(urlRegex, (match) =>{
status = status.replace(urlRegex, (match, a, b, c) =>{
const hasProtocol = match.startsWith('https://') || match.startsWith('http://')
//Make sure not a remote mention like @someone@somewhere.com
if (!hasProtocol) {
if (status.indexOf(`@${match}`) > -1) return match
}
return hasProtocol ? match : `http://${match}`
})
// markdown = statusMarkdown.replace(urlRegex, (match) =>{
@ -195,7 +198,7 @@ export function submitCompose(group, replyToId = null) {
const inReplyToId = getState().getIn(['compose', 'in_reply_to'], null) || replyToId
console.log("markdown:", markdown)
// console.log("markdown:", markdown)
dispatch(submitComposeRequest());
dispatch(closeModal());

View File

@ -7,8 +7,6 @@ export const GAB_TRENDS_RESULTS_FETCH_FAIL = 'GAB_TRENDS_RESULTS_FETCH_FAIL'
export const fetchGabTrends = () => {
return function (dispatch, getState) {
if (!me) return
dispatch(fetchGabTrendsRequest())
api(getState).get('/api/v1/gab_trends').then(response => {

View File

@ -0,0 +1,26 @@
const MicIcon = ({
className = '',
size = '16px',
title = 'Mic',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={size}
height={size}
viewBox='0 0 24 24'
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 17.453125 0 C 16.125 0 14.839844 0.398438 13.746094 1.15625 C 13.613281 1.246094 13.527344 1.390625 13.511719 1.554688 C 13.496094 1.714844 13.554688 1.875 13.667969 1.988281 L 22.011719 10.332031 C 22.113281 10.433594 22.253906 10.492188 22.398438 10.492188 C 22.414062 10.492188 22.429688 10.488281 22.445312 10.488281 C 22.609375 10.472656 22.753906 10.386719 22.847656 10.253906 C 23.601562 9.15625 24 7.875 24 6.546875 C 24 2.9375 21.0625 0 17.453125 0 Z M 17.453125 0' />
<path d='M 12.898438 2.761719 C 12.785156 2.644531 12.636719 2.589844 12.460938 2.601562 C 12.300781 2.617188 12.15625 2.703125 12.0625 2.835938 C 11.308594 3.933594 10.910156 5.214844 10.910156 6.546875 C 10.910156 10.15625 13.84375 13.089844 17.453125 13.089844 C 18.785156 13.089844 20.066406 12.691406 21.164062 11.9375 C 21.296875 11.84375 21.382812 11.699219 21.398438 11.535156 C 21.414062 11.375 21.355469 11.214844 21.238281 11.101562 Z M 12.898438 2.761719' />
<path d='M 14.785156 13.691406 C 12.714844 12.914062 11.085938 11.285156 10.308594 9.214844 C 10.242188 9.03125 10.078125 8.898438 9.886719 8.867188 C 9.695312 8.835938 9.496094 8.910156 9.375 9.0625 L 0.363281 20.246094 C 0.332031 20.285156 0.308594 20.328125 0.289062 20.375 C 0.0976562 20.648438 0 20.957031 0 21.273438 C 0 21.710938 0.171875 22.121094 0.480469 22.429688 L 1.570312 23.519531 C 1.878906 23.828125 2.289062 24 2.726562 24 C 3.042969 24 3.347656 23.902344 3.625 23.714844 C 3.671875 23.695312 3.714844 23.667969 3.753906 23.636719 L 14.933594 14.625 C 15.085938 14.503906 15.164062 14.308594 15.132812 14.113281 C 15.101562 13.921875 14.96875 13.757812 14.785156 13.691406 Z M 10.75 14.023438 L 9.660156 15.113281 C 9.550781 15.21875 9.414062 15.273438 9.273438 15.273438 C 9.132812 15.273438 8.992188 15.21875 8.886719 15.113281 C 8.675781 14.898438 8.675781 14.554688 8.886719 14.339844 L 9.976562 13.25 C 10.191406 13.039062 10.535156 13.039062 10.75 13.25 C 10.960938 13.464844 10.960938 13.808594 10.75 14.023438 Z M 10.75 14.023438' />
</g>
</svg>
)
export default MicIcon

View File

@ -10,6 +10,9 @@ import { replyCompose } from '../actions/compose'
import { openModal } from '../actions/modal'
import { openPopover } from '../actions/popover'
import { makeGetStatus } from '../selectors'
import {
CX,
} from '../constants'
import { me } from '../initial_state'
import Avatar from './avatar'
import Button from './button'
@ -74,9 +77,11 @@ class Comment extends ImmutablePureComponent {
static propTypes = {
indent: PropTypes.number,
intl: PropTypes.object.isRequired,
ancestorAccountId: PropTypes.string.isRequired,
status: ImmutablePropTypes.map.isRequired,
isHidden: PropTypes.bool,
isIntersecting: PropTypes.bool,
isHighlighted: PropTypes.bool,
onReply: PropTypes.func.isRequired,
onFavorite: PropTypes.func.isRequired,
onOpenStatusOptions: PropTypes.func.isRequired,
@ -89,6 +94,7 @@ class Comment extends ImmutablePureComponent {
'indent',
'isHidden',
'isIntersecting',
'isHighlighted',
]
state = {
@ -115,6 +121,8 @@ class Comment extends ImmutablePureComponent {
intl,
status,
isHidden,
isHighlighted,
ancestorAccountId,
} = this.props
if (isHidden) {
@ -130,6 +138,16 @@ class Comment extends ImmutablePureComponent {
paddingLeft: `${indent * 42}px`,
}
const contentClasses = CX({
default: 1,
px10: 1,
pt5: 1,
pb10: 1,
radiusSmall: 1,
bgSubtle: !isHighlighted,
highlightedComment: isHighlighted,
})
return (
<div className={[_s.default, _s.px15, _s.mb10, _s.py5].join(' ')} data-comment={status.get('id')}>
<div className={[_s.default].join(' ')} style={style}>
@ -144,8 +162,9 @@ class Comment extends ImmutablePureComponent {
</NavLink>
<div className={[_s.default, _s.flexShrink1, _s.maxWidth100PC42PX].join(' ')}>
<div className={[_s.default, _s.px10, _s.pt5, _s.pb10, _s.radiusSmall, _s.bgSubtle].join(' ')}>
<div className={contentClasses}>
<CommentHeader
ancestorAccountId={ancestorAccountId}
status={status}
onOpenLikes={this.props.onOpenLikes}
onOpenReposts={this.props.onOpenReposts}

View File

@ -6,6 +6,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import Button from './button'
import DisplayName from './display_name'
import DotTextSeperator from './dot_text_seperator'
import Icon from './icon'
import RelativeTimestamp from './relative_timestamp'
import Text from './text'
@ -21,6 +22,7 @@ class CommentHeader extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
ancestorAccountId: PropTypes.string.isRequired,
status: ImmutablePropTypes.map.isRequired,
openLikesList: PropTypes.func.isRequired,
openRepostsList: PropTypes.func.isRequired,
@ -38,12 +40,16 @@ class CommentHeader extends ImmutablePureComponent {
const {
intl,
status,
ancestorAccountId,
} = this.props
if (!status) return null
const repostCount = status.get('reblogs_count')
const favoriteCount = status.get('favourites_count')
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
const isOwner = ancestorAccountId === status.getIn(['account', 'id'])
return (
<div className={[_s.default, _s.alignItemsStart, _s.py2, _s.flexGrow1].join(' ')}>
@ -57,6 +63,16 @@ class CommentHeader extends ImmutablePureComponent {
<DisplayName account={status.get('account')} isSmall />
</NavLink>
{
isOwner &&
<div className={[_s.default, _s.alignItemsCenter, _s.ml5].join(' ')}>
<span className={_s.visiblyHidden}>
Original Gabber
</span>
<Icon id='mic' size='10px' className={_s.fillBrand} />
</div>
}
{
status.get('revised_at') !== null &&
<Fragment>

View File

@ -12,21 +12,21 @@ export default class CommentList extends ImmutablePureComponent {
commentsLimited: PropTypes.bool,
descendants: ImmutablePropTypes.list,
onViewComments: PropTypes.func.isRequired,
ancestorAccountId: PropTypes.string.isRequired,
}
render() {
const {
descendants,
commentsLimited,
onViewComments
onViewComments,
ancestorAccountId
} = this.props
const size = descendants.size
const upperLimit = commentsLimited ? 6 : size
const max = Math.min(commentsLimited ? 2 : upperLimit, size)
console.log("size, max:", size, max)
const Wrapper = !commentsLimited ? ScrollableList : DummyContainer
return (
@ -37,7 +37,9 @@ export default class CommentList extends ImmutablePureComponent {
<Comment
key={`comment-${descendant.get('statusId')}-${i}`}
id={descendant.get('statusId')}
ancestorAccountId={ancestorAccountId}
indent={descendant.get('indent')}
isHighlighted={descendant.get('isHighlighted')}
/>
))
}

View File

@ -144,7 +144,7 @@ class DisplayName extends ImmutablePureComponent {
// }
// : todo : remote
console.log("domain, isRemoteUser:", domain, isRemoteUser)
// console.log("domain, isRemoteUser:", domain, isRemoteUser)
return (
<div

View File

@ -40,6 +40,7 @@ import LoadingIcon from '../assets/loading_icon'
import LockIcon from '../assets/lock_icon'
import LockFilledIcon from '../assets/lock_filled_icon'
import MediaIcon from '../assets/media_icon'
import MicIcon from '../assets/mic_icon'
import MinimizeFullscreenIcon from '../assets/minimize_fullscreen_icon'
import MissingIcon from '../assets/missing_icon'
import MoreIcon from '../assets/more_icon'
@ -110,6 +111,7 @@ const ICONS = {
'lock': LockIcon,
'lock-filled': LockFilledIcon,
'media': MediaIcon,
'mic': MicIcon,
'minimize-fullscreen': MinimizeFullscreenIcon,
'missing': MissingIcon,
'more': MoreIcon,

View File

@ -55,6 +55,7 @@ class BoostModal extends ImmutablePureComponent {
<div className={[_s.default, _s.px15, _s.py10, _s.mt5].join(' ')}>
<StatusContainer
contextType='boost-modal'
id={status.get('id')}
isChild
/>

View File

@ -144,6 +144,7 @@ class Notification extends ImmutablePureComponent {
!!statusId &&
<div className={[_s.default, _s.pt10, _s.mt5].join(' ')}>
<StatusContainer
contextType='notification'
id={statusId}
isChild
/>

View File

@ -146,6 +146,10 @@ class ProfileHeader extends ImmutablePureComponent {
/>
</div>
}
{
headerMissing &&
<div className={[_s.default, _s.py10, _s.width100PC].join(' ')} />
}
<div className={[_s.default, _s.width100PC].join(' ')}>

View File

@ -69,6 +69,7 @@ class Status extends ImmutablePureComponent {
isMuted: PropTypes.bool,
isHidden: PropTypes.bool,
isIntersecting: PropTypes.bool,
isComment: PropTypes.bool,
onClick: PropTypes.func,
onReply: PropTypes.func,
onRepost: PropTypes.func,
@ -82,6 +83,7 @@ class Status extends ImmutablePureComponent {
onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func,
onFetchComments: PropTypes.func,
onFetchContext: PropTypes.func,
getScrollPosition: PropTypes.func,
updateScrollBottom: PropTypes.func,
cacheMediaWidth: PropTypes.func,
@ -103,6 +105,7 @@ class Status extends ImmutablePureComponent {
'isMuted',
'isHidden',
'isIntersecting',
'isComment',
]
state = {
@ -127,6 +130,8 @@ class Status extends ImmutablePureComponent {
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.isChild) return null
if (!nextProps.isHidden && (nextProps.isIntersecting || !nextProps.commentsLimited) && !prevState.loadedComments) {
return {
loadedComments: true
@ -147,11 +152,14 @@ class Status extends ImmutablePureComponent {
// Compensate height changes
componentDidUpdate(prevProps, prevState, snapshot) {
// timeline lazy loading comments
if (!prevState.loadedComments && this.state.loadedComments && this.props.status) {
if (!prevState.loadedComments && this.state.loadedComments && this.props.status && !this.props.isChild) {
const commentCount = this.props.status.get('replies_count')
if (commentCount > 0) {
if (commentCount > 0 && !this.props.isComment) {
this.props.onFetchComments(this.props.status.get('id'))
this._measureHeight(prevState.height !== this.state.height)
// this._measureHeight(prevState.height !== this.state.height)
} else {
console.log("before fetch:", this.props.status)
this.props.onFetchContext(this.props.status.get('id'))
}
}
@ -311,7 +319,11 @@ class Status extends ImmutablePureComponent {
}
_properStatus() {
const { status } = this.props
const { status, ancestorStatus } = this.props
if (ancestorStatus) {
return ancestorStatus
}
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
return status.get('reblog')
@ -346,6 +358,8 @@ class Status extends ImmutablePureComponent {
isHidden,
descendantsIds,
commentsLimited,
ancestorStatus,
isComment,
} = this.props
let { status } = this.props
@ -354,13 +368,17 @@ class Status extends ImmutablePureComponent {
let reblogContent, rebloggedByText = null
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
rebloggedByText = intl.formatMessage(
{ id: 'status.reposted_by', defaultMessage: '{name} reposted' },
{ name: status.getIn(['account', 'acct']) }
)
reblogContent = status.get('contentHtml')
status = status.get('reblog')
if (ancestorStatus) {
status = ancestorStatus
} else {
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
rebloggedByText = intl.formatMessage(
{ id: 'status.reposted_by', defaultMessage: '{name} reposted' },
{ name: status.getIn(['account', 'acct']) }
)
reblogContent = status.get('contentHtml')
status = status.get('reblog')
}
}
const handlers = (this.props.isMuted || isChild) ? {} : {
@ -417,8 +435,6 @@ class Status extends ImmutablePureComponent {
bgSubtle_onHover: isChild,
})
console.log("status:", status)
return (
<HotKeys handlers={handlers}>
<div
@ -433,7 +449,12 @@ class Status extends ImmutablePureComponent {
<div data-id={status.get('id')}>
<StatusPrepend status={this.props.status} isPromoted={isPromoted} isFeatured={isFeatured} />
<StatusPrepend
status={this.props.status}
isPromoted={isPromoted}
isFeatured={isFeatured}
isComment={isComment}
/>
<StatusHeader status={status} reduced={isChild} />
@ -495,6 +516,7 @@ class Status extends ImmutablePureComponent {
{
descendantsIds && !compactMode && !isChild && descendantsIds.size > 0 &&
<CommentList
ancestorAccountId={status.getIn(['account', 'id'])}
commentsLimited={commentsLimited}
descendants={descendantsIds}
onViewComments={this.handleClick}

View File

@ -21,6 +21,7 @@ class StatusPrepend extends ImmutablePureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
status: ImmutablePropTypes.map,
isComment: PropTypes.bool,
isFeatured: PropTypes.bool,
isPromoted: PropTypes.bool,
}
@ -31,15 +32,22 @@ class StatusPrepend extends ImmutablePureComponent {
status,
isFeatured,
isPromoted,
isComment,
} = this.props
if (!status) return null
const isRepost = (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object')
if (!isFeatured && !isPromoted && !isRepost) return null
console.log("isComment:", isComment)
const iconId = isFeatured ? 'pin' : isPromoted ? 'star' : 'repost'
if (!isFeatured && !isPromoted && !isRepost && !isComment) return null
let iconId
if (isFeatured) iconId = 'pin'
else if (isPromoted) iconId = 'star'
else if (isRepost) iconId = 'repost'
else if (isComment) iconId = 'comment'
const containerClasses = CX({
default: 1,
@ -54,7 +62,7 @@ class StatusPrepend extends ImmutablePureComponent {
<div className={[_s.default, _s.width100PC, _s.flexRow, _s.alignItemsCenter, _s.py5, _s.px15].join(' ')}>
<Icon id={iconId} size='12px' className={[_s.fillSecondary, _s.mr5].join(' ')} />
{
isRepost &&
isRepost && !isComment &&
<div className={[_s.default, _s.flexRow].join(' ')}>
<Text size='small' color='secondary'>
<FormattedMessage
@ -79,11 +87,34 @@ class StatusPrepend extends ImmutablePureComponent {
</div>
}
{
!isRepost &&
!isRepost && !isComment &&
<Text color='secondary' size='small'>
{intl.formatMessage(isFeatured ? messages.pinned : messages.promoted)}
</Text>
}
{
isComment &&
<Text color='secondary' size='small'>
<FormattedMessage
id='status.commented_on_this'
defaultMessage='{name} commented on this gab'
values={{
name: (
<NavLink
className={[_s.noUnderline, _s.underline_onHover].join(' ')}
to={`/${status.getIn(['account', 'acct'])}`}
>
<Text size='small' color='secondary'>
<bdi>
<span dangerouslySetInnerHTML={{ __html: status.getIn(['account', 'display_name_html']) }} />
</bdi>
</Text>
</NavLink>
)
}}
/>
</Text>
}
</div>
</div>
)

View File

@ -20,7 +20,7 @@ import {
hideStatus,
revealStatus,
fetchComments,
fetchAncestors,
fetchContext,
} from '../actions/statuses';
import { initMuteModal } from '../actions/mutes';
import { initReport } from '../actions/reports';
@ -34,6 +34,40 @@ import {
import { makeGetStatus } from '../selectors';
import Status from '../components/status';
const getDescendants = (state, status, highlightStatusId) => {
let descendantsIds = ImmutableList()
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(ImmutableMap({
statusId: id,
indent: indent,
isHighlighted: highlightStatusId === id,
}))
}
if (replies) {
replies.reverse().forEach(reply => {
ids.unshift(reply)
});
indent++
indent = Math.min(2, indent)
} else {
indent = 0 // reset
}
}
})
return descendantsIds
}
const makeMapStateToProps = () => {
const getStatus = makeGetStatus()
@ -48,49 +82,41 @@ const makeMapStateToProps = () => {
let descendantsIds = ImmutableList()
let ancestorStatus
let fetchedContext = false
if (status && status.get('in_reply_to_account_id')) {
fetchedContext = true
if (status && status.get('replies_count') > 0) {
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(ImmutableMap({
statusId: id,
indent: indent,
}))
}
if (replies) {
replies.reverse().forEach(reply => {
ids.unshift(reply)
});
indent++
indent = Math.min(2, indent)
} else {
indent = 0 // reset
}
let inReplyTos = state.getIn(['contexts', 'inReplyTos'])
let ancestorsIds = ImmutableList()
ancestorsIds = ancestorsIds.withMutations(mutable => {
let id = statusId;
while (id) {
mutable.unshift(id)
id = inReplyTos.get(id)
}
})
}
if (status && status.get('in_reply_to_account_id')) {
// : todo :
// console.log("FIND ANCESTOR")
const ids = [status.get('id')]
const reps = state.getIn(['contexts', 'inReplyTos'])
const ancestorStatusId = ancestorsIds.get(0)
ancestorStatus = getStatus(state, {
id: ancestorStatusId,
})
descendantsIds = getDescendants(state, ancestorStatus, statusId)
}
// console.log("ancestorStatus:", ancestorStatus)
if (status && status.get('replies_count') > 0 && !fetchedContext) {
descendantsIds = getDescendants(state, status)
}
const isComment = !!status ? !!status.get('in_reply_to_id') : false
return {
status,
descendantsIds,
ancestorStatus,
descendantsIds,
isComment,
}
}
@ -262,8 +288,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(fetchComments(statusId))
},
onFetchAncestors(statusId) {
dispatch(fetchAncestors(statusId))
onFetchContext(statusId) {
dispatch(fetchContext(statusId))
},
onOpenLikes(status) {

View File

@ -296,9 +296,10 @@ class ComposeForm extends ImmutablePureComponent {
>
{
!!reduxReplyToId && isModalOpen &&
!!reduxReplyToId && isModalOpen && !shouldCondense &&
<div className={[_s.default, _s.px15, _s.py10, _s.mt5].join(' ')}>
<StatusContainer
contextType='compose'
id={reduxReplyToId}
isChild
/>
@ -369,6 +370,7 @@ class ComposeForm extends ImmutablePureComponent {
!!quoteOfId && isModalOpen &&
<div className={[_s.default, _s.px15, _s.py10, _s.mt5].join(' ')}>
<StatusContainer
contextType='compose'
id={quoteOfId}
isChild
/>

View File

@ -38,10 +38,7 @@ class Status extends ImmutablePureComponent {
render() {
const { status } = this.props
// : todo :
// - if comment render as such
if (!status) {
return <ColumnIndicator type='loading' />
}

View File

@ -78,13 +78,16 @@ export const makeGetStatus = () => {
],
(state, statusBase, quotedStatus, statusRepost, accountBase, accountQuoted, accountRepost, username, filters) => {
console.log("statusBase:", statusBase)
if (!statusBase) {
console.log("return null 1")
return null
}
const accountUsername = accountBase.get('acct');
//Must be owner of status if username exists
if (accountUsername !== username && username !== undefined) {
console.log("return null 2")
return null
}

View File

@ -152,7 +152,9 @@ body {
}
.statusContent a,
.dangerousContent a {
.dangerousContent a,
.statusContent a *,
.dangerousContent a * {
color: var(--color_brand);
text-decoration: none;
}
@ -792,6 +794,10 @@ body {
visibility: hidden;
}
.highlightedComment {
background-color: rgba(224, 234, 66, .3);
}
.searchInput::placeholder {
color: rgba(255,255,255,0.65);
opacity: 1;

View File

@ -9,6 +9,8 @@
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'error', integrity: true, crossorigin: 'anonymous'
%body.error
.dialog
.dialog__message
%div{ :style => "padding: 60px; max-width: 660px;" }
%a{ :href => '/' }
= image_pack_tag 'logo.svg', alt: 'Gab Social', width: '100px'
%div{ :style => "margin-top: 20px; line-height: 2rem; font-family: system-ui, -apple-system, BlinkMacSystemFont, Roboto, sans-serif;" }
%h1= yield :content