Skip to content

Commit

Permalink
Improve jump to unread button (#396)
Browse files Browse the repository at this point in the history
* Improve jump to unread button

Signed-off-by: Ajay Bura <ajbura@gmail.com>

* Remove unused cod

Signed-off-by: Ajay Bura <ajbura@gmail.com>

* Fix mark as read not hidding jump to unread btn

Signed-off-by: Ajay Bura <ajbura@gmail.com>

* Add notification mark as read action

Signed-off-by: Ajay Bura <ajbura@gmail.com>

* Add esc as hotkey to mark room as read

Signed-off-by: Ajay Bura <ajbura@gmail.com>

* Add message icons

Signed-off-by: Ajay Bura <ajbura@gmail.com>

* Change jump to unread icon

Signed-off-by: Ajay Bura <ajbura@gmail.com>
  • Loading branch information
ajbura authored Mar 18, 2022
1 parent dc6e153 commit 8330f4f
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 62 deletions.
15 changes: 15 additions & 0 deletions public/res/ic/outlined/message-unread.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions public/res/ic/outlined/message.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 2 additions & 3 deletions src/app/molecules/room-options/RoomOptions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import { openInviteUser } from '../../../client/action/navigation';
import * as roomActions from '../../../client/action/room';
import { markAsRead } from '../../../client/action/notifications';

import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
import RoomNotification from '../room-notification/RoomNotification';
Expand All @@ -20,10 +21,8 @@ function RoomOptions({ roomId, afterOptionSelect }) {
const canInvite = room?.canInvite(mx.getUserId());

const handleMarkAsRead = () => {
markAsRead(roomId);
afterOptionSelect();
if (!room) return;
const events = room.getLiveTimeline().getEvents();
mx.sendReadReceipt(events[events.length - 1]);
};

const handleInviteClick = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/app/organisms/room/Room.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function Room() {
roomInfo.roomTimeline?.removeInternalListeners();
if (mx.getRoom(rId)) {
setRoomInfo({
roomTimeline: new RoomTimeline(rId, initMatrix.notifications),
roomTimeline: new RoomTimeline(rId),
eventId: eId ?? null,
});
} else {
Expand Down
13 changes: 7 additions & 6 deletions src/app/organisms/room/RoomViewContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import { openProfileViewer } from '../../../client/action/navigation';
import { diffMinutes, isInSameDay, Throttle } from '../../../util/common';
import { markAsRead } from '../../../client/action/notifications';

import Divider from '../../atoms/divider/Divider';
import ScrollView from '../../atoms/scroll/ScrollView';
Expand Down Expand Up @@ -253,7 +254,7 @@ function useHandleScroll(
);
roomTimeline.emit(cons.events.roomTimeline.AT_BOTTOM, isAtBottom);
if (isAtBottom && readUptoEvtStore.getItem()) {
requestAnimationFrame(() => roomTimeline.markAllAsRead());
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
});
autoPaginate();
Expand All @@ -263,7 +264,7 @@ function useHandleScroll(
const timelineScroll = timelineScrollRef.current;
const limit = eventLimitRef.current;
if (readUptoEvtStore.getItem()) {
requestAnimationFrame(() => roomTimeline.markAllAsRead());
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
if (roomTimeline.isServingLiveTimeline()) {
limit.setFrom(roomTimeline.timeline.length - limit.maxEvents);
Expand All @@ -286,7 +287,7 @@ function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, event
const limit = eventLimitRef.current;
const trySendReadReceipt = (event) => {
if (myUserId === event.getSender()) {
requestAnimationFrame(() => roomTimeline.markAllAsRead());
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
return;
}
const readUpToEvent = readUptoEvtStore.getItem();
Expand All @@ -295,7 +296,7 @@ function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, event

if (isUnread === false) {
if (document.visibilityState === 'visible' && timelineScroll.bottom < 16) {
requestAnimationFrame(() => roomTimeline.markAllAsRead());
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
} else {
readUptoEvtStore.setItem(roomTimeline.findEventByIdInTimelineSet(readUpToId));
}
Expand All @@ -305,7 +306,7 @@ function useEventArrive(roomTimeline, readUptoEvtStore, timelineScrollRef, event
const { timeline } = roomTimeline;
const unreadMsgIsLast = timeline[timeline.length - 2].getId() === readUpToId;
if (unreadMsgIsLast) {
requestAnimationFrame(() => roomTimeline.markAllAsRead());
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
};

Expand Down Expand Up @@ -399,7 +400,7 @@ function RoomViewContent({ eventId, roomTimeline }) {
if (timelineScroll.bottom < 16 && !roomTimeline.canPaginateForward()) {
const readUpToId = roomTimeline.getReadUpToEventId();
if (readUptoEvtStore.getItem()?.getId() === readUpToId || readUpToId === null) {
requestAnimationFrame(() => roomTimeline.markAllAsRead());
requestAnimationFrame(() => markAsRead(roomTimeline.roomId));
}
}
jumpToItemIndex = -1;
Expand Down
24 changes: 11 additions & 13 deletions src/app/organisms/room/RoomViewFloating.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import './RoomViewFloating.scss';

import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import { markAsRead } from '../../../client/action/notifications';

import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button';
import IconButton from '../../atoms/button/IconButton';

import MessageUnreadIC from '../../../../public/res/ic/outlined/message-unread.svg';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';

Expand All @@ -23,7 +25,7 @@ function useJumpToEvent(roomTimeline) {
};

const cancelJumpToEvent = () => {
roomTimeline.markAllAsRead();
markAsRead(roomTimeline.roomId);
setEventId(null);
};

Expand All @@ -36,11 +38,12 @@ function useJumpToEvent(roomTimeline) {
setEventId(readEventId);
}

const { notifications } = initMatrix;
const handleMarkAsRead = () => setEventId(null);
roomTimeline.on(cons.events.roomTimeline.MARKED_AS_READ, handleMarkAsRead);
notifications.on(cons.events.notifications.FULL_READ, handleMarkAsRead);

return () => {
roomTimeline.removeListener(cons.events.roomTimeline.MARKED_AS_READ, handleMarkAsRead);
notifications.removeListener(cons.events.notifications.FULL_READ, handleMarkAsRead);
setEventId(null);
};
}, [roomTimeline]);
Expand Down Expand Up @@ -96,17 +99,12 @@ function RoomViewFloating({
return (
<>
<div className={`room-view__unread ${isJumpToEvent ? 'room-view__unread--open' : ''}`}>
<Button onClick={jumpToEvent} variant="primary">
<Text variant="b2">Jump to unread</Text>
<Button iconSrc={MessageUnreadIC} onClick={jumpToEvent} variant="primary">
<Text variant="b3" weight="medium">Jump to unread messages</Text>
</Button>
<Button iconSrc={TickMarkIC} onClick={cancelJumpToEvent} variant="primary">
<Text variant="b3" weight="bold">Mark as read</Text>
</Button>
<IconButton
onClick={cancelJumpToEvent}
variant="primary"
size="extra-small"
src={TickMarkIC}
tooltipPlacement="bottom"
tooltip="Mark as read"
/>
</div>
<div className={`room-view__typing${typingMembers.size > 0 ? ' room-view__typing--open' : ''}`}>
<div className="bouncing-loader"><div /></div>
Expand Down
28 changes: 14 additions & 14 deletions src/app/organisms/room/RoomViewFloating.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,33 +90,33 @@

&__unread {
position: absolute;
top: var(--sp-extra-tight);
@include dir.prop(right, var(--sp-extra-tight), unset);
@include dir.prop(left, unset, var(--sp-extra-tight));
top: 0;
@include dir.prop(left, var(--sp-normal), unset);
@include dir.prop(right, unset, var(--sp-normal));
z-index: 999;

display: none;
width: calc(100% - var(--sp-extra-loose));
background-color: var(--bg-surface);
border-radius: var(--bo-radius);
border-radius: 0 0 var(--bo-radius) var(--bo-radius);
box-shadow: var(--bs-primary-border);
overflow: hidden;

&--open {
display: flex;
}

& .ic-btn {
padding: 6px var(--sp-extra-tight);
border-radius: 0;
}
& .btn-primary {
@extend .cp-fx__item-one;
@include dir.side(margin, 0, 1px);
justify-content: flex-start;
border-radius: 0;
padding: 0 var(--sp-tight);
&:focus {
background-color: var(--bg-primary-hover);
padding: 2px var(--sp-tight);
& .ic-raw {
width: 16px;
height: 16px;
}
}
& .btn-primary:first-child {
@extend .cp-fx__item-one;
padding: var(--sp-ultra-tight) var(--sp-extra-tight);
}
}
}
26 changes: 26 additions & 0 deletions src/client/action/notifications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import initMatrix from '../initMatrix';

// eslint-disable-next-line import/prefer-default-export
export async function markAsRead(roomId) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
if (!room) return;
initMatrix.notifications.deleteNoti(roomId);

const timeline = room.getLiveTimeline().getEvents();
const readEventId = room.getEventReadUpTo(mx.getUserId());

const getLatestValidEvent = () => {
for (let i = timeline.length - 1; i >= 0; i -= 1) {
const latestEvent = timeline[i];
if (latestEvent.getId() === readEventId) return null;
if (!latestEvent.isSending()) return latestEvent;
}
return null;
};
if (timeline.length === 0) return;
const latestEvent = getLatestValidEvent();
if (latestEvent === null) return;

await mx.sendReadReceipt(latestEvent);
}
15 changes: 12 additions & 3 deletions src/client/event/hotkeys.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { openSearch, toggleRoomSettings } from '../action/navigation';
import navigation from '../state/navigation';
import { markAsRead } from '../action/notifications';

function listenKeyboard(event) {
// Ctrl/Cmd +
Expand All @@ -18,11 +19,19 @@ function listenKeyboard(event) {
return;
}

// esc - close room settings panel
if (event.keyCode === 27 && navigation.isRoomSettings) {
toggleRoomSettings();
// esc
if (event.keyCode === 27) {
if (navigation.isRoomSettings) {
toggleRoomSettings();
return;
}
if (navigation.selectedRoomId) {
markAsRead(navigation.selectedRoomId);
return;
}
}

// Don't allow these keys to type/focus message field
if ((event.keyCode !== 8 && event.keyCode < 48)
|| (event.keyCode >= 91 && event.keyCode <= 93)
|| (event.keyCode >= 112 && event.keyCode <= 183)) {
Expand Down
22 changes: 1 addition & 21 deletions src/client/state/RoomTimeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function isTimelineLinked(tm1, tm2) {
}

class RoomTimeline extends EventEmitter {
constructor(roomId, notifications) {
constructor(roomId) {
super();
// These are local timelines
this.timeline = [];
Expand All @@ -88,7 +88,6 @@ class RoomTimeline extends EventEmitter {
this.matrixClient = initMatrix.matrixClient;
this.roomId = roomId;
this.room = this.matrixClient.getRoom(roomId);
this.notifications = notifications;

this.liveTimeline = this.room.getLiveTimeline();
this.activeTimeline = this.liveTimeline;
Expand Down Expand Up @@ -228,25 +227,6 @@ class RoomTimeline extends EventEmitter {
return Promise.allSettled(decryptionPromises);
}

markAllAsRead() {
const readEventId = this.getReadUpToEventId();
const getLatestValidEvent = () => {
for (let i = this.timeline.length - 1; i >= 0; i -= 1) {
const latestEvent = this.timeline[i];
if (latestEvent.getId() === readEventId) return null;
if (!latestEvent.isSending()) return latestEvent;
}
return null;
};
this.notifications.deleteNoti(this.roomId);
if (this.timeline.length === 0) return;
const latestEvent = getLatestValidEvent();
if (latestEvent === null) return;
if (readEventId === latestEvent.getId()) return;
this.matrixClient.sendReadReceipt(latestEvent);
this.emit(cons.events.roomTimeline.MARKED_AS_READ, latestEvent);
}

hasEventInTimeline(eventId, timeline = this.activeTimeline) {
const timelineSet = this.getUnfilteredTimelineSet();
const eventTimeline = timelineSet.getTimelineForEvent(eventId);
Expand Down
1 change: 0 additions & 1 deletion src/client/state/cons.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ const cons = {
PAGINATED: 'PAGINATED',
TYPING_MEMBERS_UPDATED: 'TYPING_MEMBERS_UPDATED',
LIVE_RECEIPT: 'LIVE_RECEIPT',
MARKED_AS_READ: 'MARKED_AS_READ',
EVENT_REDACTED: 'EVENT_REDACTED',
AT_BOTTOM: 'AT_BOTTOM',
SCROLL_TO_LIVE: 'SCROLL_TO_LIVE',
Expand Down

0 comments on commit 8330f4f

Please sign in to comment.