mirror of
https://github.com/element-hq/element-web
synced 2024-11-26 19:26:04 +03:00
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:
parent
766d1ee3e8
commit
8905c5d2bb
3 changed files with 92 additions and 33 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 });
|
||||
|
|
Loading…
Reference in a new issue