diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index ac727dab39c..86628a5d9e4 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -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; } } diff --git a/src/components/views/right_panel/RoomHeaderButtons.tsx b/src/components/views/right_panel/RoomHeaderButtons.tsx index 7e8ce5edf9a..a9a0b0e7c3e 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.tsx +++ b/src/components/views/right_panel/RoomHeaderButtons.tsx @@ -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 +
+
+ ; +}; + +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 =
; + unreadIndicator = ; } return { ; }; -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 = + ; + break; + case NotificationColor.Red: + unreadIndicator = + ; + break; + default: + break; + } return ; + > + { unreadIndicator } + ; }; interface IProps { diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index 2fda3decad9..7bce296e8f7 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -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 { 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 { 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 { this.rateLimitedUpdate(); }; + private onNotificationUpdate = () => { + this.forceUpdate(); + }; + private rateLimitedUpdate = throttle(() => { this.forceUpdate(); }, 500, { leading: true, trailing: true });