Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 8905c5d

Browse files
toger5jryansturt2live
authored
Add unread indicator to the timelineCard header icon (#7156)
Co-authored-by: J. Ryan Stinnett <[email protected]> Co-authored-by: Travis Ralston <[email protected]>
1 parent 766d1ee commit 8905c5d

File tree

3 files changed

+92
-33
lines changed

3 files changed

+92
-33
lines changed

res/css/structures/_RightPanel.scss

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -103,44 +103,61 @@ limitations under the License.
103103
mask-position: center;
104104
}
105105

106-
$dot-size: 8px;
106+
$dot-size: 7px;
107107
$pulse-color: $alert;
108108

109109
.mx_RightPanel_pinnedMessagesButton {
110110
&::before {
111111
mask-image: url('$(res)/img/element-icons/room/pin.svg');
112112
mask-position: center;
113113
}
114+
}
115+
.mx_RightPanel_headerButton_unreadIndicator_bg {
116+
position: absolute;
117+
right: 0;
118+
top: 0;
119+
margin: 4px;
120+
width: $dot-size;
121+
height: $dot-size;
122+
border-radius: 50%;
123+
transform: scale(1.6);
124+
transform-origin: center center;
125+
background: rgba($background, 1);
126+
}
114127

115-
.mx_RightPanel_pinnedMessagesButton_unreadIndicator {
128+
.mx_RightPanel_headerButton_unreadIndicator {
129+
position: absolute;
130+
right: 0;
131+
top: 0;
132+
margin: 4px;
133+
width: $dot-size;
134+
height: $dot-size;
135+
border-radius: 50%;
136+
transform: scale(1);
137+
background: rgba($pulse-color, 1);
138+
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
139+
animation: mx_RightPanel_indicator_pulse 2s infinite;
140+
animation-iteration-count: 1;
141+
142+
&.mx_Indicator_gray {
143+
background: rgba($input-darker-fg-color, 1);
144+
box-shadow: rgba($input-darker-fg-color, 1);
145+
}
146+
147+
&::after {
148+
content: "";
116149
position: absolute;
117-
right: 0;
150+
width: inherit;
151+
height: inherit;
118152
top: 0;
119-
margin: 4px;
120-
width: $dot-size;
121-
height: $dot-size;
122-
border-radius: 50%;
153+
left: 0;
123154
transform: scale(1);
124-
background: rgba($pulse-color, 1);
125-
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
126-
animation: mx_RightPanel_indicator_pulse 2s infinite;
127-
animation-iteration-count: 1;
128-
129-
&::after {
130-
content: "";
131-
position: absolute;
132-
width: inherit;
133-
height: inherit;
134-
top: 0;
135-
left: 0;
136-
transform: scale(1);
137-
transform-origin: center center;
138-
animation-name: mx_RightPanel_indicator_pulse_shadow;
139-
animation-duration: inherit;
140-
animation-iteration-count: inherit;
141-
border-radius: 50%;
142-
background: rgba($pulse-color, 1);
143-
}
155+
transform-origin: center center;
156+
animation-name: mx_RightPanel_indicator_pulse_shadow;
157+
animation-duration: inherit;
158+
animation-iteration-count: inherit;
159+
border-radius: 50%;
160+
background: inherit;
144161
}
145162
}
146163

src/components/views/right_panel/RoomHeaderButtons.tsx

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard';
3434
import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads";
3535
import SettingsStore from "../../../settings/SettingsStore";
3636
import dis from "../../../dispatcher/dispatcher";
37+
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
38+
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
3739

3840
const ROOM_INFO_PHASES = [
3941
RightPanelPhases.RoomSummary,
@@ -45,15 +47,32 @@ const ROOM_INFO_PHASES = [
4547
RightPanelPhases.Room3pidMemberInfo,
4648
];
4749

48-
const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
50+
interface IUnreadIndicatorProps {
51+
className: string;
52+
}
53+
54+
const UnreadIndicator = ({ className }: IUnreadIndicatorProps) => {
55+
return <React.Fragment>
56+
<div className="mx_RightPanel_headerButton_unreadIndicator_bg" />
57+
<div className={className} />
58+
</React.Fragment>;
59+
};
60+
61+
interface IHeaderButtonProps {
62+
room: Room;
63+
isHighlighted: boolean;
64+
onClick: () => void;
65+
}
66+
67+
const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }: IHeaderButtonProps) => {
4968
const pinningEnabled = useSettingValue("feature_pinning");
5069
const pinnedEvents = usePinnedEvents(pinningEnabled && room);
5170
const readPinnedEvents = useReadPinnedEvents(pinningEnabled && room);
5271
if (!pinningEnabled) return null;
5372

5473
let unreadIndicator;
5574
if (pinnedEvents.some(id => !readPinnedEvents.has(id))) {
56-
unreadIndicator = <div className="mx_RightPanel_pinnedMessagesButton_unreadIndicator" />;
75+
unreadIndicator = <UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator" />;
5776
}
5877

5978
return <HeaderButton
@@ -67,16 +86,30 @@ const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
6786
</HeaderButton>;
6887
};
6988

70-
const TimelineCardHeaderButton = ({ room, isHighlighted, onClick }) => {
89+
const TimelineCardHeaderButton = ({ room, isHighlighted, onClick }: IHeaderButtonProps) => {
7190
if (!SettingsStore.getValue("feature_maximised_widgets")) return null;
72-
91+
let unreadIndicator;
92+
switch (RoomNotificationStateStore.instance.getRoomState(room).color) {
93+
case NotificationColor.Grey:
94+
unreadIndicator =
95+
<UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator mx_Indicator_gray" />;
96+
break;
97+
case NotificationColor.Red:
98+
unreadIndicator =
99+
<UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator" />;
100+
break;
101+
default:
102+
break;
103+
}
73104
return <HeaderButton
74105
name="timelineCardButton"
75106
title={_t("Chat")}
76107
isHighlighted={isHighlighted}
77108
onClick={onClick}
78109
analytics={["Right Panel", "Timeline Panel Button", "click"]}
79-
/>;
110+
>
111+
{ unreadIndicator }
112+
</HeaderButton>;
80113
};
81114

82115
interface IProps {

src/components/views/rooms/RoomHeader.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import { SearchScope } from './SearchBar';
3939
import { ContextMenuTooltipButton } from '../../structures/ContextMenu';
4040
import RoomContextMenu from "../context_menus/RoomContextMenu";
4141
import { contextMenuBelow } from './RoomTile';
42+
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
43+
import { NOTIFICATION_STATE_UPDATE } from '../../../stores/notifications/NotificationState';
4244
import { RightPanelPhases } from '../../../stores/RightPanelStorePhases';
4345

4446
export interface ISearchInfo {
@@ -75,7 +77,8 @@ export default class RoomHeader extends React.Component<IProps, IState> {
7577

7678
constructor(props, context) {
7779
super(props, context);
78-
80+
const notiStore = RoomNotificationStateStore.instance.getRoomState(props.room);
81+
notiStore.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
7982
this.state = {};
8083
}
8184

@@ -89,6 +92,8 @@ export default class RoomHeader extends React.Component<IProps, IState> {
8992
if (cli) {
9093
cli.removeListener("RoomState.events", this.onRoomStateEvents);
9194
}
95+
const notiStore = RoomNotificationStateStore.instance.getRoomState(this.props.room);
96+
notiStore.removeListener(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
9297
}
9398

9499
private onRoomStateEvents = (event: MatrixEvent, state: RoomState) => {
@@ -100,6 +105,10 @@ export default class RoomHeader extends React.Component<IProps, IState> {
100105
this.rateLimitedUpdate();
101106
};
102107

108+
private onNotificationUpdate = () => {
109+
this.forceUpdate();
110+
};
111+
103112
private rateLimitedUpdate = throttle(() => {
104113
this.forceUpdate();
105114
}, 500, { leading: true, trailing: true });

0 commit comments

Comments
 (0)