2020-03-14 17:31:29 +00:00
import Immutable from 'immutable'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages , injectIntl , FormattedMessage } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { HotKeys } from 'react-hotkeys'
import { fetchStatus } from '../../actions/statuses'
2019-08-09 17:06:27 +01:00
import {
2020-03-04 22:26:01 +00:00
favorite ,
unfavorite ,
2020-03-08 23:02:28 +00:00
repost ,
unrepost ,
2019-08-09 17:06:27 +01:00
pin ,
unpin ,
2020-03-14 17:31:29 +00:00
} from '../../actions/interactions'
2019-08-09 17:06:27 +01:00
import {
replyCompose ,
mentionCompose ,
2020-03-14 17:31:29 +00:00
} from '../../actions/compose'
import { blockAccount } from '../../actions/accounts'
2019-08-09 17:06:27 +01:00
import {
muteStatus ,
unmuteStatus ,
deleteStatus ,
hideStatus ,
revealStatus ,
2020-03-14 17:31:29 +00:00
} from '../../actions/statuses'
import { initMuteModal } from '../../actions/mutes'
import { initReport } from '../../actions/reports'
import { openModal } from '../../actions/modal'
import { boostModal , deleteModal , me } from '../../initial_state'
import { makeGetStatus } from '../../selectors'
import StatusContainer from '../../containers/status_container'
import { textForScreenReader , defaultMediaVisibility } from '../../components/status/status'
import ColumnIndicator from '../../components/column_indicator'
2020-03-08 23:02:28 +00:00
import Block from '../../components/block'
import Comment from '../../components/comment'
2019-08-09 17:06:27 +01:00
const messages = defineMessages ( {
deleteConfirm : { id : 'confirmations.delete.confirm' , defaultMessage : 'Delete' } ,
deleteMessage : { id : 'confirmations.delete.message' , defaultMessage : 'Are you sure you want to delete this status?' } ,
redraftConfirm : { id : 'confirmations.redraft.confirm' , defaultMessage : 'Delete & redraft' } ,
redraftMessage : { id : 'confirmations.redraft.message' , defaultMessage : 'Are you sure you want to delete this status and re-draft it? Favorites and reposts will be lost, and replies to the original post will be orphaned.' } ,
blockConfirm : { id : 'confirmations.block.confirm' , defaultMessage : 'Block' } ,
revealAll : { id : 'status.show_more_all' , defaultMessage : 'Show more for all' } ,
hideAll : { id : 'status.show_less_all' , defaultMessage : 'Show less for all' } ,
detailedStatus : { id : 'status.detailed_status' , defaultMessage : 'Detailed conversation view' } ,
replyConfirm : { id : 'confirmations.reply.confirm' , defaultMessage : 'Reply' } ,
replyMessage : { id : 'confirmations.reply.message' , defaultMessage : 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' } ,
blockAndReport : { id : 'confirmations.block.block_and_report' , defaultMessage : 'Block & Report' } ,
} ) ;
const makeMapStateToProps = ( ) => {
2020-03-08 23:02:28 +00:00
const getStatus = makeGetStatus ( )
2019-08-09 17:06:27 +01:00
const mapStateToProps = ( state , props ) => {
const status = getStatus ( state , {
id : props . params . statusId ,
username : props . params . username ,
2020-03-08 23:02:28 +00:00
} )
2019-08-09 17:06:27 +01:00
2020-03-08 23:02:28 +00:00
let ancestorsIds = Immutable . List ( )
2019-08-09 17:06:27 +01:00
let descendantsIds = Immutable . List ( ) ;
if ( status ) {
ancestorsIds = ancestorsIds . withMutations ( mutable => {
let id = status . get ( 'in_reply_to_id' ) ;
while ( id ) {
mutable . unshift ( id ) ;
id = state . getIn ( [ 'contexts' , 'inReplyTos' , id ] ) ;
}
} ) ;
2020-03-08 23:02:28 +00:00
// ONLY Direct descendants
descendantsIds = state . getIn ( [ 'contexts' , 'replies' , status . get ( 'id' ) ] )
2020-03-11 23:56:18 +00:00
console . log ( "descendantsIds:" , descendantsIds )
2019-08-09 17:06:27 +01:00
}
return {
status ,
ancestorsIds ,
descendantsIds ,
askReplyConfirmation : state . getIn ( [ 'compose' , 'text' ] ) . trim ( ) . length !== 0 ,
domain : state . getIn ( [ 'meta' , 'domain' ] ) ,
} ;
} ;
return mapStateToProps ;
} ;
2020-02-25 16:04:44 +00:00
export default
@ injectIntl
2019-08-09 17:06:27 +01:00
@ connect ( makeMapStateToProps )
class Status extends ImmutablePureComponent {
static contextTypes = {
router : PropTypes . object ,
} ;
static propTypes = {
params : PropTypes . object . isRequired ,
dispatch : PropTypes . func . isRequired ,
status : ImmutablePropTypes . map ,
ancestorsIds : ImmutablePropTypes . list ,
descendantsIds : ImmutablePropTypes . list ,
intl : PropTypes . object . isRequired ,
askReplyConfirmation : PropTypes . bool ,
domain : PropTypes . string . isRequired ,
} ;
state = {
fullscreen : false ,
showMedia : defaultMediaVisibility ( this . props . status ) ,
loadedStatusId : undefined ,
} ;
2020-02-24 23:25:55 +00:00
componentWillMount ( ) {
2019-08-09 17:06:27 +01:00
this . props . dispatch ( fetchStatus ( this . props . params . statusId ) ) ;
}
2020-02-24 23:25:55 +00:00
componentWillReceiveProps ( nextProps ) {
2019-08-09 17:06:27 +01:00
if ( nextProps . params . statusId !== this . props . params . statusId && nextProps . params . statusId ) {
this . _scrolledIntoView = false ;
this . props . dispatch ( fetchStatus ( nextProps . params . statusId ) ) ;
}
if ( nextProps . status && nextProps . status . get ( 'id' ) !== this . state . loadedStatusId ) {
this . setState ( { showMedia : defaultMediaVisibility ( nextProps . status ) , loadedStatusId : nextProps . status . get ( 'id' ) } ) ;
}
}
handleToggleMediaVisibility = ( ) => {
this . setState ( { showMedia : ! this . state . showMedia } ) ;
}
2020-03-04 22:26:01 +00:00
handleFavoriteClick = ( status ) => {
2019-08-09 17:06:27 +01:00
if ( status . get ( 'favourited' ) ) {
2020-03-04 22:26:01 +00:00
this . props . dispatch ( unfavorite ( status ) ) ;
2019-08-09 17:06:27 +01:00
} else {
2020-03-04 22:26:01 +00:00
this . props . dispatch ( favorite ( status ) ) ;
2019-08-09 17:06:27 +01:00
}
}
handlePin = ( status ) => {
if ( status . get ( 'pinned' ) ) {
this . props . dispatch ( unpin ( status ) ) ;
} else {
this . props . dispatch ( pin ( status ) ) ;
}
}
handleReplyClick = ( status ) => {
let { askReplyConfirmation , dispatch , intl } = this . props ;
if ( askReplyConfirmation ) {
dispatch ( openModal ( 'CONFIRM' , {
message : intl . formatMessage ( messages . replyMessage ) ,
confirm : intl . formatMessage ( messages . replyConfirm ) ,
onConfirm : ( ) => dispatch ( replyCompose ( status , this . context . router . history ) ) ,
} ) ) ;
} else {
dispatch ( replyCompose ( status , this . context . router . history ) ) ;
}
}
2020-03-04 22:26:01 +00:00
handleModalRepost = ( status ) => {
this . props . dispatch ( repost ( status ) ) ;
2019-08-09 17:06:27 +01:00
}
2020-03-04 22:26:01 +00:00
handleRepostClick = ( status , e ) => {
2019-08-09 17:06:27 +01:00
if ( status . get ( 'reblogged' ) ) {
2020-03-04 22:26:01 +00:00
this . props . dispatch ( unrepost ( status ) ) ;
2019-08-09 17:06:27 +01:00
} else {
if ( ( e && e . shiftKey ) || ! boostModal ) {
2020-03-04 22:26:01 +00:00
this . handleModalRepost ( status ) ;
2019-08-09 17:06:27 +01:00
} else {
2020-03-04 22:26:01 +00:00
this . props . dispatch ( openModal ( 'BOOST' , { status , onRepost : this . handleModalRepost } ) ) ;
2019-08-09 17:06:27 +01:00
}
}
}
handleDeleteClick = ( status , history , withRedraft = false ) => {
const { dispatch , intl } = this . props ;
if ( ! deleteModal ) {
dispatch ( deleteStatus ( status . get ( 'id' ) , history , withRedraft ) ) ;
} else {
dispatch ( openModal ( 'CONFIRM' , {
message : intl . formatMessage ( withRedraft ? messages . redraftMessage : messages . deleteMessage ) ,
confirm : intl . formatMessage ( withRedraft ? messages . redraftConfirm : messages . deleteConfirm ) ,
onConfirm : ( ) => dispatch ( deleteStatus ( status . get ( 'id' ) , history , withRedraft ) ) ,
} ) ) ;
}
}
handleMentionClick = ( account , router ) => {
this . props . dispatch ( mentionCompose ( account , router ) ) ;
}
handleOpenMedia = ( media , index ) => {
this . props . dispatch ( openModal ( 'MEDIA' , { media , index } ) ) ;
}
handleOpenVideo = ( media , time ) => {
this . props . dispatch ( openModal ( 'VIDEO' , { media , time } ) ) ;
}
handleMuteClick = ( account ) => {
this . props . dispatch ( initMuteModal ( account ) ) ;
}
handleConversationMuteClick = ( status ) => {
if ( status . get ( 'muted' ) ) {
this . props . dispatch ( unmuteStatus ( status . get ( 'id' ) ) ) ;
} else {
this . props . dispatch ( muteStatus ( status . get ( 'id' ) ) ) ;
}
}
handleToggleHidden = ( status ) => {
if ( status . get ( 'hidden' ) ) {
this . props . dispatch ( revealStatus ( status . get ( 'id' ) ) ) ;
} else {
this . props . dispatch ( hideStatus ( status . get ( 'id' ) ) ) ;
}
}
handleToggleAll = ( ) => {
const { status , ancestorsIds , descendantsIds } = this . props ;
const statusIds = [ status . get ( 'id' ) ] . concat ( ancestorsIds . toJS ( ) , descendantsIds . toJS ( ) ) ;
if ( status . get ( 'hidden' ) ) {
this . props . dispatch ( revealStatus ( statusIds ) ) ;
} else {
this . props . dispatch ( hideStatus ( statusIds ) ) ;
}
}
handleBlockClick = ( status ) => {
const { dispatch , intl } = this . props ;
const account = status . get ( 'account' ) ;
dispatch ( openModal ( 'CONFIRM' , {
message : < FormattedMessage id = 'confirmations.block.message' defaultMessage = 'Are you sure you want to block {name}?' values = { { name : < strong > @ { account . get ( 'acct' ) } < /strong> }} / > ,
confirm : intl . formatMessage ( messages . blockConfirm ) ,
onConfirm : ( ) => dispatch ( blockAccount ( account . get ( 'id' ) ) ) ,
secondary : intl . formatMessage ( messages . blockAndReport ) ,
onSecondary : ( ) => {
dispatch ( blockAccount ( account . get ( 'id' ) ) ) ;
dispatch ( initReport ( account , status ) ) ;
} ,
} ) ) ;
}
handleReport = ( status ) => {
this . props . dispatch ( initReport ( status . get ( 'account' ) , status ) ) ;
}
handleEmbed = ( status ) => {
this . props . dispatch ( openModal ( 'EMBED' , { url : status . get ( 'url' ) } ) ) ;
}
handleHotkeyMoveUp = ( ) => {
this . handleMoveUp ( this . props . status . get ( 'id' ) ) ;
}
handleHotkeyMoveDown = ( ) => {
this . handleMoveDown ( this . props . status . get ( 'id' ) ) ;
}
handleHotkeyReply = e => {
e . preventDefault ( ) ;
this . handleReplyClick ( this . props . status ) ;
}
2020-03-04 22:26:01 +00:00
handleHotkeyFavorite = ( ) => {
this . handleFavoriteClick ( this . props . status ) ;
2019-08-09 17:06:27 +01:00
}
handleHotkeyBoost = ( ) => {
2020-03-04 22:26:01 +00:00
this . handleRepostClick ( this . props . status ) ;
2019-08-09 17:06:27 +01:00
}
handleHotkeyMention = e => {
e . preventDefault ( ) ;
this . handleMentionClick ( this . props . status . get ( 'account' ) ) ;
}
handleHotkeyOpenProfile = ( ) => {
this . context . router . history . push ( ` / ${ this . props . status . getIn ( [ 'account' , 'acct' ] ) } ` ) ;
}
handleHotkeyToggleHidden = ( ) => {
this . handleToggleHidden ( this . props . status ) ;
}
handleHotkeyToggleSensitive = ( ) => {
this . handleToggleMediaVisibility ( ) ;
}
handleMoveUp = id => {
const { status , ancestorsIds , descendantsIds } = this . props ;
if ( id === status . get ( 'id' ) ) {
this . _selectChild ( ancestorsIds . size - 1 , true ) ;
} else {
let index = ancestorsIds . indexOf ( id ) ;
if ( index === - 1 ) {
index = descendantsIds . indexOf ( id ) ;
this . _selectChild ( ancestorsIds . size + index , true ) ;
} else {
this . _selectChild ( index - 1 , true ) ;
}
}
}
handleMoveDown = id => {
const { status , ancestorsIds , descendantsIds } = this . props ;
if ( id === status . get ( 'id' ) ) {
this . _selectChild ( ancestorsIds . size + 1 , false ) ;
} else {
let index = ancestorsIds . indexOf ( id ) ;
if ( index === - 1 ) {
index = descendantsIds . indexOf ( id ) ;
this . _selectChild ( ancestorsIds . size + index + 2 , false ) ;
} else {
this . _selectChild ( index + 1 , false ) ;
}
}
}
2020-02-24 23:25:55 +00:00
_selectChild ( index , align _top ) {
2019-08-09 17:06:27 +01:00
const container = this . node ;
const element = container . querySelectorAll ( '.focusable' ) [ index ] ;
if ( element ) {
if ( align _top && container . scrollTop > element . offsetTop ) {
element . scrollIntoView ( true ) ;
} else if ( ! align _top && container . scrollTop + container . clientHeight < element . offsetTop + element . offsetHeight ) {
element . scrollIntoView ( false ) ;
}
element . focus ( ) ;
}
}
2020-02-24 23:25:55 +00:00
renderChildren ( list ) {
2020-03-08 23:02:28 +00:00
console . log ( "list:" , list )
2020-03-07 04:53:28 +00:00
// : todo : comments
2019-08-09 17:06:27 +01:00
return list . map ( id => (
2020-03-08 23:02:28 +00:00
< Comment
key = { ` comment- ${ id } ` }
2019-08-09 17:06:27 +01:00
id = { id }
onMoveUp = { this . handleMoveUp }
onMoveDown = { this . handleMoveDown }
/ >
2020-03-08 23:02:28 +00:00
) )
2019-08-09 17:06:27 +01:00
}
setRef = c => {
this . node = c ;
}
2020-02-24 23:25:55 +00:00
componentDidUpdate ( ) {
2020-03-08 23:02:28 +00:00
if ( this . _scrolledIntoView ) return
2019-08-09 17:06:27 +01:00
2020-03-08 23:02:28 +00:00
const { status , ancestorsIds } = this . props
2019-08-09 17:06:27 +01:00
if ( status && ancestorsIds && ancestorsIds . size > 0 ) {
const element = this . node . querySelectorAll ( '.focusable' ) [ ancestorsIds . size - 1 ] ;
window . requestAnimationFrame ( ( ) => {
element . scrollIntoView ( true ) ;
} ) ;
this . _scrolledIntoView = true ;
}
}
2020-02-24 23:25:55 +00:00
render ( ) {
2020-03-08 23:02:28 +00:00
const {
status ,
ancestorsIds ,
descendantsIds ,
intl ,
domain
} = this . props
let ancestors , descendants
2019-08-09 17:06:27 +01:00
if ( status === null ) {
2020-03-08 23:02:28 +00:00
return < ColumnIndicator type = 'loading' / >
2019-08-09 17:06:27 +01:00
}
if ( ancestorsIds && ancestorsIds . size > 0 ) {
2020-03-08 23:02:28 +00:00
ancestors = this . renderChildren ( ancestorsIds )
2019-08-09 17:06:27 +01:00
}
if ( descendantsIds && descendantsIds . size > 0 ) {
2020-03-08 23:02:28 +00:00
descendants = this . renderChildren ( descendantsIds )
2019-08-09 17:06:27 +01:00
}
const handlers = {
moveUp : this . handleHotkeyMoveUp ,
moveDown : this . handleHotkeyMoveDown ,
reply : this . handleHotkeyReply ,
2020-03-04 22:26:01 +00:00
favorite : this . handleHotkeyFavorite ,
2019-08-09 17:06:27 +01:00
boost : this . handleHotkeyBoost ,
mention : this . handleHotkeyMention ,
openProfile : this . handleHotkeyOpenProfile ,
toggleHidden : this . handleHotkeyToggleHidden ,
toggleSensitive : this . handleHotkeyToggleSensitive ,
} ;
return (
2020-02-24 23:25:55 +00:00
< div ref = { this . setRef } >
2020-03-08 23:02:28 +00:00
< Block >
{
/* ancestors */
}
< 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}
/ >
< / d i v >
< / H o t K e y s >
{
descendantsIds && descendantsIds . size > 0 &&
2020-03-11 23:56:18 +00:00
< div className = { [ _s . default , _s . mr10 , _s . ml10 , _s . mb10 , _s . borderColorSecondary , _s . borderBottom1PX ] . join ( ' ' ) } / >
2020-03-08 23:02:28 +00:00
}
{ descendants }
< / B l o c k >
2020-02-24 23:25:55 +00:00
< / d i v >
2020-03-08 23:02:28 +00:00
)
2019-08-09 17:06:27 +01:00
}
}