diff --git a/src/scenes/mailboxes/src/Components/Service/DefaultServiceBadge.js b/src/scenes/mailboxes/src/Components/Service/DefaultServiceBadge.js new file mode 100644 index 000000000..8d8cac9da --- /dev/null +++ b/src/scenes/mailboxes/src/Components/Service/DefaultServiceBadge.js @@ -0,0 +1,111 @@ +import PropTypes from 'prop-types' +import React from 'react' +import shallowCompare from 'react-addons-shallow-compare' +import { mailboxStore } from 'stores/mailbox' +import ServiceBadge from './ServiceBadge' + +export default class DefaultServiceBadge extends React.Component { + /* **************************************************************************/ + // Class + /* **************************************************************************/ + + static propTypes = { + isAuthInvalid: PropTypes.bool.isRequired, + mailboxId: PropTypes.string.isRequired, + displayMailboxOverview: PropTypes.bool.isRequired, + badgeStyle: PropTypes.object, + iconStyle: PropTypes.object + } + + /* **************************************************************************/ + // Component Lifecycle + /* **************************************************************************/ + + componentDidMount () { + mailboxStore.listen(this.mailboxUpdated) + } + + componentWillUnmount () { + mailboxStore.unlisten(this.mailboxUpdated) + } + + componentWillReceiveProps (nextProps) { + if (nextProps.mailboxId !== this.props.mailboxId) { + this.setState(this.generateState(nextProps, undefined)) + } + } + + /* **************************************************************************/ + // Data lifecycle + /* **************************************************************************/ + + /** + * Generates the state + * @param props: the props to use + * @param mailboxState=autoget: the mailbox state + * @return the state object + */ + generateState (props, mailboxState = mailboxStore.getState()) { + const mailbox = mailboxState.getMailbox(props.mailboxId) + return { + mailbox: mailbox, + service: mailbox ? mailbox.defaultService : undefined, + mailboxUnreadCount: mailboxState.mailboxUnreadCountForUser(props.mailboxId), + mailboxHasUnreadActivity: mailboxState.mailboxHasUnreadActivityForUser(props.mailboxId) + } + } + + state = this.generateState(this.props, undefined) + + mailboxUpdated = (mailboxState) => { + this.setState(this.generateState(this.props, mailboxState)) + } + + /* **************************************************************************/ + // Rendering + /* **************************************************************************/ + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState) + } + + render () { + const { + mailboxId, + displayMailboxOverview, + ...passProps + } = this.props + const { + mailbox, + service, + mailboxUnreadCount, + mailboxHasUnreadActivity + } = this.state + + if (displayMailboxOverview && mailbox.hasAdditionalServices) { + return ( + + ) + } else { + return ( + + ) + } + } +} diff --git a/src/scenes/mailboxes/src/Components/Service/index.js b/src/scenes/mailboxes/src/Components/Service/index.js index 734773d1b..a6b7f91ff 100644 --- a/src/scenes/mailboxes/src/Components/Service/index.js +++ b/src/scenes/mailboxes/src/Components/Service/index.js @@ -1,7 +1,9 @@ import ServiceBadge from './ServiceBadge' import ServiceTooltip from './ServiceTooltip' +import DefaultServiceBadge from './DefaultServiceBadge' export { ServiceBadge, - ServiceTooltip + ServiceTooltip, + DefaultServiceBadge } diff --git a/src/scenes/mailboxes/src/Components/Tray/Tray.js b/src/scenes/mailboxes/src/Components/Tray/Tray.js index ae26adf1b..226d4b386 100644 --- a/src/scenes/mailboxes/src/Components/Tray/Tray.js +++ b/src/scenes/mailboxes/src/Components/Tray/Tray.js @@ -106,7 +106,7 @@ export default class Tray extends React.Component { */ generateMenuUnreadMessages (mailboxState = mailboxStore.getState()) { const mailboxMenuItems = mailboxState.allMailboxes().map((mailbox) => { - const trayMessages = mailbox.trayMessages + const trayMessages = mailboxState.mailboxTrayMessagesForUser(mailbox.id) const messageItemsSignature = trayMessages.map((message) => message.id).join(':') let messageItems = trayMessages.map((message) => { return { @@ -132,10 +132,11 @@ export default class Tray extends React.Component { { type: 'separator' } ) + const unreadCount = mailboxState.mailboxUnreadCountForUser(mailbox.id) return { signature: messageItemsSignature, label: [ - mailbox.unreadCount ? `(${mailbox.unreadCount})` : undefined, + unreadCount ? `(${unreadCount})` : undefined, mailbox.displayName || 'Untitled' ].filter((item) => !!item).join(' '), submenu: messageItems.length === 2 ? [...messageItems, diff --git a/src/scenes/mailboxes/src/Components/WindowTitle.js b/src/scenes/mailboxes/src/Components/WindowTitle.js index ab714ec46..7ae0558b8 100644 --- a/src/scenes/mailboxes/src/Components/WindowTitle.js +++ b/src/scenes/mailboxes/src/Components/WindowTitle.js @@ -32,7 +32,7 @@ export default class Provider extends React.Component { mailboxesChanged = (mailboxState) => { const activeMailbox = mailboxState.activeMailbox() this.setState({ - unreadCount: mailboxState.totalUnreadCountForAppBadge(), + unreadCount: mailboxState.totalUnreadCountForAppBadgeForUser(), activeMailboxName: activeMailbox ? activeMailbox.displayName : undefined, activeGuestTitle: this._getGuestPageTitle(mailboxState, undefined) }) @@ -70,7 +70,7 @@ export default class Provider extends React.Component { return { showTitlebarCount: settingsState.ui.showTitlebarCount, showTitlebarAccount: settingsState.ui.showTitlebarAccount, - unreadCount: mailboxState.totalUnreadCountForAppBadge(), + unreadCount: mailboxState.totalUnreadCountForAppBadgeForUser(), activeGuestTitle: this._getGuestPageTitle(mailboxState, guestState), activeMailboxName: activeMailbox ? activeMailbox.displayName : undefined } diff --git a/src/scenes/mailboxes/src/Scenes/AppScene/Sidelist/SidelistMailboxes/SidelistItemMailboxAvatar.js b/src/scenes/mailboxes/src/Scenes/AppScene/Sidelist/SidelistMailboxes/SidelistItemMailboxAvatar.js index f36437116..f2a232f35 100644 --- a/src/scenes/mailboxes/src/Scenes/AppScene/Sidelist/SidelistMailboxes/SidelistItemMailboxAvatar.js +++ b/src/scenes/mailboxes/src/Scenes/AppScene/Sidelist/SidelistMailboxes/SidelistItemMailboxAvatar.js @@ -5,7 +5,7 @@ import { MailboxAvatar } from 'Components/Mailbox' import { mailboxStore } from 'stores/mailbox' import { settingsStore } from 'stores/settings' import { userStore } from 'stores/user' -import { ServiceBadge, ServiceTooltip } from 'Components/Service' +import { DefaultServiceBadge, ServiceTooltip } from 'Components/Service' import * as Colors from 'material-ui/styles/colors' import uuid from 'uuid' import CoreMailbox from 'shared/Models/Accounts/CoreMailbox' @@ -188,26 +188,24 @@ export default class SidelistItemMalboxAvatar extends React.Component { let borderColor let showSleeping + let displayMailboxOverview if (mailbox.serviceDisplayMode === CoreMailbox.SERVICE_DISPLAY_MODES.SIDEBAR) { borderColor = isDefaultServiceActive || isHovering ? mailbox.color : Color(mailbox.color).lighten(0.4).rgb().string() showSleeping = isDefaultServiceSleeping && mailbox.showSleepableServiceIndicator && globalShowSleepableServiceIndicator + displayMailboxOverview = mailbox.collapseSidebarServices && !isMailboxActive && mailbox.hasAdditionalServices } else { borderColor = isMailboxActive || isHovering ? mailbox.color : Color(mailbox.color).lighten(0.4).rgb().string() showSleeping = false + displayMailboxOverview = mailbox.hasAdditionalServices } return ( - - + ) } } diff --git a/src/scenes/mailboxes/src/Scenes/Provider.js b/src/scenes/mailboxes/src/Scenes/Provider.js index 6c4d5ec92..6ea1451fc 100644 --- a/src/scenes/mailboxes/src/Scenes/Provider.js +++ b/src/scenes/mailboxes/src/Scenes/Provider.js @@ -97,8 +97,8 @@ export default class Provider extends React.Component { const settingsState = settingsStore.getState() const mailboxState = mailboxStore.getState() return { - messagesUnreadCount: mailboxState.totalUnreadCountForAppBadge(), - hasUnreadActivity: mailboxState.hasUnreadActivityForAppBadge(), + messagesUnreadCount: mailboxState.totalUnreadCountForAppBadgeForUser(), + hasUnreadActivity: mailboxState.hasUnreadActivityForAppBadgeForUser(), uiSettings: settingsState.ui, traySettings: settingsState.tray, launchTraySettings: settingsState.launched.tray, @@ -108,8 +108,8 @@ export default class Provider extends React.Component { mailboxesChanged = (mailboxState) => { this.setState({ - messagesUnreadCount: mailboxState.totalUnreadCountForAppBadge(), - hasUnreadActivity: mailboxState.hasUnreadActivityForAppBadge() + messagesUnreadCount: mailboxState.totalUnreadCountForAppBadgeForUser(), + hasUnreadActivity: mailboxState.hasUnreadActivityForAppBadgeForUser() }) } diff --git a/src/scenes/mailboxes/src/Scenes/SettingsScene/Accounts/AccountAppearanceSettings.js b/src/scenes/mailboxes/src/Scenes/SettingsScene/Accounts/AccountAppearanceSettings.js index 080559d70..c4bc05817 100644 --- a/src/scenes/mailboxes/src/Scenes/SettingsScene/Accounts/AccountAppearanceSettings.js +++ b/src/scenes/mailboxes/src/Scenes/SettingsScene/Accounts/AccountAppearanceSettings.js @@ -5,6 +5,7 @@ import { ColorPickerButton } from 'Components' import { mailboxActions, MailboxReducer } from 'stores/mailbox' import styles from '../CommonSettingStyles' import shallowCompare from 'react-addons-shallow-compare' +import CoreMailbox from 'shared/Models/Accounts/CoreMailbox' export default class AccountAppearanceSettings extends React.Component { /* **************************************************************************/ @@ -59,6 +60,15 @@ export default class AccountAppearanceSettings extends React.Component { render () { const { mailbox, ...passProps } = this.props + const hasCumulativeBadge = ( + mailbox.serviceDisplayMode === CoreMailbox.SERVICE_DISPLAY_MODES.TOOLBAR && + mailbox.hasAdditionalServices + ) || ( + mailbox.serviceDisplayMode === CoreMailbox.SERVICE_DISPLAY_MODES.SIDEBAR && + mailbox.collapseSidebarServices && + mailbox.hasAdditionalServices + ) + return (

Appearance

@@ -93,6 +103,32 @@ export default class AccountAppearanceSettings extends React.Component { label='Show Account Colour around Icon' labelPosition='right' onToggle={(evt, toggled) => mailboxActions.reduce(mailbox.id, MailboxReducer.setShowAvatarColorRing, toggled)} /> + {hasCumulativeBadge ? ( +
+
+

Sidebar Badge

+

+ When you have multiple services you can show the total unread count for those + services in the sidebar, so at a glance you know what's new +

+ { + mailboxActions.reduce(mailbox.id, MailboxReducer.setShowCumulativeSidebarUnreadBadge, toggled) + }} /> +
+ sms} + value={mailbox.cumulativeSidebarUnreadBadgeColor} + onChange={(col) => { + mailboxActions.reduce(mailbox.id, MailboxReducer.setCumulativeSidebarUnreadBadgeColor, col) + }} /> +
+
+ ) : undefined}
) } diff --git a/src/scenes/mailboxes/src/stores/mailbox/MailboxReducer.js b/src/scenes/mailboxes/src/stores/mailbox/MailboxReducer.js index 9262cdc3a..9be7f65fc 100644 --- a/src/scenes/mailboxes/src/stores/mailbox/MailboxReducer.js +++ b/src/scenes/mailboxes/src/stores/mailbox/MailboxReducer.js @@ -78,6 +78,32 @@ class MailboxReducer { }) } + /* **************************************************************************/ + // Badge + /* **************************************************************************/ + + /** + * Sets if the cumulative sidebar badge should be shown + * @param mailbox: the mailbox to update + * @param show: true to show, false otherwise + */ + static setShowCumulativeSidebarUnreadBadge (mailbox, show) { + return mailbox.changeData({ + showCumulativeSidebarUnreadBadge: show + }) + } + + /** + * Sets the cumulative sidebar badge color + * @param mailbox: the mailbox to update + * @param col: the color to set + */ + static setCumulativeSidebarUnreadBadgeColor (mailbox, col) { + return mailbox.changeData({ + cumulativeSidebarUnreadBadgeColor: typeof (col) === 'object' ? col.rgbaStr : col + }) + } + /* **************************************************************************/ // Lifecycle & Ordering /* **************************************************************************/ diff --git a/src/scenes/mailboxes/src/stores/mailbox/mailboxStore.js b/src/scenes/mailboxes/src/stores/mailbox/mailboxStore.js index af54782fb..70b61b479 100644 --- a/src/scenes/mailboxes/src/stores/mailbox/mailboxStore.js +++ b/src/scenes/mailboxes/src/stores/mailbox/mailboxStore.js @@ -345,24 +345,28 @@ class MailboxStore { /* ****************************************/ /** + * @param user=autoget: the user object * @return the total amount of unread items */ - this.totalUnreadCount = () => { + this.totalUnreadCountForUser = (user = userStore.getState().user) => { + const hasServices = user.hasServices return this.allMailboxes().reduce((acc, mailbox) => { if (mailbox) { - acc += mailbox.unreadCount + acc += mailbox.getUnreadCount(!hasServices) } return acc }, 0) } /** + * @param user=autoget: the user object * @return the total amount of unread items taking mailbox settings into account */ - this.totalUnreadCountForAppBadge = () => { + this.totalUnreadCountForAppBadgeForUser = (user = userStore.getState().user) => { + const hasServices = user.hasServices return this.allMailboxes().reduce((acc, mailbox) => { if (mailbox) { - return acc + mailbox.unreadCountForAppBadge + return acc + mailbox.getUnreadCountForAppBadge(!hasServices) } else { return acc } @@ -370,14 +374,51 @@ class MailboxStore { } /** + * @param user=autoget: the user object * @return true if any mailboxes have another unread info status, taking settings into account */ - this.hasUnreadActivityForAppBadge = () => { + this.hasUnreadActivityForAppBadgeForUser = (user = userStore.getState().user) => { + const hasServices = user.hasServices return !!this.allMailboxes().find((mailbox) => { - return mailbox && mailbox.unreadActivityForAppBadge + return mailbox && mailbox.getUnreadActivityForAppbadge(!hasServices) }) } + /** + * Gets the unread count taking into account the current user state + * @param id: the id of the mailbox + * @param user=autoget: the user object + * @return the unread count + */ + this.mailboxUnreadCountForUser = (id, user = userStore.getState().user) => { + const mailbox = this.getMailbox(id) + if (!mailbox) { return 0 } + return mailbox.getUnreadCount(!user.hasServices) + } + + /** + * Gets the unread activity taking into account the current user state + * @param id: the id of the mailbox + * @param user=autoget: the user object + * @return true if there is unread activity for the account + */ + this.mailboxHasUnreadActivityForUser = (id, user = userStore.getState().user) => { + const mailbox = this.getMailbox(id) + if (!mailbox) { return false } + return mailbox.getHasUnreadActivity(!user.hasServices) + } + + /** + * @param id: the id of the mailbox + * @param user=autoget: the user object + * @return the array of tray messages + */ + this.mailboxTrayMessagesForUser = (id, user = userStore.getState().user) => { + const mailbox = this.getMailbox(id) + if (!mailbox) { return [] } + return mailbox.getTrayMessages(!user.hasServices) + } + /* ****************************************/ // Takeout /* ****************************************/ diff --git a/src/shared/Models/Accounts/CoreMailbox.js b/src/shared/Models/Accounts/CoreMailbox.js index bbcd53f1a..0dc2f3cf8 100644 --- a/src/shared/Models/Accounts/CoreMailbox.js +++ b/src/shared/Models/Accounts/CoreMailbox.js @@ -252,6 +252,13 @@ class CoreMailbox extends Model { get collapseSidebarServices () { return this._value_('collapseSidebarServices', false) } get showSleepableServiceIndicator () { return this._value_('showSleepableServiceIndicator', true) } + /* **************************************************************************/ + // Properties : Badge + /* **************************************************************************/ + + get showCumulativeSidebarUnreadBadge () { return this._value_('showCumulativeSidebarUnreadBadge', true) } + get cumulativeSidebarUnreadBadgeColor () { return this._value_('cumulativeSidebarUnreadBadgeColor', 'rgba(238, 54, 55, 0.95)') } + /* **************************************************************************/ // Properties : Authentication /* **************************************************************************/ @@ -264,8 +271,15 @@ class CoreMailbox extends Model { /* **************************************************************************/ get displayName () { return this.id } - get unreadCount () { - return this.enabledServices.reduce((acc, service) => { + + /** + * Gets the unread count + * @param defaultServiceOnly=false: set to true to only return for the default service + * @return the total unread count + */ + getUnreadCount (defaultServiceOnly = false) { + const services = defaultServiceOnly ? [this.defaultService] : this.enabledServices + return services.reduce((acc, service) => { if (service.supportsUnreadCount && service.showUnreadBadge) { return acc + service.unreadCount } else { @@ -273,8 +287,15 @@ class CoreMailbox extends Model { } }, 0) } - get unreadCountForAppBadge () { - return this.enabledServices.reduce((acc, service) => { + + /** + * Gets the unread count for the app badge + * @param defaultServiceOnly=false: set to true to only return for the default service + * @return the total unread count for the app badge + */ + getUnreadCountForAppBadge (defaultServiceOnly = false) { + const services = defaultServiceOnly ? [this.defaultService] : this.enabledServices + return services.reduce((acc, service) => { if (service.supportsUnreadCount && service.unreadCountsTowardsAppUnread) { return acc + service.unreadCount } else { @@ -282,18 +303,39 @@ class CoreMailbox extends Model { } }, 0) } - get hasUnreadActivity () { - return !!this.enabledServices.find((service) => { + + /** + * Gets the unread activity + * @param defaultServiceOnly=false: set to true to only return for the default service + * @return true if there is any unread activity + */ + getHasUnreadActivity (defaultServiceOnly = false) { + const services = defaultServiceOnly ? [this.defaultService] : this.enabledServices + return !!services.find((service) => { return service.supportsUnreadCount && service.showUnreadActivityBadge && service.hasUnreadActivity }) } - get unreadActivityForAppBadge () { - return !!this.enabledServices.find((service) => { + + /** + * Gets the unread activity + * @param defaultServiceOnly=false: set to true to only return for the default service + * @return the total unread actibity for the app badge + */ + getUnreadActivityForAppbadge (defaultServiceOnly = false) { + const services = defaultServiceOnly ? [this.defaultService] : this.enabledServices + return !!services.find((service) => { return service.supportsUnreadCount && service.unreadActivityCountsTowardsAppUnread && service.hasUnreadActivity }) } - get trayMessages () { - return this.enabledServices.reduce((acc, service) => { + + /** + * Gets the tray messages + * @param defaultServiceOnly=false: set to true to only return for the default service + * @return all the tray messages as an array + */ + getTrayMessages (defaultServiceOnly = false) { + const services = defaultServiceOnly ? [this.defaultService] : this.enabledServices + return services.reduce((acc, service) => { if (service.supportsTrayMessages && service.showUnreadActivityBadge) { return acc.concat(service.trayMessages) } else { diff --git a/src/shared/Models/Accounts/Google/GoogleCommunicationService.js b/src/shared/Models/Accounts/Google/GoogleCommunicationService.js index 790d450b9..d774afe76 100644 --- a/src/shared/Models/Accounts/Google/GoogleCommunicationService.js +++ b/src/shared/Models/Accounts/Google/GoogleCommunicationService.js @@ -65,7 +65,7 @@ class GoogleCommunicationService extends GoogleService { // Properties : Provider Details & counts etc /* **************************************************************************/ - get unreadCount () { return 0 } + get unreadCount () { return 2 } } module.exports = GoogleCommunicationService