@ -1,18 +1,18 @@
'use strict' ;
var async = require ( 'async' ) ;
var _ = require ( 'lodash' ) ;
var db = require ( '../database' ) ;
var user = require ( '../user' ) ;
var posts = require ( '../posts' ) ;
var notifications = require ( '../notifications' ) ;
var categories = require ( '../categories' ) ;
var privileges = require ( '../privileges' ) ;
var meta = require ( '../meta' ) ;
var utils = require ( '../utils' ) ;
var plugins = require ( '../plugins' ) ;
const async = require ( 'async' ) ;
const _ = require ( 'lodash' ) ;
const db = require ( '../database' ) ;
const user = require ( '../user' ) ;
const posts = require ( '../posts' ) ;
const notifications = require ( '../notifications' ) ;
const categories = require ( '../categories' ) ;
const privileges = require ( '../privileges' ) ;
const meta = require ( '../meta' ) ;
const utils = require ( '../utils' ) ;
const plugins = require ( '../plugins' ) ;
module . exports = function ( Topics ) {
Topics . getTotalUnread = async function ( uid , filter ) {
@ -22,7 +22,7 @@ module.exports = function (Topics) {
} ;
Topics . getUnreadTopics = async function ( params ) {
var unreadTopics = {
const unreadTopics = {
showSelect : true ,
nextStart : 0 ,
topics : [ ] ,
@ -57,51 +57,18 @@ module.exports = function (Topics) {
Topics . getUnreadData = async function ( params ) {
const uid = parseInt ( params . uid , 10 ) ;
const counts = {
'' : 0 ,
new : 0 ,
watched : 0 ,
unreplied : 0 ,
} ;
const noUnreadData = {
tids : [ ] ,
counts : counts ,
tidsByFilter : {
'' : [ ] ,
new : [ ] ,
watched : [ ] ,
unreplied : [ ] ,
} ,
} ;
if ( uid <= 0 ) {
return noUnreadData ;
}
params . filter = params . filter || '' ;
var cutoff = params . cutoff || Topics . unreadCutoff ( ) ;
if ( params . cid && ! Array . isArray ( params . cid ) ) {
params . cid = [ params . cid ] ;
}
const [ ignoredTids , recentTids , userScores , tids _unread ] = await Promise . all ( [
user . getIgnoredTids ( uid , 0 , - 1 ) ,
db . getSortedSetRevRangeByScoreWithScores ( 'topics:recent' , 0 , - 1 , '+inf' , cutoff ) ,
db . getSortedSetRevRangeByScoreWithScores ( 'uid:' + uid + ':tids_read' , 0 , - 1 , '+inf' , cutoff ) ,
db . getSortedSetRevRangeWithScores ( 'uid:' + uid + ':tids_unread' , 0 , - 1 ) ,
] ) ;
if ( recentTids && ! recentTids . length && ! tids _unread . length ) {
return noUnreadData ;
const data = await getTids ( params ) ;
if ( ! data . tids && ! data . tids . length ) {
return data ;
}
const data = await filterTopics ( params , {
ignoredTids : ignoredTids ,
recentTids : recentTids ,
userScores : userScores ,
tids _unread : tids _unread ,
} ) ;
const result = await plugins . fireHook ( 'filter:topics.getUnreadTids' , {
uid : uid ,
tids : data . tids ,
@ -113,83 +80,69 @@ module.exports = function (Topics) {
return result ;
} ;
async function filterTopics ( params , results ) {
const counts = {
'' : 0 ,
new : 0 ,
watched : 0 ,
unreplied : 0 ,
} ;
const tidsByFilter = {
'' : [ ] ,
new : [ ] ,
watched : [ ] ,
unreplied : [ ] ,
} ;
async function getTids ( params ) {
const counts = { '' : 0 , new : 0 , watched : 0 , unreplied : 0 } ;
const tidsByFilter = { '' : [ ] , new : [ ] , watched : [ ] , unreplied : [ ] } ;
var userRead = { } ;
results . userScores . forEach ( function ( userItem ) {
userRead [ userItem . value ] = userItem . score ;
} ) ;
if ( params . uid <= 0 ) {
return { counts : counts , tids : [ ] , tidsByFilter : tidsByFilter } ;
}
results . recentTids = results . recentTids . concat ( results . tids _unread ) ;
results . recentTids . sort ( function ( a , b ) {
return b . score - a . score ;
} ) ;
const cutoff = params . cutoff || Topics . unreadCutoff ( ) ;
var tids = results . recentTids . filter ( function ( recentTopic ) {
if ( results . ignoredTids . includes ( String ( recentTopic . value ) ) ) {
return false ;
}
return ! userRead [ recentTopic . value ] || recentTopic . score > userRead [ recentTopic . value ] ;
} ) ;
const [ followedTids , ignoredTids , recentTids , userScores , tids _unread ] = await Promise . all ( [
getFollowedTids ( params ) ,
user . getIgnoredTids ( params . uid , 0 , - 1 ) ,
getRecentTids ( params ) ,
db . getSortedSetRevRangeByScoreWithScores ( 'uid:' + params . uid + ':tids_read' , 0 , - 1 , '+inf' , cutoff ) ,
db . getSortedSetRevRangeWithScores ( 'uid:' + params . uid + ':tids_unread' , 0 , - 1 ) ,
] ) ;
tids = _ . uniq ( tids . map ( topic => topic . value ) ) ;
const userReadTime = _ . mapValues ( _ . keyBy ( userScores , 'value' ) , 'score' ) ;
const isTopicsFollowed = _ . mapValues ( _ . keyBy ( followedTids , 'value' ) , 'score' ) ;
var cid = params . cid ;
var uid = params . uid ;
var cids ;
var topicData ;
const unreadTopics = _ . unionWith ( recentTids , followedTids . concat ( tids _unread ) , ( a , b ) => a . value === b . value )
. filter ( t => ! ignoredTids . includes ( t . value ) && ( ! userReadTime [ t . value ] || t . score > userReadTime [ t . value ] ) )
. sort ( ( a , b ) => b . score - a . score ) ;
tids = tids . slice ( 0 , 200 ) ;
let tids = _ . uniq ( unreadTopics . map ( topic => topic . value ) ) . slice ( 0 , 200 ) ;
if ( ! tids . length ) {
return { counts : counts , tids : tids , tidsByFilter : tidsByFilter } ;
}
const blockedUids = await user . blocks . list ( uid ) ;
const blockedUids = await user . blocks . list ( params . uid ) ;
tids = await filterTidsThatHaveBlockedPosts ( {
uid : uid,
uid : params. uid,
tids : tids ,
blockedUids : blockedUids ,
recentTids : re sults. re centTids,
recentTids : re centTids,
} ) ;
topicData = await Topics . getTopicsFields ( tids , [ 'tid' , 'cid' , 'uid' , 'postcount' ] ) ;
cids = _ . uniq ( topicData . map ( topic => topic . cid ) ) . filter ( Boolean ) ;
const topicData = await Topics . getTopicsFields ( tids , [ 'tid' , 'cid' , 'uid' , 'postcount' ] ) ;
const topi cC ids = _ . uniq ( topicData . map ( topic => topic . cid ) ) . filter ( Boolean ) ;
const [ isTopicsFollowed , categoryWatchState , readCids ] = await Promise . all ( [
db . sortedSetScores ( 'uid:' + uid + ':followed_tids' , tids ) ,
categories . getWatchState ( cids , uid ) ,
privileges . categories . filterCids ( 'read' , cids , uid ) ,
const [ categoryWatchState , readCids ] = await Promise . all ( [
categories . getWatchState ( topicCids , params . uid ) ,
privileges . categories . filterCids ( 'topics:read' , topicCids , params . uid ) ,
] ) ;
cid = cid && cid . map ( String ) ;
const filterCids = params . cid && params . cid . map ( String ) ;
const readableCids = readCids . map ( String ) ;
const userCidState = _ . zipObject ( cids, categoryWatchState ) ;
const userCidState = _ . zipObject ( topi cC ids, categoryWatchState ) ;
topicData . forEach ( function ( topic , index ) {
function cidMatch ( topicCid ) {
return ( ! cid || ( cid . length && cid . includes ( String ( topicC id) ) ) ) && readableCids . includes ( String ( topic C id) ) ;
topicData . forEach ( function ( topic ) {
function cidMatch ( ) {
return ( ! filterCids || ( filterCids . length && filterCids . includes ( String ( topic . c id) ) ) ) && readableCids . includes ( String ( topic . c id) ) ;
}
if ( topic && topic . cid && cidMatch ( topic . cid ) && ! blockedUids . includes ( parseInt ( topic . uid , 10 ) ) ) {
topic . tid = parseInt ( topic . tid , 10 ) ;
if ( ( isTopicsFollowed [ index ] || userCidState [ topic . cid ] === categories . watchStates . watching ) ) {
if ( topic && topic . cid && cidMatch ( ) && ! blockedUids . includes ( topic . uid ) ) {
if ( isTopicsFollowed [ topic . tid ] || userCidState [ topic . cid ] === categories . watchStates . watching ) {
tidsByFilter [ '' ] . push ( topic . tid ) ;
}
if ( isTopicsFollowed [ index ] ) {
if ( isTopicsFollowed [ topic. tid ] ) {
tidsByFilter . watched . push ( topic . tid ) ;
}
@ -197,11 +150,12 @@ module.exports = function (Topics) {
tidsByFilter . unreplied . push ( topic . tid ) ;
}
if ( ! userRead [ topic . tid ] ) {
if ( ! userRead Time [ topic . tid ] ) {
tidsByFilter . new . push ( topic . tid ) ;
}
}
} ) ;
counts [ '' ] = tidsByFilter [ '' ] . length ;
counts . watched = tidsByFilter . watched . length ;
counts . unreplied = tidsByFilter . unreplied . length ;
@ -214,6 +168,25 @@ module.exports = function (Topics) {
} ;
}
async function getRecentTids ( params ) {
if ( params . filter === 'watched' ) {
return [ ] ;
}
const cutoff = params . cutoff || Topics . unreadCutoff ( ) ;
const cids = params . cid || await user . getWatchedCategories ( params . uid ) ;
const keys = cids . map ( cid => 'cid:' + cid + ':tids:lastposttime' ) ;
return await db . getSortedSetRevRangeByScoreWithScores ( keys , 0 , - 1 , '+inf' , cutoff ) ;
}
async function getFollowedTids ( params ) {
const tids = await db . getSortedSetRevRange ( 'uid:' + params . uid + ':followed_tids' , 0 , - 1 ) ;
const scores = await db . sortedSetScores ( 'topics:recent' , tids ) ;
const cutoff = params . cutoff || Topics . unreadCutoff ( ) ;
const data = tids . map ( ( tid , index ) => ( { value : tid , score : scores [ index ] } ) ) ;
return data . filter ( item => item . score > cutoff ) ;
}
async function filterTidsThatHaveBlockedPosts ( params ) {
if ( ! params . blockedUids . length ) {
return params . tids ;
@ -234,14 +207,14 @@ module.exports = function (Topics) {
}
async function doesTidHaveUnblockedUnreadPosts ( tid , params ) {
var userLastReadTimestamp = params . userLastReadTimestamp ;
const userLastReadTimestamp = params . userLastReadTimestamp ;
if ( ! userLastReadTimestamp ) {
return true ;
}
var start = 0 ;
var count = 3 ;
var done = false ;
var hasUnblockedUnread = params . topicTimestamp > userLastReadTimestamp ;
let start = 0 ;
const count = 3 ;
let done = false ;
let hasUnblockedUnread = params . topicTimestamp > userLastReadTimestamp ;
if ( ! params . blockedUids . length ) {
return hasUnblockedUnread ;
}
@ -252,9 +225,7 @@ module.exports = function (Topics) {
return hasUnblockedUnread ;
}
let postData = await posts . getPostsFields ( pidsSinceLastVisit , [ 'pid' , 'uid' ] ) ;
postData = postData . filter ( function ( post ) {
return ! params . blockedUids . includes ( parseInt ( post . uid , 10 ) ) ;
} ) ;
postData = postData . filter ( post => ! params . blockedUids . includes ( parseInt ( post . uid , 10 ) ) ) ;
done = postData . length > 0 ;
hasUnblockedUnread = postData . length > 0 ;
@ -295,23 +266,21 @@ module.exports = function (Topics) {
db . sortedSetScores ( 'uid:' + uid + ':tids_read' , tids ) ,
] ) ;
tids = tids . filter ( function ( tid , index ) {
return topicScores [ index ] && ( ! userScores [ index ] || userScores [ index ] < topicScores [ index ] ) ;
} ) ;
tids = tids . filter ( ( tid , index ) => topicScores [ index ] && ( ! userScores [ index ] || userScores [ index ] < topicScores [ index ] ) ) ;
if ( ! tids . length ) {
return false ;
}
var now = Date . now ( ) ;
var scores = tids . map ( ( ) => now ) ;
const now = Date . now ( ) ;
const scores = tids . map ( ( ) => now ) ;
const [ topicData ] = await Promise . all ( [
Topics . getTopicsFields ( tids , [ 'cid' ] ) ,
db . sortedSetAdd ( 'uid:' + uid + ':tids_read' , scores , tids ) ,
db . sortedSetRemove ( 'uid:' + uid + ':tids_unread' , tids ) ,
] ) ;
var cids = _ . uniq ( topicData . map ( t => t && t . cid ) . filter ( Boolean ) ) ;
const cids = _ . uniq ( topicData . map ( t => t && t . cid ) . filter ( Boolean ) ) ;
await categories . markAsRead ( cids , uid ) ;
plugins . fireHook ( 'action:topics.markAsRead' , { uid : uid , tids : tids } ) ;
@ -350,9 +319,9 @@ module.exports = function (Topics) {
user . blocks . list ( uid ) ,
] ) ;
var cutoff = Topics . unreadCutoff ( ) ;
var result = tids . map ( function ( tid , index ) {
var read = ! tids _unread [ index ] &&
const cutoff = Topics . unreadCutoff ( ) ;
const result = tids . map ( function ( tid , index ) {
const read = ! tids _unread [ index ] &&
( topicScores [ index ] < cutoff ||
! ! ( userScores [ index ] && userScores [ index ] >= topicScores [ index ] ) ) ;
return { tid : tid , read : read , index : index } ;