@ -18,228 +18,79 @@ import {
UNFAVOURITE_SUCCESS ,
} from '../actions/interactions' ;
import {
ACCOUNT_TIMELINE_FETCH_REQUEST ,
ACCOUNT_TIMELINE_FETCH_SUCCESS ,
ACCOUNT_TIMELINE_FETCH_FAIL ,
ACCOUNT_TIMELINE_EXPAND_REQUEST ,
ACCOUNT_TIMELINE_EXPAND_SUCCESS ,
ACCOUNT_TIMELINE_EXPAND_FAIL ,
ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST ,
ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS ,
ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL ,
ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST ,
ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS ,
ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL ,
ACCOUNT_BLOCK_SUCCESS ,
ACCOUNT_MUTE_SUCCESS ,
} from '../actions/accounts' ;
import {
CONTEXT_FETCH_SUCCESS ,
} from '../actions/statuses' ;
import Immutable from 'immutable' ;
const initialState = Immutable . Map ( {
home : Immutable . Map ( {
path : ( ) => '/api/v1/timelines/home' ,
next : null ,
isLoading : false ,
online : false ,
loaded : false ,
top : true ,
unread : 0 ,
items : Immutable . List ( ) ,
} ) ,
public : Immutable . Map ( {
path : ( ) => '/api/v1/timelines/public' ,
next : null ,
isLoading : false ,
online : false ,
loaded : false ,
top : true ,
unread : 0 ,
items : Immutable . List ( ) ,
} ) ,
community : Immutable . Map ( {
path : ( ) => '/api/v1/timelines/public' ,
next : null ,
params : { local : true } ,
isLoading : false ,
online : false ,
loaded : false ,
top : true ,
unread : 0 ,
items : Immutable . List ( ) ,
} ) ,
const initialState = Immutable . Map ( ) ;
tag : Immutable . Map ( {
path : ( id ) => ` /api/v1/timelines/tag/ ${ id } ` ,
next : null ,
isLoading : false ,
id : null ,
loaded : false ,
top : true ,
unread : 0 ,
items : Immutable . List ( ) ,
} ) ,
accounts_timelines : Immutable . Map ( ) ,
accounts_media_timelines : Immutable . Map ( ) ,
ancestors : Immutable . Map ( ) ,
descendants : Immutable . Map ( ) ,
const initialTimeline = Immutable . Map ( {
unread : 0 ,
online : false ,
top : true ,
loaded : false ,
isLoading : false ,
next : false ,
items : Immutable . List ( ) ,
} ) ;
const normalizeStatus = ( state , status ) => {
return state ;
} ;
const normalizeTimeline = ( state , timeline , statuses , next ) => {
let ids = Immutable . List ( ) ;
const loaded = state . getIn ( [ timeline , 'loaded' ] ) ;
statuses . forEach ( ( status , i ) => {
state = normalizeStatus ( state , status ) ;
ids = ids . set ( i , status . get ( 'id' ) ) ;
} ) ;
state = state . setIn ( [ timeline , 'loaded' ] , true ) ;
state = state . setIn ( [ timeline , 'isLoading' ] , false ) ;
if ( state . getIn ( [ timeline , 'next' ] ) === null ) {
state = state . setIn ( [ timeline , 'next' ] , next ) ;
}
return state . updateIn ( [ timeline , 'items' ] , Immutable . List ( ) , list => ( loaded ? ids . concat ( list ) : ids ) ) ;
const ids = Immutable . List ( statuses . map ( status => status . get ( 'id' ) ) ) ;
const wasLoaded = state . getIn ( [ timeline , 'loaded' ] ) ;
const hadNext = state . getIn ( [ timeline , 'next' ] ) ;
const oldIds = state . getIn ( [ timeline , 'items' ] , Immutable . List ( ) ) ;
return state . update ( timeline , initialTimeline , map => map . withMutations ( mMap => {
mMap . set ( 'loaded' , true ) ;
mMap . set ( 'isLoading' , false ) ;
if ( ! hadNext ) mMap . set ( 'next' , next ) ;
mMap . set ( 'items' , wasLoaded ? ids . concat ( oldIds ) : ids ) ;
} ) ) ;
} ;
const appendNormalizedTimeline = ( state , timeline , statuses , next ) => {
let moreIds = Immutable . List ( ) ;
statuses . forEach ( ( status , i ) => {
state = normalizeStatus ( state , status ) ;
moreIds = moreIds . set ( i , status . get ( 'id' ) ) ;
} ) ;
state = state . setIn ( [ timeline , 'isLoading' ] , false ) ;
state = state . setIn ( [ timeline , 'next' ] , next ) ;
return state . updateIn ( [ timeline , 'items' ] , Immutable . List ( ) , list => list . concat ( moreIds ) ) ;
} ;
const normalizeAccountTimeline = ( state , accountId , statuses , replace , next ) => {
let ids = Immutable . List ( ) ;
statuses . forEach ( ( status , i ) => {
state = normalizeStatus ( state , status ) ;
ids = ids . set ( i , status . get ( 'id' ) ) ;
} ) ;
return state . updateIn ( [ 'accounts_timelines' , accountId ] , Immutable . Map ( ) , map => map
. set ( 'isLoading' , false )
. set ( 'loaded' , true )
. update ( 'next' , null , v => replace ? next : v )
. update ( 'items' , Immutable . List ( ) , list => ( replace ? ids : ids . concat ( list ) ) ) ) ;
} ;
const normalizeAccountMediaTimeline = ( state , accountId , statuses , replace , next ) => {
let ids = Immutable . List ( ) ;
statuses . forEach ( ( status , i ) => {
state = normalizeStatus ( state , status ) ;
ids = ids . set ( i , status . get ( 'id' ) ) ;
} ) ;
return state . updateIn ( [ 'accounts_media_timelines' , accountId ] , Immutable . Map ( ) , map => map
. set ( 'isLoading' , false )
. update ( 'next' , null , v => replace ? next : v )
. update ( 'items' , Immutable . List ( ) , list => ( replace ? ids : ids . concat ( list ) ) ) ) ;
} ;
const appendNormalizedAccountTimeline = ( state , accountId , statuses , next ) => {
let moreIds = Immutable . List ( [ ] ) ;
statuses . forEach ( ( status , i ) => {
state = normalizeStatus ( state , status ) ;
moreIds = moreIds . set ( i , status . get ( 'id' ) ) ;
} ) ;
return state . updateIn ( [ 'accounts_timelines' , accountId ] , Immutable . Map ( ) , map => map
. set ( 'isLoading' , false )
. set ( 'next' , next )
. update ( 'items' , list => list . concat ( moreIds ) ) ) ;
} ;
const appendNormalizedAccountMediaTimeline = ( state , accountId , statuses , next ) => {
let moreIds = Immutable . List ( [ ] ) ;
statuses . forEach ( ( status , i ) => {
state = normalizeStatus ( state , status ) ;
moreIds = moreIds . set ( i , status . get ( 'id' ) ) ;
} ) ;
return state . updateIn ( [ 'accounts_media_timelines' , accountId ] , Immutable . Map ( ) , map => map
. set ( 'isLoading' , false )
. set ( 'next' , next )
. update ( 'items' , list => list . concat ( moreIds ) ) ) ;
const ids = Immutable . List ( statuses . map ( status => status . get ( 'id' ) ) ) ;
const oldIds = state . getIn ( [ timeline , 'items' ] , Immutable . List ( ) ) ;
return state . update ( timeline , initialTimeline , map => map . withMutations ( mMap => {
mMap . set ( 'isLoading' , false ) ;
mMap . set ( 'next' , next ) ;
mMap . set ( 'items' , oldIds . concat ( ids ) ) ;
} ) ) ;
} ;
const updateTimeline = ( state , timeline , status , references ) => {
const top = state . getIn ( [ timeline , 'top' ] ) ;
const top = state . getIn ( [ timeline , 'top' ] ) ;
const ids = state . getIn ( [ timeline , 'items' ] , Immutable . List ( ) ) ;
const includesId = ids . includes ( status . get ( 'id' ) ) ;
const unread = state . getIn ( [ timeline , 'unread' ] , 0 ) ;
state = normalizeStatus ( state , status ) ;
if ( ! top ) {
state = state . updateIn ( [ timeline , 'unread' ] , unread => unread + 1 ) ;
if ( includesId ) {
return state ;
}
state = state . updateIn ( [ timeline , 'items' ] , Immutable . List ( ) , list => {
if ( top && list . size > 40 ) {
list = list . take ( 20 ) ;
}
if ( list . includes ( status . get ( 'id' ) ) ) {
return list ;
}
const reblogOfId = status . getIn ( [ 'reblog' , 'id' ] , null ) ;
let newIds = ids ;
if ( reblogOfId !== null ) {
list = list . filterNot ( itemId => references . includes ( itemId ) ) ;
}
return list . unshift ( status . get ( 'id' ) ) ;
} ) ;
return state ;
return state . update ( timeline , initialTimeline , map => map . withMutations ( mMap => {
if ( ! top ) mMap . set ( 'unread' , unread + 1 ) ;
if ( top && ids . size > 40 ) newIds = newIds . take ( 20 ) ;
if ( status . getIn ( [ 'reblog' , 'id' ] , null ) !== null ) newIds = newIds . filterNot ( item => references . includes ( item ) ) ;
mMap . set ( 'items' , newIds . unshift ( status . get ( 'id' ) ) ) ;
} ) ) ;
} ;
const deleteStatus = ( state , id , accountId , references , reblogOf ) => {
if ( reblogOf ) {
// If we are deleting a reblog, just replace reblog with its original
return state . updateIn ( [ 'home' , 'items' ] , list => list . map ( item => item === id ? reblogOf : item ) ) ;
}
// Remove references from timelines
[ 'home' , 'public' , 'community' , 'tag' ] . forEach ( function ( timeline ) {
state = state . updateIn ( [ timeline , 'items' ] , list => list . filterNot ( item => item === id ) ) ;
state . keySeq ( ) . forEach ( timeline => {
state = state . updateIn ( [ timeline , 'items' ] , list => {
if ( reblogOf && ! list . includes ( reblogOf ) ) {
return list . map ( item => item === id ? reblogOf : item ) ;
} else {
return list . filterNot ( item => item === id ) ;
}
} ) ;
} ) ;
// Remove references from account timelines
state = state . updateIn ( [ 'accounts_timelines' , accountId , 'items' ] , Immutable . List ( [ ] ) , list => list . filterNot ( item => item === id ) ) ;
state = state . updateIn ( [ 'accounts_media_timelines' , accountId , 'items' ] , Immutable . List ( [ ] ) , list => list . filterNot ( item => item === id ) ) ;
// Remove references from context
state . getIn ( [ 'descendants' , id ] , Immutable . List ( ) ) . forEach ( descendantId => {
state = state . updateIn ( [ 'ancestors' , descendantId ] , Immutable . List ( ) , list => list . filterNot ( itemId => itemId === id ) ) ;
} ) ;
state . getIn ( [ 'ancestors' , id ] , Immutable . List ( ) ) . forEach ( ancestorId => {
state = state . updateIn ( [ 'descendants' , ancestorId ] , Immutable . List ( ) , list => list . filterNot ( itemId => itemId === id ) ) ;
} ) ;
state = state . deleteIn ( [ 'descendants' , id ] ) . deleteIn ( [ 'ancestors' , id ] ) ;
// Remove reblogs of deleted status
references . forEach ( ref => {
state = deleteStatus ( state , ref [ 0 ] , ref [ 1 ] , [ ] ) ;
@ -257,54 +108,27 @@ const filterTimelines = (state, relationship, statuses) => {
}
references = statuses . filter ( item => item . get ( 'reblog' ) === status . get ( 'id' ) ) . map ( item => [ item . get ( 'id' ) , item . get ( 'account' ) ] ) ;
state = deleteStatus ( state , status . get ( 'id' ) , status . get ( 'account' ) , references ) ;
} ) ;
return state ;
} ;
const normalizeContext = ( state , id , ancestors , descendants ) => {
const ancestorsIds = ancestors . map ( ancestor => ancestor . get ( 'id' ) ) ;
const descendantsIds = descendants . map ( descendant => descendant . get ( 'id' ) ) ;
return state . withMutations ( map => {
map . setIn ( [ 'ancestors' , id ] , ancestorsIds ) ;
map . setIn ( [ 'descendants' , id ] , descendantsIds ) ;
state = deleteStatus ( state , status . get ( 'id' ) , status . get ( 'account' ) , references ) ;
} ) ;
} ;
const resetTimeline = ( state , timeline , id ) => {
if ( timeline === 'tag' && typeof id !== 'undefined' && state . getIn ( [ timeline , 'id' ] ) !== id ) {
state = state . update ( timeline , map => map
. set ( 'id' , id )
. set ( 'isLoading' , true )
. set ( 'loaded' , false )
. set ( 'next' , null )
. set ( 'top' , true )
. update ( 'items' , list => list . clear ( ) ) ) ;
} else {
state = state . setIn ( [ timeline , 'isLoading' ] , true ) ;
}
return state ;
} ;
const updateTop = ( state , timeline , top ) => {
if ( top ) {
state = state . setIn ( [ timeline , 'unread' ] , 0 ) ;
}
return state . setIn ( [ timeline , 'top' ] , top ) ;
return state . update ( timeline , initialTimeline , map => map . withMutations ( mMap => {
if ( top ) mMap . set ( 'unread' , 0 ) ;
mMap . set ( 'top' , top ) ;
} ) ) ;
} ;
export default function timelines ( state = initialState , action ) {
switch ( action . type ) {
case TIMELINE_REFRESH_REQUEST :
case TIMELINE_EXPAND_REQUEST :
return resetTimeline ( state , action . timeline , action . id ) ;
return state . update ( action . timeline , initialTimeline , map => map . set ( 'isLoading' , true ) ) ;
case TIMELINE_REFRESH_FAIL :
case TIMELINE_EXPAND_FAIL :
return state . setIn ( [ action . timeline , 'isLoading' ] , false ) ;
return state . update ( action . timeline , initialTimeline , map => map . set ( 'isLoading' , false ) ) ;
case TIMELINE_REFRESH_SUCCESS :
return normalizeTimeline ( state , action . timeline , Immutable . fromJS ( action . statuses ) , action . next ) ;
case TIMELINE_EXPAND_SUCCESS :
@ -313,37 +137,15 @@ export default function timelines(state = initialState, action) {
return updateTimeline ( state , action . timeline , Immutable . fromJS ( action . status ) , action . references ) ;
case TIMELINE_DELETE :
return deleteStatus ( state , action . id , action . accountId , action . references , action . reblogOf ) ;
case CONTEXT_FETCH_SUCCESS :
return normalizeContext ( state , action . id , Immutable . fromJS ( action . ancestors ) , Immutable . fromJS ( action . descendants ) ) ;
case ACCOUNT_TIMELINE_FETCH_REQUEST :
case ACCOUNT_TIMELINE_EXPAND_REQUEST :
return state . updateIn ( [ 'accounts_timelines' , action . id ] , Immutable . Map ( ) , map => map . set ( 'isLoading' , true ) ) ;
case ACCOUNT_TIMELINE_FETCH_FAIL :
case ACCOUNT_TIMELINE_EXPAND_FAIL :
return state . updateIn ( [ 'accounts_timelines' , action . id ] , Immutable . Map ( ) , map => map . set ( 'isLoading' , false ) ) ;
case ACCOUNT_TIMELINE_FETCH_SUCCESS :
return normalizeAccountTimeline ( state , action . id , Immutable . fromJS ( action . statuses ) , action . replace , action . next ) ;
case ACCOUNT_TIMELINE_EXPAND_SUCCESS :
return appendNormalizedAccountTimeline ( state , action . id , Immutable . fromJS ( action . statuses ) , action . next ) ;
case ACCOUNT_MEDIA_TIMELINE_FETCH_REQUEST :
case ACCOUNT_MEDIA_TIMELINE_EXPAND_REQUEST :
return state . updateIn ( [ 'accounts_media_timelines' , action . id ] , Immutable . Map ( ) , map => map . set ( 'isLoading' , true ) ) ;
case ACCOUNT_MEDIA_TIMELINE_FETCH_FAIL :
case ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL :
return state . updateIn ( [ 'accounts_media_timelines' , action . id ] , Immutable . Map ( ) , map => map . set ( 'isLoading' , false ) ) ;
case ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS :
return normalizeAccountMediaTimeline ( state , action . id , Immutable . fromJS ( action . statuses ) , action . replace , action . next ) ;
case ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS :
return appendNormalizedAccountMediaTimeline ( state , action . id , Immutable . fromJS ( action . statuses ) , action . next ) ;
case ACCOUNT_BLOCK_SUCCESS :
case ACCOUNT_MUTE_SUCCESS :
return filterTimelines ( state , action . relationship , action . statuses ) ;
case TIMELINE_SCROLL_TOP :
return updateTop ( state , action . timeline , action . top ) ;
case TIMELINE_CONNECT :
return state . setIn ( [ action . timeline , 'online' ] , true ) ;
return state . update ( action . timeline , initialTimeline , map => map . set ( 'online' , true ) ) ;
case TIMELINE_DISCONNECT :
return state . setIn ( [ action . timeline , 'online' ] , false ) ;
return state . update ( action . timeline , initialTimeline , map => map . set ( 'online' , false ) ) ;
default :
return state ;
}