diff --git a/app/javascript/gabsocial/actions/notifications.js b/app/javascript/gabsocial/actions/notifications.js index 11114089..29332ccb 100644 --- a/app/javascript/gabsocial/actions/notifications.js +++ b/app/javascript/gabsocial/actions/notifications.js @@ -140,7 +140,7 @@ const excludeTypesFromFilter = filter => { return allTypes.filterNot(item => item === filter).toJS(); }; -const noOp = () => { }; +const noOp = () => {} export function expandNotifications({ maxId } = {}, done = noOp) { return (dispatch, getState) => { @@ -252,17 +252,20 @@ export function setFilter(path, value) { export function markReadNotifications() { return (dispatch, getState) => { - if (!me) return; - const top_notification = parseInt(getState().getIn(['notifications', 'items', 0, 'id'])); - const last_read = getState().getIn(['notifications', 'lastRead']); + if (!me) return + + const topNotification = parseInt(getState().getIn(['notifications', 'items', 0, 'id'])) + const lastReadId = getState().getIn(['notifications', 'lastReadId']) - if (top_notification && top_notification > last_read) { - api(getState).post('/api/v1/notifications/mark_read', { id: top_notification }).then(response => { + if (topNotification && topNotification > lastReadId && lastReadId !== -1) { + api(getState).post('/api/v1/notifications/mark_read', { + id: topNotification + }).then(() => { dispatch({ type: NOTIFICATIONS_MARK_READ, - notification: top_notification, - }); - }); + notification: topNotification, + }) + }) } } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/app/javascript/gabsocial/components/notification.js b/app/javascript/gabsocial/components/notification.js index 444e0840..0ac92824 100644 --- a/app/javascript/gabsocial/components/notification.js +++ b/app/javascript/gabsocial/components/notification.js @@ -2,13 +2,19 @@ import { Fragment } from 'react' import { NavLink } from 'react-router-dom' import { injectIntl, defineMessages } from 'react-intl' import ImmutablePureComponent from 'react-immutable-pure-component' -import { HotKeys } from 'react-hotkeys' import ImmutablePropTypes from 'react-immutable-proptypes' import { me } from '../initial_state' +import { + CX, + BREAKPOINT_EXTRA_SMALL, +} from '../constants' +import Responsive from '../features/ui/util/responsive_component' import StatusContainer from '../containers/status_container' import Avatar from './avatar' import Icon from './icon' import Text from './text' +import DotTextSeperator from './dot_text_seperator' +import RelativeTimestamp from './relative_timestamp' import DisplayName from './display_name' const messages = defineMessages({ @@ -24,15 +30,6 @@ const messages = defineMessages({ repostedStatusMultiple: { id: 'reposted_status_multiple', defaultMessage: 'and {count} others reposted your status' }, }) -// : todo : -const notificationForScreenReader = (intl, message, timestamp) => { - const output = [message] - - output.push(intl.formatDate(timestamp, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' })) - - return output.join(', ') -} - export default @injectIntl class Notification extends ImmutablePureComponent { @@ -48,6 +45,7 @@ class Notification extends ImmutablePureComponent { statusId: PropTypes.string, type: PropTypes.string.isRequired, isHidden: PropTypes.bool, + isUnread: PropTypes.bool, } render() { @@ -58,12 +56,14 @@ class Notification extends ImmutablePureComponent { type, statusId, isHidden, + isUnread, } = this.props const count = !!accounts ? accounts.size : 0 let message let icon + switch (type) { case 'follow': icon = 'group' @@ -114,29 +114,43 @@ class Notification extends ImmutablePureComponent { ) } + const containerClasses = CX({ + default: 1, + px10: 1, + cursorPointer: 1, + bgSubtle_onHover: !isUnread, + highlightedComment: isUnread, + }) + return ( -
+
- + + + -
-
+
+
{ - accounts && accounts.slice(0, 8).map((account, i) => ( + accounts && accounts.map((account, i) => ( - + )) }
-
-
+
+
{ accounts && accounts.slice(0, 1).map((account, i) => ( @@ -148,6 +162,15 @@ class Notification extends ImmutablePureComponent { {' '} {message} + { + !!createdAt && + + + + + + + }
{ diff --git a/app/javascript/gabsocial/containers/notification_container.js b/app/javascript/gabsocial/containers/notification_container.js index 41e53e17..2da1826d 100644 --- a/app/javascript/gabsocial/containers/notification_container.js +++ b/app/javascript/gabsocial/containers/notification_container.js @@ -1,17 +1,4 @@ import { List as ImmutableList } from 'immutable' -import { openModal } from '../actions/modal' -import { mentionCompose } from '../actions/compose' -import { - reblog, - favorite, - unreblog, - unfavorite, -} from '../actions/interactions' -import { - hideStatus, - revealStatus, -} from '../actions/statuses' -import { boostModal } from '../initial_state' import { makeGetNotification } from '../selectors' import Notification from '../components/notification' @@ -27,25 +14,29 @@ const makeMapStateToProps = () => { const isLikes = !!props.notification.get('like') const isReposts = !!props.notification.get('repost') const isGrouped = isFollows || isLikes || isReposts + const lastReadId = state.getIn(['notifications', 'lastReadId']) if (isFollows) { + let lastUpdated const list = props.notification.get('follow') - let accounts = ImmutableList() list.forEach((item) => { const account = getAccountFromState(state, item.get('account')) accounts = accounts.set(accounts.size, account) + if (!lastUpdated) lastUpdated = item.get('created_at') }) return { type: 'follow', - accounts: accounts, - createdAt: undefined, + accounts: accounts, + createdAt: lastUpdated, + isUnread: false, statusId: undefined, } } else if (isLikes || isReposts) { const theType = isLikes ? 'like' : 'repost' const list = props.notification.get(theType) + let lastUpdated = list.get('lastUpdated') let accounts = ImmutableList() const accountIdArr = list.get('accounts') @@ -59,7 +50,8 @@ const makeMapStateToProps = () => { return { type: theType, accounts: accounts, - createdAt: undefined, + createdAt: lastUpdated, + isUnread: false, statusId: list.get('status'), } } else if (!isGrouped) { @@ -68,9 +60,10 @@ const makeMapStateToProps = () => { const statusId = notification.get('status') return { - accounts: !!account ? ImmutableList([account]) : ImmutableList(), type: notification.get('type'), + accounts: !!account ? ImmutableList([account]) : ImmutableList(), createdAt: notification.get('created_at'), + isUnread: lastReadId < notification.get('id'), statusId: statusId || undefined, } } @@ -79,42 +72,4 @@ const makeMapStateToProps = () => { return mapStateToProps } -const mapDispatchToProps = (dispatch) => ({ - onMention: (account, router) => { - dispatch(mentionCompose(account, router)) - }, - - onModalRepost (status) { - dispatch(repost(status)) - }, - - onRepost (status, e) { - if (status.get('reblogged')) { - dispatch(unrepost(status)) - } else { - if (e.shiftKey || !boostModal) { - this.onModalRepost(status) - } else { - dispatch(openModal('BOOST', { status, onRepost: this.onModalRepost })) - } - } - }, - - onFavorite (status) { - if (status.get('favourited')) { - dispatch(unfavorite(status)) - } else { - dispatch(favorite(status)) - } - }, - - onToggleHidden (status) { - if (status.get('hidden')) { - dispatch(revealStatus(status.get('id'))) - } else { - dispatch(hideStatus(status.get('id'))) - } - }, -}) - -export default connect(makeMapStateToProps, mapDispatchToProps)(Notification) +export default connect(makeMapStateToProps)(Notification) diff --git a/app/javascript/gabsocial/features/notifications.js b/app/javascript/gabsocial/features/notifications.js index 7a1a7f9c..dd236606 100644 --- a/app/javascript/gabsocial/features/notifications.js +++ b/app/javascript/gabsocial/features/notifications.js @@ -1,8 +1,7 @@ +import { Fragment } from 'react' 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 { FormattedMessage } from 'react-intl' import debounce from 'lodash.debounce' import { expandNotifications, @@ -10,37 +9,33 @@ import { dequeueNotifications, } 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' +import TimelineQueueButtonHeader from '../components/timeline_queue_button_header' +import Block from '../components/block' const mapStateToProps = (state) => ({ notifications: state.getIn(['notifications', 'items']), + sortedNotifications: state.getIn(['notifications', 'sortedItems']), isLoading: state.getIn(['notifications', 'isLoading'], true), - isUnread: state.getIn(['notifications', 'unread']) > 0, hasMore: state.getIn(['notifications', 'hasMore']), totalQueuedNotificationsCount: state.getIn(['notifications', 'totalQueuedNotificationsCount'], 0), }) export default @connect(mapStateToProps) -@injectIntl class Notifications extends ImmutablePureComponent { static propTypes = { + sortedNotifications: ImmutablePropTypes.list.isRequired, notifications: ImmutablePropTypes.list.isRequired, dispatch: PropTypes.func.isRequired, - intl: PropTypes.object.isRequired, isLoading: PropTypes.bool, - isUnread: PropTypes.bool, hasMore: PropTypes.bool, dequeueNotifications: PropTypes.func, totalQueuedNotificationsCount: PropTypes.number, } - componentWillUnmount () { + componentWillUnmount() { this.handleLoadOlder.cancel() this.handleScrollToTop.cancel() this.handleScroll.cancel() @@ -52,14 +47,9 @@ class Notifications extends ImmutablePureComponent { this.props.dispatch(scrollTopNotifications(true)) } - handleLoadGap = (maxId) => { - // maxId={index > 0 ? notifications.getIn([index - 1, 'id']) : null} - // this.props.dispatch(expandNotifications({ maxId })) - } - handleLoadOlder = debounce(() => { const last = this.props.notifications.last() - // this.props.dispatch(expandNotifications({ maxId: last && last.get('id') })) + this.props.dispatch(expandNotifications({ maxId: last && last.get('id') })) }, 300, { leading: true }) handleScrollToTop = debounce(() => { @@ -70,97 +60,57 @@ class Notifications extends ImmutablePureComponent { this.props.dispatch(scrollTopNotifications(false)) }, 100) - setColumnRef = c => { - this.column = c - } - - handleMoveUp = id => { - const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) - 1 - this._selectChild(elementIndex, true) - } - - handleMoveDown = id => { - const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) + 1 - this._selectChild(elementIndex, false) - } - - _selectChild (index, align_top) { - const container = this.column.node - const element = container.querySelector(`article:nth-of-type(${index + 1}) .focusable`) - - 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() - } - } - handleDequeueNotifications = () => { this.props.dispatch(dequeueNotifications()) } - render () { + render() { const { - intl, - notifications, + sortedNotifications, isLoading, - isUnread, hasMore, - totalQueuedNotificationsCount + totalQueuedNotificationsCount, } = this.props let scrollableContent = null // : todo : include follow requests - // console.log('--0--notifications:', hasMore) - if (isLoading && this.scrollableContent) { scrollableContent = this.scrollableContent - } else if (notifications.size > 0 || hasMore) { - scrollableContent = notifications.map((item, index) => item === null ? ( - - ) : ( + } else if (sortedNotifications.size > 0 || hasMore) { + scrollableContent = sortedNotifications.map((item, index) => ( )) - } else { - scrollableContent = null } this.scrollableContent = scrollableContent return ( -
- + - } onLoadMore={this.handleLoadOlder} onScrollToTop={this.handleScrollToTop} onScroll={this.handleScroll} > - { scrollableContent } + {scrollableContent} -
+ ) } diff --git a/app/javascript/gabsocial/initial_state.js b/app/javascript/gabsocial/initial_state.js index bb840b14..b4ac7bc3 100644 --- a/app/javascript/gabsocial/initial_state.js +++ b/app/javascript/gabsocial/initial_state.js @@ -24,6 +24,7 @@ export const isStaff = getMeta('is_staff'); export const forceSingleColumn = !getMeta('advanced_layout'); export const promotions = initialState && initialState.promotions; export const unreadCount = getMeta('unread_count'); +export const lastReadNotificationId = getMeta('last_read_notification_id'); export const monthlyExpensesComplete = getMeta('monthly_expenses_complete'); export const favouritesCount = getMeta('favourites_count'); export const compactMode = false; diff --git a/app/javascript/gabsocial/reducers/notifications.js b/app/javascript/gabsocial/reducers/notifications.js index d8b7b914..9a7d36e6 100644 --- a/app/javascript/gabsocial/reducers/notifications.js +++ b/app/javascript/gabsocial/reducers/notifications.js @@ -11,25 +11,29 @@ import { NOTIFICATIONS_DEQUEUE, MAX_QUEUED_NOTIFICATIONS, NOTIFICATIONS_MARK_READ, -} from '../actions/notifications'; +} from '../actions/notifications' import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS, -} from '../actions/accounts'; -import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines'; -import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +} from '../actions/accounts' +import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines' +import { Range, Map as ImmutableMap, List as ImmutableList } from 'immutable' +import { unreadCount, lastReadNotificationId } from '../initial_state' import compareId from '../utils/compare_id'; -import { unreadCount } from '../initial_state'; + +const DEFAULT_NOTIFICATIONS_LIMIT = 20 const initialState = ImmutableMap({ items: ImmutableList(), + sortedItems: ImmutableList(), + lastId: null, hasMore: true, top: false, unread: 0, isLoading: false, queuedNotifications: ImmutableList(), //max = MAX_QUEUED_NOTIFICATIONS totalQueuedNotificationsCount: 0, //used for queuedItems overflow for MAX_QUEUED_NOTIFICATIONS+ - lastRead: -1, + lastReadId: -1, filter: ImmutableMap({ active: 'all', onlyVerified: false, @@ -37,7 +41,7 @@ const initialState = ImmutableMap({ }), }); -const notificationToMap = notification => ImmutableMap({ +const notificationToMap = (notification) => ImmutableMap({ id: notification.id, type: notification.type, account: notification.account.id, @@ -45,98 +49,137 @@ const notificationToMap = notification => ImmutableMap({ status: notification.status ? notification.status.id : null, }); -const normalizeNotification = (state, notification) => { - const top = state.get('top'); +const makeSortedNotifications = (state) => { + let finalSortedItems = ImmutableList() + const items = state.get('items') - if (!top) { - state = state.update('unread', unread => unread + 1); - } - - return state.update('items', list => { - if (top && list.size > 40) { - list = list.take(20); - } - - return list.unshift(notificationToMap(notification)); - }); -}; - -const expandNormalizedNotifications = (state, notifications, next) => { - //Grouped items - let follows = ImmutableList() - let likes = {} - let reposts = {} + const chunks = Range(0, items.count(), DEFAULT_NOTIFICATIONS_LIMIT) + .map((chunkStart) => items.slice(chunkStart, chunkStart + DEFAULT_NOTIFICATIONS_LIMIT)) - let items = ImmutableList() + chunks.forEach((chunk) => { + let sortedItems = ImmutableList() - notifications.forEach((n) => { - const notification = notificationToMap(n) - const statusId = notification.get('status') - const type = notification.get('type') + let follows = ImmutableList() + let likes = {} + let reposts = {} + + let followIndex = -1 + let indexesForStatusesForReposts = {} + let indexesForStatusesForFavorites = {} - switch (type) { - case 'follow': { - follows = follows.set(follows.size, notification) - break + chunk.forEach((notification) => { + const statusId = notification.get('status') + const type = notification.get('type') + + switch (type) { + case 'follow': { + if (followIndex === -1) followIndex = sortedItems.size + sortedItems = sortedItems.set(followIndex, ImmutableMap()) + follows = follows.set(follows.size, notification) + break + } + case 'favourite': { + if (likes[statusId] === undefined) { + let size = sortedItems.size + sortedItems = sortedItems.set(size, ImmutableMap()) + indexesForStatusesForFavorites[statusId] = size + likes[statusId] = [] + } + likes[statusId].push({ + account: notification.get('account'), + created_at: notification.get('created_at'), + }) + break + } + case 'reblog': { + if (reposts[statusId] === undefined) { + let size = sortedItems.size + sortedItems = sortedItems.set(size, ImmutableMap()) + indexesForStatusesForReposts[statusId] = size + reposts[statusId] = [] + } + reposts[statusId].push({ + account: notification.get('account'), + created_at: notification.get('created_at'), + }) + break + } + default: { + sortedItems = sortedItems.set(sortedItems.size, notification) + break + } } - case 'favourite': { - if (likes[statusId] === undefined) likes[statusId] = [] - likes[statusId].push({ - account: notification.get('account'), - }) - break + + if (follows.size > 0) { + sortedItems = sortedItems.set(followIndex, ImmutableMap({ + follow: follows, + })) } - case 'reblog': { - if (reposts[statusId] === undefined) reposts[statusId] = [] - reposts[statusId].push({ - account: notification.get('account'), - }) - break + if (Object.keys(likes).length > 0) { + for (const statusId in likes) { + if (likes.hasOwnProperty(statusId)) { + const likeArr = likes[statusId] + const accounts = likeArr.map((l) => l.account) + const lastUpdated = likeArr[0]['created_at'] + sortedItems = sortedItems.set(indexesForStatusesForFavorites[statusId], ImmutableMap({ + like: ImmutableMap({ + accounts, + lastUpdated, + status: statusId, + }) + })) + } + } } - default: { - items = items.set(items.size, notification) - break + if (Object.keys(reposts).length > 0) { + for (const statusId in reposts) { + if (reposts.hasOwnProperty(statusId)) { + const repostArr = reposts[statusId] + const accounts = repostArr.map((l) => l.account) + const lastUpdated = repostArr[0]['created_at'] + sortedItems = sortedItems.set(indexesForStatusesForReposts[statusId], ImmutableMap({ + repost: ImmutableMap({ + accounts, + lastUpdated, + status: statusId, + }) + })) + } + } } - } + }) + + finalSortedItems = finalSortedItems.concat(sortedItems) }) - if (follows.size > 0) { - items = items.set(items.size, ImmutableMap({ - follow: follows, - })) - } - if (Object.keys(likes).length > 0) { - for (const statusId in likes) { - if (likes.hasOwnProperty(statusId)) { - const likeArr = likes[statusId] - const accounts = likeArr.map((l) => l.account) - items = items.set(items.size, ImmutableMap({ - like: ImmutableMap({ - status: statusId, - accounts: accounts, - }) - })) - } - } - } - if (Object.keys(reposts).length > 0) { - for (const statusId in reposts) { - if (reposts.hasOwnProperty(statusId)) { - const repostArr = reposts[statusId] - const accounts = repostArr.map((l) => l.account) - items = items.set(items.size, ImmutableMap({ - repost: ImmutableMap({ - status: statusId, - accounts: accounts, - }) - })) - } - } + return state.set('sortedItems', finalSortedItems) +} + +const normalizeNotification = (state, notification) => { + const top = state.get('top') + + if (!top) { + state = state.update('unread', (unread) => unread + 1) } - return state.withMutations(mutable => { + state = state.update('items', (list) => { + if (top && list.size > 40) list = list.take(20) + return list.unshift(notificationToMap(notification)) + }) + + return makeSortedNotifications(state) +} + +const expandNormalizedNotifications = (state, notifications, next) => { + let items = ImmutableList() + + notifications.forEach((n, i) => { + items = items.set(i, notificationToMap(n)) + }) + + state = state.withMutations((mutable) => { if (!items.isEmpty()) { - mutable.update('items', list => { + mutable.update('items', (list) => { const lastIndex = 1 + list.findLastIndex( item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id')) ) @@ -145,35 +188,35 @@ const expandNormalizedNotifications = (state, notifications, next) => { item => item !== null && compareId(item.get('id'), items.first().get('id')) > 0 ) - const pop = list.take(firstIndex).concat(items, list.skip(lastIndex)) - - return pop + return list.take(firstIndex).concat(items, list.skip(lastIndex)) }) } - if (!next) { - mutable.set('hasMore', false); - } + if (!next) mutable.set('hasMore', false) + mutable.set('isLoading', false) + }) - mutable.set('isLoading', false); - }); -}; - -const filterNotifications = (state, relationship) => { - return state.update('items', list => list.filterNot(item => item !== null && item.get('account') === relationship.id)); -}; + return makeSortedNotifications(state) +} const updateTop = (state, top) => { - if (top) { - state = state.set('unread', 0); - } + return state.withMutations((mutable) => { + if (top) mutable.set('unread', 0) + mutable.set('top', top) + }) +} - return state.set('top', top); -}; +const filterNotifications = (state, relationship) => { + const filterer = (list) => list.filterNot((item) => !!item && item.get('account') === relationship.id) + state = state.update('items', filterer) + return makeSortedNotifications(state) +} const deleteByStatus = (state, statusId) => { - return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId)); -}; + const filterer = (list) => list.filterNot((item) => !!item && item.get('status') === statusId) + state = state.update('items', filterer) + return makeSortedNotifications(state) +} const updateNotificationsQueue = (state, notification, intlMessages, intlLocale) => { const queuedNotifications = state.getIn(['queuedNotifications'], ImmutableList()); @@ -181,9 +224,10 @@ const updateNotificationsQueue = (state, notification, intlMessages, intlLocale) const totalQueuedNotificationsCount = state.getIn(['totalQueuedNotificationsCount'], 0); const unread = state.getIn(['unread'], 0) - let alreadyExists = queuedNotifications.find(existingQueuedNotification => existingQueuedNotification.id === notification.id); - if (!alreadyExists) alreadyExists = listedNotifications.find(existingListedNotification => existingListedNotification.get('id') === notification.id); - + let alreadyExists = queuedNotifications.find((existingQueuedNotification) => existingQueuedNotification.id === notification.id); + if (!alreadyExists) { + alreadyExists = listedNotifications.find((existingListedNotification) => existingListedNotification.get('id') === notification.id); + } if (alreadyExists) { return state; } @@ -206,9 +250,12 @@ const updateNotificationsQueue = (state, notification, intlMessages, intlLocale) export default function notifications(state = initialState, action) { switch(action.type) { case NOTIFICATIONS_INITIALIZE: - return state.set('unread', unreadCount); + return state.withMutations((mutable) => { + mutable.set('unread', unreadCount) + mutable.set('lastReadId', lastReadNotificationId) + }) case NOTIFICATIONS_MARK_READ: - return state.set('lastRead', action.notification); + return state.set('lastReadId', action.lastReadId); case NOTIFICATIONS_EXPAND_REQUEST: return state.set('isLoading', true); case NOTIFICATIONS_EXPAND_FAIL: diff --git a/app/javascript/styles/global.css b/app/javascript/styles/global.css index b92b41e3..ecc5a959 100644 --- a/app/javascript/styles/global.css +++ b/app/javascript/styles/global.css @@ -497,6 +497,7 @@ body { .maxWidth100PC42PX { max-width: calc(100% - 42px); } .minWidth330PX { min-width: 330px; } +.minWidth20PX { min-width: 20px; } .minWidth14PX { min-width: 14px; } .width100PC { width: 100%; } diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 956dfbe5..f50d0502 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -37,6 +37,7 @@ class InitialStateSerializer < ActiveModel::Serializer store[:group_in_home_feed] = object.current_account.user.setting_group_in_home_feed store[:is_staff] = object.current_account.user.staff? store[:unread_count] = unread_count object.current_account + store[:last_read_notification_id] = object.current_account.user.last_read_notification store[:monthly_expenses_complete] = Redis.current.get("monthly_funding_amount") || 0 store[:favourites_count] = object.current_account.favourites.count.to_s end