2020-03-03 22:45:16 -05:00
import ImmutablePropTypes from 'react-immutable-proptypes'
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'
2019-08-13 11:54:29 -04:00
import {
expandNotifications ,
scrollTopNotifications ,
dequeueNotifications ,
2020-03-03 22:45:16 -05:00
} from '../../actions/notifications'
import NotificationContainer from './containers/notification_container'
// import ColumnSettingsContainer from './containers/column_settings_container'
import ScrollableList from '../../components/scrollable_list'
import LoadMore from '../../components/load_more'
import TimelineQueueButtonHeader from '../../components/timeline_queue_button_header'
import Block from '../../components/block'
2019-08-13 11:54:29 -04:00
const getNotifications = createSelector ( [
state => state . getIn ( [ 'settings' , 'notifications' , 'quickFilter' , 'show' ] ) ,
state => state . getIn ( [ 'settings' , 'notifications' , 'quickFilter' , 'active' ] ) ,
state => ImmutableList ( state . getIn ( [ 'settings' , 'notifications' , 'shows' ] ) . filter ( item => ! item ) . keys ( ) ) ,
state => state . getIn ( [ 'notifications' , 'items' ] ) ,
] , ( showFilterBar , allowedType , excludedTypes , notifications ) => {
if ( ! showFilterBar || allowedType === 'all' ) {
// used if user changed the notification settings after loading the notifications from the server
// otherwise a list of notifications will come pre-filtered from the backend
// we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category
2020-03-03 22:45:16 -05:00
return notifications . filterNot ( item => item !== null && excludedTypes . includes ( item . get ( 'type' ) ) )
2019-08-13 11:54:29 -04:00
}
2020-03-03 22:45:16 -05:00
return notifications . filter ( item => item !== null && allowedType === item . get ( 'type' ) )
} )
2019-08-13 11:54:29 -04:00
const mapStateToProps = state => ( {
showFilterBar : state . getIn ( [ 'settings' , 'notifications' , 'quickFilter' , 'show' ] ) ,
notifications : getNotifications ( state ) ,
isLoading : state . getIn ( [ 'notifications' , 'isLoading' ] , true ) ,
isUnread : state . getIn ( [ 'notifications' , 'unread' ] ) > 0 ,
hasMore : state . getIn ( [ 'notifications' , 'hasMore' ] ) ,
totalQueuedNotificationsCount : state . getIn ( [ 'notifications' , 'totalQueuedNotificationsCount' ] , 0 ) ,
2020-03-03 22:45:16 -05:00
} )
2019-08-13 11:54:29 -04:00
2020-02-25 11:04:44 -05:00
export default
@ connect ( mapStateToProps )
2019-08-13 11:54:29 -04:00
@ injectIntl
class Notifications extends ImmutablePureComponent {
static propTypes = {
notifications : ImmutablePropTypes . list . isRequired ,
showFilterBar : PropTypes . bool . isRequired ,
dispatch : PropTypes . func . isRequired ,
intl : PropTypes . object . isRequired ,
isLoading : PropTypes . bool ,
isUnread : PropTypes . bool ,
hasMore : PropTypes . bool ,
dequeueNotifications : PropTypes . func ,
totalQueuedNotificationsCount : PropTypes . number ,
2020-03-03 22:45:16 -05:00
}
2019-08-13 11:54:29 -04:00
componentWillUnmount ( ) {
2020-03-03 22:45:16 -05:00
this . handleLoadOlder . cancel ( )
this . handleScrollToTop . cancel ( )
this . handleScroll . cancel ( )
this . props . dispatch ( scrollTopNotifications ( false ) )
2019-08-13 11:54:29 -04:00
}
componentDidMount ( ) {
2020-03-03 22:45:16 -05:00
this . handleDequeueNotifications ( )
this . props . dispatch ( scrollTopNotifications ( true ) )
2019-08-13 11:54:29 -04:00
}
handleLoadGap = ( maxId ) => {
2020-03-03 22:45:16 -05:00
this . props . dispatch ( expandNotifications ( { maxId } ) )
}
2019-08-13 11:54:29 -04:00
handleLoadOlder = debounce ( ( ) => {
2020-03-03 22:45:16 -05:00
const last = this . props . notifications . last ( )
this . props . dispatch ( expandNotifications ( { maxId : last && last . get ( 'id' ) } ) )
} , 300 , { leading : true } )
2019-08-13 11:54:29 -04:00
handleScrollToTop = debounce ( ( ) => {
2020-03-03 22:45:16 -05:00
this . props . dispatch ( scrollTopNotifications ( true ) )
} , 100 )
2019-08-13 11:54:29 -04:00
handleScroll = debounce ( ( ) => {
2020-03-03 22:45:16 -05:00
this . props . dispatch ( scrollTopNotifications ( false ) )
} , 100 )
2019-08-13 11:54:29 -04:00
setColumnRef = c => {
2020-03-03 22:45:16 -05:00
this . column = c
2019-08-13 11:54:29 -04:00
}
handleMoveUp = id => {
2020-03-03 22:45:16 -05:00
const elementIndex = this . props . notifications . findIndex ( item => item !== null && item . get ( 'id' ) === id ) - 1
this . _selectChild ( elementIndex , true )
2019-08-13 11:54:29 -04:00
}
handleMoveDown = id => {
2020-03-03 22:45:16 -05:00
const elementIndex = this . props . notifications . findIndex ( item => item !== null && item . get ( 'id' ) === id ) + 1
this . _selectChild ( elementIndex , false )
2019-08-13 11:54:29 -04:00
}
_selectChild ( index , align _top ) {
2020-03-03 22:45:16 -05:00
const container = this . column . node
const element = container . querySelector ( ` article:nth-of-type( ${ index + 1 } ) .focusable ` )
2019-08-13 11:54:29 -04:00
if ( element ) {
if ( align _top && container . scrollTop > element . offsetTop ) {
2020-03-03 22:45:16 -05:00
element . scrollIntoView ( true )
2019-08-13 11:54:29 -04:00
} else if ( ! align _top && container . scrollTop + container . clientHeight < element . offsetTop + element . offsetHeight ) {
2020-03-03 22:45:16 -05:00
element . scrollIntoView ( false )
2019-08-13 11:54:29 -04:00
}
2020-03-03 22:45:16 -05:00
element . focus ( )
2019-08-13 11:54:29 -04:00
}
}
handleDequeueNotifications = ( ) => {
2020-03-03 22:45:16 -05:00
this . props . dispatch ( dequeueNotifications ( ) )
}
2019-08-13 11:54:29 -04:00
render ( ) {
2020-03-03 22:45:16 -05:00
const {
intl ,
notifications ,
isLoading ,
isUnread ,
hasMore ,
showFilterBar ,
totalQueuedNotificationsCount
} = this . props
let scrollableContent = null
2019-08-13 11:54:29 -04:00
2020-02-22 18:26:23 -05:00
// : todo : include follow requests
2020-03-02 17:26:25 -05:00
console . log ( 'notifications:' , notifications )
let filteredNotifications = { follows : [ ] }
notifications . forEach ( ( notification ) => {
// const createdAt = notification.get('createdAt')
// const account = notification.get('account')
const type = notification . get ( 'type' )
const status = notification . get ( 'status' )
if ( type === 'follow' ) {
filteredNotifications . follows . push ( notification )
} else if ( type === 'favourite' ) {
if ( filteredNotifications [ status ] === undefined ) {
filteredNotifications [ status ] = { }
}
if ( filteredNotifications [ status ] [ 'favorite' ] === undefined ) {
filteredNotifications [ status ] . favorite = [ ]
}
filteredNotifications [ status ] . favorite . push ( notification )
} else if ( type === 'poll' ) {
if ( filteredNotifications [ status ] === undefined ) {
filteredNotifications [ status ] = { }
}
if ( filteredNotifications [ status ] [ 'poll' ] === undefined ) {
filteredNotifications [ status ] . poll = [ ]
}
filteredNotifications [ status ] . poll . push ( notification )
}
} )
console . log ( 'filteredNotifications:' , filteredNotifications )
2019-08-13 11:54:29 -04:00
if ( isLoading && this . scrollableContent ) {
2020-03-03 22:45:16 -05:00
scrollableContent = this . scrollableContent
2019-08-13 11:54:29 -04:00
} else if ( notifications . size > 0 || hasMore ) {
scrollableContent = notifications . map ( ( item , index ) => item === null ? (
< LoadMore
gap
key = { 'gap:' + notifications . getIn ( [ index + 1 , 'id' ] ) }
disabled = { isLoading }
maxId = { index > 0 ? notifications . getIn ( [ index - 1 , 'id' ] ) : null }
onClick = { this . handleLoadGap }
/ >
) : (
< NotificationContainer
2020-03-03 22:45:16 -05:00
key = { ` notification- ${ index } ` }
2019-08-13 11:54:29 -04:00
notification = { item }
onMoveUp = { this . handleMoveUp }
onMoveDown = { this . handleMoveDown }
/ >
2020-03-03 22:45:16 -05:00
) )
2019-08-13 11:54:29 -04:00
} else {
2020-03-03 22:45:16 -05:00
scrollableContent = null
2019-08-13 11:54:29 -04:00
}
2020-03-03 22:45:16 -05:00
this . scrollableContent = scrollableContent
2019-08-13 11:54:29 -04:00
return (
2020-02-24 18:25:55 -05:00
< div ref = { this . setColumnRef } >
2020-03-02 17:26:25 -05:00
< TimelineQueueButtonHeader
onClick = { this . handleDequeueNotifications }
count = { totalQueuedNotificationsCount }
itemType = 'notification'
/ >
2020-03-03 22:45:16 -05:00
< Block >
< ScrollableList
scrollKey = 'notifications'
isLoading = { isLoading }
showLoading = { isLoading && notifications . size === 0 }
hasMore = { hasMore }
emptyMessage = { < FormattedMessage id = 'empty_column.notifications' defaultMessage = "You don't have any notifications yet. Interact with others to start the conversation." / > }
onLoadMore = { this . handleLoadOlder }
onScrollToTop = { this . handleScrollToTop }
onScroll = { this . handleScroll }
>
{ scrollableContent }
< / S c r o l l a b l e L i s t >
< / B l o c k >
2020-02-24 18:25:55 -05:00
< / d i v >
2020-03-02 17:26:25 -05:00
)
2019-08-13 11:54:29 -04:00
}
}