Show unsent message warning on Space panel button
This commit is contained in:
parent
329bc8a89e
commit
bbe714257e
6 changed files with 43 additions and 9 deletions
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { MouseEvent } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { formatCount } from "../../../utils/FormattingUtils";
|
import { formatCount } from "../../../utils/FormattingUtils";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -22,6 +22,9 @@ import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { XOR } from "../../../@types/common";
|
import { XOR } from "../../../@types/common";
|
||||||
import { NOTIFICATION_STATE_UPDATE, NotificationState } from "../../../stores/notifications/NotificationState";
|
import { NOTIFICATION_STATE_UPDATE, NotificationState } from "../../../stores/notifications/NotificationState";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import Tooltip from "../elements/Tooltip";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
notification: NotificationState;
|
notification: NotificationState;
|
||||||
|
@ -39,6 +42,7 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IClickableProps extends IProps, React.InputHTMLAttributes<Element> {
|
interface IClickableProps extends IProps, React.InputHTMLAttributes<Element> {
|
||||||
|
showUnsentTooltip?: boolean;
|
||||||
/**
|
/**
|
||||||
* If specified will return an AccessibleButton instead of a div.
|
* If specified will return an AccessibleButton instead of a div.
|
||||||
*/
|
*/
|
||||||
|
@ -47,6 +51,7 @@ interface IClickableProps extends IProps, React.InputHTMLAttributes<Element> {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
showCounts: boolean; // whether or not to show counts. Independent of props.forceCount
|
showCounts: boolean; // whether or not to show counts. Independent of props.forceCount
|
||||||
|
showTooltip: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.NotificationBadge")
|
@replaceableComponent("views.rooms.NotificationBadge")
|
||||||
|
@ -59,6 +64,7 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showCounts: SettingsStore.getValue("Notifications.alwaysShowBadgeCounts", this.roomId),
|
showCounts: SettingsStore.getValue("Notifications.alwaysShowBadgeCounts", this.roomId),
|
||||||
|
showTooltip: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.countWatcherRef = SettingsStore.watchSetting(
|
this.countWatcherRef = SettingsStore.watchSetting(
|
||||||
|
@ -93,9 +99,22 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
||||||
this.forceUpdate(); // notification state changed - update
|
this.forceUpdate(); // notification state changed - update
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onMouseOver = (e: MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.setState({
|
||||||
|
showTooltip: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private onMouseLeave = () => {
|
||||||
|
this.setState({
|
||||||
|
showTooltip: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
public render(): React.ReactElement {
|
public render(): React.ReactElement {
|
||||||
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||||
const { notification, forceCount, roomId, onClick, ...props } = this.props;
|
const { notification, showUnsentTooltip, forceCount, roomId, onClick, ...props } = this.props;
|
||||||
|
|
||||||
// Don't show a badge if we don't need to
|
// Don't show a badge if we don't need to
|
||||||
if (notification.isIdle) return null;
|
if (notification.isIdle) return null;
|
||||||
|
@ -124,9 +143,23 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
||||||
});
|
});
|
||||||
|
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
|
let tooltip;
|
||||||
|
if (showUnsentTooltip && this.state.showTooltip && notification.color === NotificationColor.Unsent) {
|
||||||
|
tooltip = (
|
||||||
|
<Tooltip className="mx_RoleButton_tooltip" label={_t("Message didn't send. Click for info.")} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccessibleButton {...props} className={classes} onClick={onClick}>
|
<AccessibleButton
|
||||||
|
{...props}
|
||||||
|
className={classes}
|
||||||
|
onClick={onClick}
|
||||||
|
onMouseOver={this.onMouseOver}
|
||||||
|
onMouseLeave={this.onMouseLeave}
|
||||||
|
>
|
||||||
<span className="mx_NotificationBadge_count">{ symbol }</span>
|
<span className="mx_NotificationBadge_count">{ symbol }</span>
|
||||||
|
{ tooltip }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,7 @@ export const SpaceButton: React.FC<IButtonProps> = ({
|
||||||
notification={notificationState}
|
notification={notificationState}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
|
showUnsentTooltip={true}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1587,6 +1587,7 @@
|
||||||
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.": "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.",
|
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.": "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.",
|
||||||
"Enable encryption in settings.": "Enable encryption in settings.",
|
"Enable encryption in settings.": "Enable encryption in settings.",
|
||||||
"End-to-end encryption isn't enabled": "End-to-end encryption isn't enabled",
|
"End-to-end encryption isn't enabled": "End-to-end encryption isn't enabled",
|
||||||
|
"Message didn't send. Click for info.": "Message didn't send. Click for info.",
|
||||||
"Unpin": "Unpin",
|
"Unpin": "Unpin",
|
||||||
"View message": "View message",
|
"View message": "View message",
|
||||||
"%(duration)ss": "%(duration)ss",
|
"%(duration)ss": "%(duration)ss",
|
||||||
|
|
|
@ -21,4 +21,5 @@ export enum NotificationColor {
|
||||||
Bold, // no badge, show as unread
|
Bold, // no badge, show as unread
|
||||||
Grey, // unread notified messages
|
Grey, // unread notified messages
|
||||||
Red, // unread pings
|
Red, // unread pings
|
||||||
|
Unsent, // some messages failed to send
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
||||||
|
|
||||||
if (getUnsentMessages(this.room).length > 0) {
|
if (getUnsentMessages(this.room).length > 0) {
|
||||||
// When there are unsent messages we show a red `!`
|
// When there are unsent messages we show a red `!`
|
||||||
this._color = NotificationColor.Red;
|
this._color = NotificationColor.Unsent;
|
||||||
this._symbol = "!";
|
this._symbol = "!";
|
||||||
this._count = 1; // not used, technically
|
this._count = 1; // not used, technically
|
||||||
} else if (RoomNotifs.getRoomNotifsState(this.room.roomId) === RoomNotifs.MUTE) {
|
} else if (RoomNotifs.getRoomNotifsState(this.room.roomId) === RoomNotifs.MUTE) {
|
||||||
|
|
|
@ -30,10 +30,6 @@ export class SpaceNotificationState extends NotificationState {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get symbol(): string {
|
|
||||||
return null; // This notification state doesn't support symbols
|
|
||||||
}
|
|
||||||
|
|
||||||
public setRooms(rooms: Room[]) {
|
public setRooms(rooms: Room[]) {
|
||||||
const oldRooms = this.rooms;
|
const oldRooms = this.rooms;
|
||||||
const diff = arrayDiff(oldRooms, rooms);
|
const diff = arrayDiff(oldRooms, rooms);
|
||||||
|
@ -54,7 +50,7 @@ export class SpaceNotificationState extends NotificationState {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFirstRoomWithNotifications() {
|
public getFirstRoomWithNotifications() {
|
||||||
return this.rooms.find((room) => room.getUnreadNotificationCount() > 0).roomId;
|
return Object.values(this.states).find(state => state.color >= this.color)?.room.roomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
|
@ -79,6 +75,8 @@ export class SpaceNotificationState extends NotificationState {
|
||||||
this._color = Math.max(this.color, state.color);
|
this._color = Math.max(this.color, state.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._symbol = this._color === NotificationColor.Unsent ? "!" : null;
|
||||||
|
|
||||||
// finally, publish an update if needed
|
// finally, publish an update if needed
|
||||||
this.emitIfUpdated(snapshot);
|
this.emitIfUpdated(snapshot);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue