diff --git a/ts/components/SessionScrollButton.tsx b/ts/components/SessionScrollButton.tsx
index 5c7543da2..4d0b2b50d 100644
--- a/ts/components/SessionScrollButton.tsx
+++ b/ts/components/SessionScrollButton.tsx
@@ -3,8 +3,8 @@ import { useSelector } from 'react-redux';
import styled from 'styled-components';
import { getShowScrollButton } from '../state/selectors/conversations';
+import { useSelectedUnreadCount } from '../state/selectors/selectedConversation';
import { SessionIconButton } from './icon';
-import { Noop } from '../types/Util';
const SessionScrollButtonDiv = styled.div`
position: fixed;
@@ -18,8 +18,9 @@ const SessionScrollButtonDiv = styled.div`
}
`;
-export const SessionScrollButton = (props: { onClickScrollBottom: Noop }) => {
+export const SessionScrollButton = (props: { onClickScrollBottom: () => void }) => {
const show = useSelector(getShowScrollButton);
+ const unreadCount = useSelectedUnreadCount();
return (
@@ -29,6 +30,7 @@ export const SessionScrollButton = (props: { onClickScrollBottom: Noop }) => {
isHidden={!show}
onClick={props.onClickScrollBottom}
dataTestId="scroll-to-bottom-button"
+ unreadCount={unreadCount}
/>
);
diff --git a/ts/components/icon/SessionIcon.tsx b/ts/components/icon/SessionIcon.tsx
index e190abf42..604357ca3 100644
--- a/ts/components/icon/SessionIcon.tsx
+++ b/ts/components/icon/SessionIcon.tsx
@@ -16,6 +16,7 @@ export type SessionIconProps = {
noScale?: boolean;
backgroundColor?: string;
dataTestId?: string;
+ unreadCount?: number;
};
const getIconDimensionFromIconSize = (iconSize: SessionIconSize | number) => {
diff --git a/ts/components/icon/SessionIconButton.tsx b/ts/components/icon/SessionIconButton.tsx
index b572f6619..6ef11e431 100644
--- a/ts/components/icon/SessionIconButton.tsx
+++ b/ts/components/icon/SessionIconButton.tsx
@@ -1,10 +1,9 @@
-import React, { KeyboardEvent } from 'react';
import classNames from 'classnames';
import _ from 'lodash';
+import React, { KeyboardEvent } from 'react';
import styled from 'styled-components';
-
import { SessionIcon, SessionIconProps } from '.';
-import { SessionNotificationCount } from './SessionNotificationCount';
+import { SessionNotificationCount, SessionUnreadCount } from './SessionNotificationCount';
interface SProps extends SessionIconProps {
onClick?: (e?: React.MouseEvent) => void;
@@ -61,6 +60,7 @@ const SessionIconButtonInner = React.forwardRef((props,
dataTestIdIcon,
style,
tabIndex,
+ unreadCount,
} = props;
const clickHandler = (e: React.MouseEvent) => {
if (props.onClick) {
@@ -103,6 +103,7 @@ const SessionIconButtonInner = React.forwardRef((props,
dataTestId={dataTestIdIcon}
/>
{Boolean(notificationCount) && }
+ {Boolean(unreadCount) && }
);
});
diff --git a/ts/components/icon/SessionNotificationCount.tsx b/ts/components/icon/SessionNotificationCount.tsx
index 26281b828..ba014f7e9 100644
--- a/ts/components/icon/SessionNotificationCount.tsx
+++ b/ts/components/icon/SessionNotificationCount.tsx
@@ -1,18 +1,20 @@
import React from 'react';
import styled from 'styled-components';
+import { Constants } from '../../session';
type Props = {
+ overflowingAt: number;
+ centeredOnTop: boolean;
count?: number;
};
-
-const StyledCountContainer = styled.div<{ shouldRender: boolean }>`
+const StyledCountContainer = styled.div<{ centeredOnTop: boolean }>`
position: absolute;
font-size: 18px;
line-height: 1.2;
- top: 27px;
- left: 28px;
- padding: 1px 4px;
- opacity: 1;
+ top: ${props => (props.centeredOnTop ? '-10px' : '27px')};
+ left: ${props => (props.centeredOnTop ? '50%' : '28px')};
+ transform: ${props => (props.centeredOnTop ? 'translateX(-50%)' : 'none')};
+ padding: ${props => (props.centeredOnTop ? '3px 3px' : '1px 4px')};
display: flex;
align-items: center;
justify-content: center;
@@ -21,34 +23,56 @@ const StyledCountContainer = styled.div<{ shouldRender: boolean }>`
font-weight: 700;
background: var(--unread-messages-alert-background-color);
transition: var(--default-duration);
- opacity: ${props => (props.shouldRender ? 1 : 0)};
text-align: center;
color: var(--unread-messages-alert-text-color);
+ white-space: ${props => (props.centeredOnTop ? 'nowrap' : 'normal')};
`;
-const StyledCount = styled.div`
+const StyledCount = styled.div<{ centeredOnTop: boolean }>`
position: relative;
- font-size: 0.6rem;
+ font-size: ${props => (props.centeredOnTop ? 'var(--font-size-xs)' : '0.6rem')};
`;
-export const SessionNotificationCount = (props: Props) => {
- const { count } = props;
- const overflow = Boolean(count && count > 99);
- const shouldRender = Boolean(count && count > 0);
+const OverflowingAt = (props: { overflowingAt: number }) => {
+ return (
+ <>
+ {props.overflowingAt}
+ +
+ >
+ );
+};
- if (overflow) {
- return (
-
-
- {99}
- +
-
-
- );
+const NotificationOrUnreadCount = ({ centeredOnTop, overflowingAt, count }: Props) => {
+ if (!count) {
+ return null;
}
+ const overflowing = count > overflowingAt;
+
return (
-
- {count}
+
+
+ {overflowing ? : count}
+
);
};
+
+export const SessionNotificationCount = (props: Pick) => {
+ return (
+
+ );
+};
+
+export const SessionUnreadCount = (props: Pick) => {
+ return (
+
+ );
+};
diff --git a/ts/components/leftpane/conversation-list-item/HeaderItem.tsx b/ts/components/leftpane/conversation-list-item/HeaderItem.tsx
index 81d37e82f..ffdcd1f17 100644
--- a/ts/components/leftpane/conversation-list-item/HeaderItem.tsx
+++ b/ts/components/leftpane/conversation-list-item/HeaderItem.tsx
@@ -13,6 +13,7 @@ import {
useMentionedUs,
useUnreadCount,
} from '../../../hooks/useParamSelector';
+import { Constants } from '../../../session';
import {
openConversationToSpecificMessage,
openConversationWithMessages,
@@ -160,8 +161,14 @@ const UnreadCount = ({ convoId }: { convoId: string }) => {
const unreadMsgCount = useUnreadCount(convoId);
const forcedUnread = useIsForcedUnreadWithoutUnreadMsg(convoId);
+ const unreadWithOverflow =
+ unreadMsgCount > Constants.CONVERSATION.MAX_CONVO_UNREAD_COUNT
+ ? `${Constants.CONVERSATION.MAX_CONVO_UNREAD_COUNT}+`
+ : unreadMsgCount || ' ';
+
+ // TODO would be good to merge the style of this with SessionNotificationCount or SessionUnreadCount at some point.
return unreadMsgCount > 0 || forcedUnread ? (
- {unreadMsgCount || ' '}
+ {unreadWithOverflow}
) : null;
};
diff --git a/ts/hooks/useParamSelector.ts b/ts/hooks/useParamSelector.ts
index 71744f8ee..e51be0c27 100644
--- a/ts/hooks/useParamSelector.ts
+++ b/ts/hooks/useParamSelector.ts
@@ -7,7 +7,6 @@ import {
hasValidOutgoingRequestValues,
} from '../models/conversation';
import { isUsAnySogsFromCache } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys';
-import { CONVERSATION } from '../session/constants';
import { TimerOptions, TimerOptionsArray } from '../session/disappearing_messages/timerOptions';
import { PubKey } from '../session/types';
import { UserUtils } from '../session/utils';
@@ -241,12 +240,11 @@ export function useMessageReactsPropsById(messageId?: string) {
/**
* Returns the unread count of that conversation, or 0 if none are found.
- * Note: returned value is capped at a max of CONVERSATION.MAX_UNREAD_COUNT
+ * Note: returned value is capped at a max of CONVERSATION.MAX_CONVO_UNREAD_COUNT
*/
export function useUnreadCount(conversationId?: string): number {
const convoProps = useConversationPropsById(conversationId);
- const convoUnreadCount = convoProps?.unreadCount || 0;
- return Math.min(CONVERSATION.MAX_UNREAD_COUNT, convoUnreadCount);
+ return convoProps?.unreadCount || 0;
}
export function useHasUnread(conversationId?: string): boolean {
diff --git a/ts/session/constants.ts b/ts/session/constants.ts
index 72602b720..806061e31 100644
--- a/ts/session/constants.ts
+++ b/ts/session/constants.ts
@@ -51,8 +51,9 @@ export const CONVERSATION = {
// Maximum voice message duraton of 5 minutes
// which equates to 1.97 MB
MAX_VOICE_MESSAGE_DURATION: 300,
- MAX_UNREAD_COUNT: 999,
-};
+ MAX_CONVO_UNREAD_COUNT: 999,
+ MAX_GLOBAL_UNREAD_COUNT: 99, // the global one does not look good with 4 digits (999+) so we have a smaller one for it
+} as const;
/**
* The file server and onion request max upload size is 10MB precisely.
diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts
index 16a46412b..5babd3893 100644
--- a/ts/state/selectors/conversations.ts
+++ b/ts/state/selectors/conversations.ts
@@ -336,7 +336,6 @@ const _getGlobalUnreadCount = (sortedConversations: Array
}
if (
- globalUnreadCount < 100 &&
isNumber(conversation.unreadCount) &&
isFinite(conversation.unreadCount) &&
conversation.unreadCount > 0 &&
@@ -345,7 +344,6 @@ const _getGlobalUnreadCount = (sortedConversations: Array
globalUnreadCount += conversation.unreadCount;
}
}
-
return globalUnreadCount;
};
diff --git a/ts/state/selectors/selectedConversation.ts b/ts/state/selectors/selectedConversation.ts
index 5aca5a79f..3f4b2bf3c 100644
--- a/ts/state/selectors/selectedConversation.ts
+++ b/ts/state/selectors/selectedConversation.ts
@@ -1,5 +1,6 @@
import { isString } from 'lodash';
import { useSelector } from 'react-redux';
+import { useUnreadCount } from '../../hooks/useParamSelector';
import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes';
import {
DisappearingMessageConversationModeType,
@@ -302,6 +303,11 @@ export function useSelectedIsActive() {
return useSelector(getIsSelectedActive);
}
+export function useSelectedUnreadCount() {
+ const selectedConversation = useSelectedConversationKey();
+ return useUnreadCount(selectedConversation);
+}
+
export function useSelectedIsNoteToSelf() {
return useSelector(getIsSelectedNoteToSelf);
}