Files
ThreadNet-Web/src/components/views/rooms/NotificationBadge/StatelessNotificationBadge.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

102 lines
4.0 KiB
TypeScript
Raw Normal View History

/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { forwardRef } from "react";
import classNames from "classnames";
import { formatCount } from "../../../../utils/FormattingUtils";
import AccessibleButton, { ButtonEvent } from "../../elements/AccessibleButton";
import { NotificationLevel } from "../../../../stores/notifications/NotificationLevel";
import { useSettingValue } from "../../../../hooks/useSettings";
import { XOR } from "../../../../@types/common";
interface Props {
symbol: string | null;
count: number;
level: NotificationLevel;
2023-09-19 14:24:35 +03:00
knocked?: boolean;
/**
* If true, where we would normally show a badge, we instead show a dot. No numeric count will
* be displayed (but may affect whether the the dot is displayed). See class doc
* for the difference between the two.
*/
forceDot?: boolean;
}
interface ClickableProps extends Props {
/**
* If specified will return an AccessibleButton instead of a div.
*/
onClick(ev: ButtonEvent): void;
tabIndex?: number;
}
/**
* A notification indicator that conveys what activity / notifications the user has in whatever
* context it is being used.
*
* Can either be a 'badge': a small circle with a number in it (the 'count'), or a 'dot': a smaller, empty circle.
* The two can be used to convey the same meaning but in different contexts, for example: for unread
* notifications in the room list, it may have a green badge with the number of unread notifications,
* but somewhere else it may just have a green dot as a more compact representation of the same information.
*/
export const StatelessNotificationBadge = forwardRef<HTMLDivElement, XOR<Props, ClickableProps>>(
({ symbol, count, level, knocked, forceDot = false, ...props }, ref) => {
const hideBold = useSettingValue("feature_hidebold");
// Don't show a badge if we don't need to
if ((level === NotificationLevel.None || (hideBold && level == NotificationLevel.Activity)) && !knocked) {
return <></>;
}
const hasUnreadCount = level >= NotificationLevel.Notification && (!!count || !!symbol);
const isEmptyBadge = symbol === null && count === 0;
if (symbol === null && count > 0) {
symbol = formatCount(count);
}
const classes = classNames({
mx_NotificationBadge: true,
mx_NotificationBadge_visible: isEmptyBadge || knocked ? true : hasUnreadCount,
mx_NotificationBadge_level_notification: level == NotificationLevel.Notification,
mx_NotificationBadge_level_highlight: level >= NotificationLevel.Highlight,
mx_NotificationBadge_knocked: knocked,
// At most one of mx_NotificationBadge_dot, mx_NotificationBadge_2char, mx_NotificationBadge_3char
mx_NotificationBadge_dot: (isEmptyBadge && !knocked) || forceDot,
mx_NotificationBadge_2char: !forceDot && symbol && symbol.length > 0 && symbol.length < 3,
mx_NotificationBadge_3char: !forceDot && symbol && symbol.length > 2,
});
if (props.onClick) {
return (
<AccessibleButton {...props} className={classes} onClick={props.onClick} ref={ref}>
<span className="mx_NotificationBadge_count">{symbol}</span>
{props.children}
</AccessibleButton>
);
}
return (
<div className={classes} ref={ref}>
<span className="mx_NotificationBadge_count">{symbol}</span>
</div>
);
},
);