Add unread indicator to the timelineCard header icon (#7156)

Co-authored-by: J. Ryan Stinnett <jryans@gmail.com>
Co-authored-by: Travis Ralston <travisr@matrix.org>
This commit is contained in:
Timo 2021-11-30 11:06:20 +01:00 committed by GitHub
parent 766d1ee3e8
commit 8905c5d2bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 33 deletions

View file

@ -103,7 +103,7 @@ limitations under the License.
mask-position: center;
}
$dot-size: 8px;
$dot-size: 7px;
$pulse-color: $alert;
.mx_RightPanel_pinnedMessagesButton {
@ -111,36 +111,53 @@ $pulse-color: $alert;
mask-image: url('$(res)/img/element-icons/room/pin.svg');
mask-position: center;
}
}
.mx_RightPanel_headerButton_unreadIndicator_bg {
position: absolute;
right: 0;
top: 0;
margin: 4px;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
transform: scale(1.6);
transform-origin: center center;
background: rgba($background, 1);
}
.mx_RightPanel_pinnedMessagesButton_unreadIndicator {
.mx_RightPanel_headerButton_unreadIndicator {
position: absolute;
right: 0;
top: 0;
margin: 4px;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
transform: scale(1);
background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_RightPanel_indicator_pulse 2s infinite;
animation-iteration-count: 1;
&.mx_Indicator_gray {
background: rgba($input-darker-fg-color, 1);
box-shadow: rgba($input-darker-fg-color, 1);
}
&::after {
content: "";
position: absolute;
right: 0;
width: inherit;
height: inherit;
top: 0;
margin: 4px;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
left: 0;
transform: scale(1);
background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_RightPanel_indicator_pulse 2s infinite;
animation-iteration-count: 1;
&::after {
content: "";
position: absolute;
width: inherit;
height: inherit;
top: 0;
left: 0;
transform: scale(1);
transform-origin: center center;
animation-name: mx_RightPanel_indicator_pulse_shadow;
animation-duration: inherit;
animation-iteration-count: inherit;
border-radius: 50%;
background: rgba($pulse-color, 1);
}
transform-origin: center center;
animation-name: mx_RightPanel_indicator_pulse_shadow;
animation-duration: inherit;
animation-iteration-count: inherit;
border-radius: 50%;
background: inherit;
}
}

View file

@ -34,6 +34,8 @@ import { useReadPinnedEvents, usePinnedEvents } from './PinnedMessagesCard';
import { dispatchShowThreadsPanelEvent } from "../../../dispatcher/dispatch-actions/threads";
import SettingsStore from "../../../settings/SettingsStore";
import dis from "../../../dispatcher/dispatcher";
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
const ROOM_INFO_PHASES = [
RightPanelPhases.RoomSummary,
@ -45,7 +47,24 @@ const ROOM_INFO_PHASES = [
RightPanelPhases.Room3pidMemberInfo,
];
const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
interface IUnreadIndicatorProps {
className: string;
}
const UnreadIndicator = ({ className }: IUnreadIndicatorProps) => {
return <React.Fragment>
<div className="mx_RightPanel_headerButton_unreadIndicator_bg" />
<div className={className} />
</React.Fragment>;
};
interface IHeaderButtonProps {
room: Room;
isHighlighted: boolean;
onClick: () => void;
}
const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }: IHeaderButtonProps) => {
const pinningEnabled = useSettingValue("feature_pinning");
const pinnedEvents = usePinnedEvents(pinningEnabled && room);
const readPinnedEvents = useReadPinnedEvents(pinningEnabled && room);
@ -53,7 +72,7 @@ const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
let unreadIndicator;
if (pinnedEvents.some(id => !readPinnedEvents.has(id))) {
unreadIndicator = <div className="mx_RightPanel_pinnedMessagesButton_unreadIndicator" />;
unreadIndicator = <UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator" />;
}
return <HeaderButton
@ -67,16 +86,30 @@ const PinnedMessagesHeaderButton = ({ room, isHighlighted, onClick }) => {
</HeaderButton>;
};
const TimelineCardHeaderButton = ({ room, isHighlighted, onClick }) => {
const TimelineCardHeaderButton = ({ room, isHighlighted, onClick }: IHeaderButtonProps) => {
if (!SettingsStore.getValue("feature_maximised_widgets")) return null;
let unreadIndicator;
switch (RoomNotificationStateStore.instance.getRoomState(room).color) {
case NotificationColor.Grey:
unreadIndicator =
<UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator mx_Indicator_gray" />;
break;
case NotificationColor.Red:
unreadIndicator =
<UnreadIndicator className="mx_RightPanel_headerButton_unreadIndicator" />;
break;
default:
break;
}
return <HeaderButton
name="timelineCardButton"
title={_t("Chat")}
isHighlighted={isHighlighted}
onClick={onClick}
analytics={["Right Panel", "Timeline Panel Button", "click"]}
/>;
>
{ unreadIndicator }
</HeaderButton>;
};
interface IProps {

View file

@ -39,6 +39,8 @@ import { SearchScope } from './SearchBar';
import { ContextMenuTooltipButton } from '../../structures/ContextMenu';
import RoomContextMenu from "../context_menus/RoomContextMenu";
import { contextMenuBelow } from './RoomTile';
import { RoomNotificationStateStore } from '../../../stores/notifications/RoomNotificationStateStore';
import { NOTIFICATION_STATE_UPDATE } from '../../../stores/notifications/NotificationState';
import { RightPanelPhases } from '../../../stores/RightPanelStorePhases';
export interface ISearchInfo {
@ -75,7 +77,8 @@ export default class RoomHeader extends React.Component<IProps, IState> {
constructor(props, context) {
super(props, context);
const notiStore = RoomNotificationStateStore.instance.getRoomState(props.room);
notiStore.on(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
this.state = {};
}
@ -89,6 +92,8 @@ export default class RoomHeader extends React.Component<IProps, IState> {
if (cli) {
cli.removeListener("RoomState.events", this.onRoomStateEvents);
}
const notiStore = RoomNotificationStateStore.instance.getRoomState(this.props.room);
notiStore.removeListener(NOTIFICATION_STATE_UPDATE, this.onNotificationUpdate);
}
private onRoomStateEvents = (event: MatrixEvent, state: RoomState) => {
@ -100,6 +105,10 @@ export default class RoomHeader extends React.Component<IProps, IState> {
this.rateLimitedUpdate();
};
private onNotificationUpdate = () => {
this.forceUpdate();
};
private rateLimitedUpdate = throttle(() => {
this.forceUpdate();
}, 500, { leading: true, trailing: true });