mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-16 20:20:29 +03:00
Added unread symbol for Spaces, DMs and Home (#82)
This commit is contained in:
parent
fc0dc8aea0
commit
b07c50e580
14 changed files with 229 additions and 108 deletions
|
|
@ -12,7 +12,7 @@ import { AtoZ } from './common';
|
|||
|
||||
const drawerPostie = new Postie();
|
||||
function Directs() {
|
||||
const { roomList } = initMatrix;
|
||||
const { roomList, notifications } = initMatrix;
|
||||
const directIds = [...roomList.directs].sort(AtoZ);
|
||||
|
||||
const [, forceUpdate] = useState({});
|
||||
|
|
@ -26,10 +26,11 @@ function Directs() {
|
|||
drawerPostie.post('selector-change', addresses, selectedRoomId);
|
||||
}
|
||||
|
||||
function unreadChanged(roomId) {
|
||||
if (!drawerPostie.hasTopic('unread-change')) return;
|
||||
if (!drawerPostie.hasSubscriber('unread-change', roomId)) return;
|
||||
drawerPostie.post('unread-change', roomId);
|
||||
function notiChanged(roomId, total, prevTotal) {
|
||||
if (total === prevTotal) return;
|
||||
if (drawerPostie.hasTopicAndSubscriber('unread-change', roomId)) {
|
||||
drawerPostie.post('unread-change', roomId);
|
||||
}
|
||||
}
|
||||
|
||||
function roomListUpdated() {
|
||||
|
|
@ -47,13 +48,11 @@ function Directs() {
|
|||
useEffect(() => {
|
||||
roomList.on(cons.events.roomList.ROOMLIST_UPDATED, roomListUpdated);
|
||||
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||
roomList.on(cons.events.roomList.MY_RECEIPT_ARRIVED, unreadChanged);
|
||||
roomList.on(cons.events.roomList.EVENT_ARRIVED, unreadChanged);
|
||||
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||
return () => {
|
||||
roomList.removeListener(cons.events.roomList.ROOMLIST_UPDATED, roomListUpdated);
|
||||
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||
roomList.removeListener(cons.events.roomList.MY_RECEIPT_ARRIVED, unreadChanged);
|
||||
roomList.removeListener(cons.events.roomList.EVENT_ARRIVED, unreadChanged);
|
||||
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './DrawerBreadcrumb.scss';
|
||||
|
||||
|
|
@ -6,51 +6,108 @@ import initMatrix from '../../../client/initMatrix';
|
|||
import cons from '../../../client/state/cons';
|
||||
import { selectSpace } from '../../../client/action/navigation';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { abbreviateNumber } from '../../../util/common';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import RawIcon from '../../atoms/system-icons/RawIcon';
|
||||
import Button from '../../atoms/button/Button';
|
||||
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||
import NotificationBadge from '../../atoms/badge/NotificationBadge';
|
||||
|
||||
import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg';
|
||||
|
||||
function DrawerBreadcrumb({ spaceId }) {
|
||||
const [, forceUpdate] = useState({});
|
||||
const scrollRef = useRef(null);
|
||||
const { roomList, notifications } = initMatrix;
|
||||
const mx = initMatrix.matrixClient;
|
||||
const spacePath = navigation.selectedSpacePath;
|
||||
|
||||
function onNotiChanged(roomId, total, prevTotal) {
|
||||
if (total === prevTotal) return;
|
||||
if (navigation.selectedSpacePath.includes(roomId)) {
|
||||
forceUpdate({});
|
||||
}
|
||||
if (navigation.selectedSpacePath[0] === cons.tabs.HOME) {
|
||||
if (!roomList.isOrphan(roomId)) return;
|
||||
if (roomList.directs.has(roomId)) return;
|
||||
forceUpdate({});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
if (scrollRef?.current === null) return;
|
||||
scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
|
||||
});
|
||||
notifications.on(cons.events.notifications.NOTI_CHANGED, onNotiChanged);
|
||||
return () => {
|
||||
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, onNotiChanged);
|
||||
};
|
||||
}, [spaceId]);
|
||||
|
||||
if (spacePath.length === 1) return null;
|
||||
|
||||
function getHomeNotiExcept(childId) {
|
||||
const orphans = roomList.getOrphans();
|
||||
const childIndex = orphans.indexOf(childId);
|
||||
if (childId !== -1) orphans.splice(childIndex, 1);
|
||||
|
||||
let noti = null;
|
||||
|
||||
orphans.forEach((roomId) => {
|
||||
if (!notifications.hasNoti(roomId)) return;
|
||||
if (noti === null) noti = { total: 0, highlight: 0 };
|
||||
const childNoti = notifications.getNoti(roomId);
|
||||
noti.total += childNoti.total;
|
||||
noti.highlight += childNoti.highlight;
|
||||
});
|
||||
|
||||
return noti;
|
||||
}
|
||||
|
||||
function getNotiExcept(roomId, childId) {
|
||||
if (!notifications.hasNoti(roomId)) return null;
|
||||
|
||||
const noti = notifications.getNoti(roomId);
|
||||
if (!notifications.hasNoti(childId)) return noti;
|
||||
if (noti.from === null) return noti;
|
||||
if (noti.from.has(childId) && noti.from.size === 1) return null;
|
||||
|
||||
const childNoti = notifications.getNoti(childId);
|
||||
|
||||
return {
|
||||
total: noti.total - childNoti.total,
|
||||
highlight: noti.highlight - childNoti.highlight,
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="breadcrumb__wrapper">
|
||||
<ScrollView ref={scrollRef} horizontal vertical={false} invisible>
|
||||
<div className="breadcrumb">
|
||||
{
|
||||
spacePath.map((id, index) => {
|
||||
if (index === 0) {
|
||||
return (
|
||||
<Button key={id} onClick={() => selectSpace(id)}>
|
||||
<Text variant="b2">{id === cons.tabs.HOME ? 'Home' : mx.getRoom(id).name}</Text>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
const noti = (id !== cons.tabs.HOME && index < spacePath.length)
|
||||
? getNotiExcept(id, (index === spacePath.length - 1) ? null : spacePath[index + 1])
|
||||
: getHomeNotiExcept((index === spacePath.length - 1) ? null : spacePath[index + 1]);
|
||||
|
||||
return (
|
||||
<React.Fragment
|
||||
key={id}
|
||||
>
|
||||
<RawIcon size="extra-small" src={ChevronRightIC} />
|
||||
{ index !== 0 && <RawIcon size="extra-small" src={ChevronRightIC} />}
|
||||
<Button
|
||||
className={index === spacePath.length - 1 ? 'breadcrumb__btn--selected' : ''}
|
||||
onClick={() => selectSpace(id)}
|
||||
>
|
||||
<Text variant="b2">{ mx.getRoom(id).name }</Text>
|
||||
<Text variant="b2">{id === cons.tabs.HOME ? 'Home' : mx.getRoom(id).name}</Text>
|
||||
{ noti !== null && (
|
||||
<NotificationBadge
|
||||
alert={noti.highlight !== 0}
|
||||
content={noti.total > 0 ? abbreviateNumber(noti.total) : null}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,14 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
& .notification-badge {
|
||||
margin-left: var(--sp-extra-tight);
|
||||
[dir=rtl] & {
|
||||
margin-left: 0;
|
||||
margin-right: var(--sp-extra-tight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__btn--selected {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { AtoZ } from './common';
|
|||
const drawerPostie = new Postie();
|
||||
function Home({ spaceId }) {
|
||||
const [, forceUpdate] = useState({});
|
||||
const { roomList } = initMatrix;
|
||||
const { roomList, notifications } = initMatrix;
|
||||
let spaceIds = [];
|
||||
let roomIds = [];
|
||||
let directIds = [];
|
||||
|
|
@ -40,10 +40,11 @@ function Home({ spaceId }) {
|
|||
if (addresses.length === 0) return;
|
||||
drawerPostie.post('selector-change', addresses, selectedRoomId);
|
||||
}
|
||||
function unreadChanged(roomId) {
|
||||
if (!drawerPostie.hasTopic('unread-change')) return;
|
||||
if (!drawerPostie.hasSubscriber('unread-change', roomId)) return;
|
||||
drawerPostie.post('unread-change', roomId);
|
||||
function notiChanged(roomId, total, prevTotal) {
|
||||
if (total === prevTotal) return;
|
||||
if (drawerPostie.hasTopicAndSubscriber('unread-change', roomId)) {
|
||||
drawerPostie.post('unread-change', roomId);
|
||||
}
|
||||
}
|
||||
|
||||
function roomListUpdated() {
|
||||
|
|
@ -61,13 +62,11 @@ function Home({ spaceId }) {
|
|||
useEffect(() => {
|
||||
roomList.on(cons.events.roomList.ROOMLIST_UPDATED, roomListUpdated);
|
||||
navigation.on(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||
roomList.on(cons.events.roomList.MY_RECEIPT_ARRIVED, unreadChanged);
|
||||
roomList.on(cons.events.roomList.EVENT_ARRIVED, unreadChanged);
|
||||
notifications.on(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||
return () => {
|
||||
roomList.removeListener(cons.events.roomList.ROOMLIST_UPDATED, roomListUpdated);
|
||||
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, selectorChanged);
|
||||
roomList.removeListener(cons.events.roomList.MY_RECEIPT_ARRIVED, unreadChanged);
|
||||
roomList.removeListener(cons.events.roomList.EVENT_ARRIVED, unreadChanged);
|
||||
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, notiChanged);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ import React, { useState, useEffect } from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import { doesRoomHaveUnread } from '../../../util/matrixUtil';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { openRoomOptions } from '../../../client/action/navigation';
|
||||
import { createSpaceShortcut, deleteSpaceShortcut } from '../../../client/action/room';
|
||||
import { getEventCords } from '../../../util/common';
|
||||
import { getEventCords, abbreviateNumber } from '../../../util/common';
|
||||
|
||||
import IconButton from '../../atoms/button/IconButton';
|
||||
import RoomSelector from '../../molecules/room-selector/RoomSelector';
|
||||
|
|
@ -24,6 +23,7 @@ function Selector({
|
|||
roomId, isDM, drawerPostie, onClick,
|
||||
}) {
|
||||
const mx = initMatrix.matrixClient;
|
||||
const noti = initMatrix.notifications;
|
||||
const room = mx.getRoom(roomId);
|
||||
let imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
||||
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
|
||||
|
|
@ -54,9 +54,9 @@ function Selector({
|
|||
name={room.name}
|
||||
roomId={roomId}
|
||||
iconSrc={room.getJoinRule() === 'invite' ? SpaceLockIC : SpaceIC}
|
||||
isUnread={doesRoomHaveUnread(room)}
|
||||
notificationCount={room.getUnreadNotificationCount('total') || 0}
|
||||
isAlert={room.getUnreadNotificationCount('highlight') !== 0}
|
||||
isUnread={noti.hasNoti(roomId)}
|
||||
notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))}
|
||||
isAlert={noti.getHighlightNoti(roomId) !== 0}
|
||||
onClick={onClick}
|
||||
options={(
|
||||
<IconButton
|
||||
|
|
@ -85,9 +85,9 @@ function Selector({
|
|||
// eslint-disable-next-line no-nested-ternary
|
||||
iconSrc={isDM ? null : room.getJoinRule() === 'invite' ? HashLockIC : HashIC}
|
||||
isSelected={isSelected}
|
||||
isUnread={doesRoomHaveUnread(room)}
|
||||
notificationCount={room.getUnreadNotificationCount('total') || 0}
|
||||
isAlert={room.getUnreadNotificationCount('highlight') !== 0}
|
||||
isUnread={noti.hasNoti(roomId)}
|
||||
notificationCount={abbreviateNumber(noti.getTotalNoti(roomId))}
|
||||
isAlert={noti.getHighlightNoti(roomId) !== 0}
|
||||
onClick={onClick}
|
||||
options={(
|
||||
<IconButton
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
selectTab, openInviteList, openPublicRooms, openSettings,
|
||||
} from '../../../client/action/navigation';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { abbreviateNumber } from '../../../util/common';
|
||||
|
||||
import ScrollView from '../../atoms/scroll/ScrollView';
|
||||
import SidebarAvatar from '../../molecules/sidebar-avatar/SidebarAvatar';
|
||||
|
|
@ -55,7 +56,7 @@ function ProfileAvatarMenu() {
|
|||
}
|
||||
|
||||
function SideBar() {
|
||||
const { roomList } = initMatrix;
|
||||
const { roomList, notifications } = initMatrix;
|
||||
const mx = initMatrix.matrixClient;
|
||||
const totalInviteCount = () => roomList.inviteRooms.size
|
||||
+ roomList.inviteSpaces.size
|
||||
|
|
@ -74,33 +75,83 @@ function SideBar() {
|
|||
function onSpaceShortcutUpdated() {
|
||||
forceUpdate({});
|
||||
}
|
||||
function onNotificationChanged(roomId, total, prevTotal) {
|
||||
if (total === prevTotal) return;
|
||||
forceUpdate({});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
navigation.on(cons.events.navigation.TAB_SELECTED, onTabSelected);
|
||||
roomList.on(cons.events.roomList.SPACE_SHORTCUT_UPDATED, onSpaceShortcutUpdated);
|
||||
initMatrix.roomList.on(
|
||||
cons.events.roomList.INVITELIST_UPDATED,
|
||||
onInviteListChange,
|
||||
);
|
||||
roomList.on(cons.events.roomList.INVITELIST_UPDATED, onInviteListChange);
|
||||
notifications.on(cons.events.notifications.NOTI_CHANGED, onNotificationChanged);
|
||||
|
||||
return () => {
|
||||
navigation.removeListener(cons.events.navigation.TAB_SELECTED, onTabSelected);
|
||||
roomList.removeListener(cons.events.roomList.SPACE_SHORTCUT_UPDATED, onSpaceShortcutUpdated);
|
||||
initMatrix.roomList.removeListener(
|
||||
cons.events.roomList.INVITELIST_UPDATED,
|
||||
onInviteListChange,
|
||||
);
|
||||
roomList.removeListener(cons.events.roomList.INVITELIST_UPDATED, onInviteListChange);
|
||||
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, onNotificationChanged);
|
||||
};
|
||||
}, []);
|
||||
|
||||
function getHomeNoti() {
|
||||
const orphans = roomList.getOrphans();
|
||||
let noti = null;
|
||||
|
||||
orphans.forEach((roomId) => {
|
||||
if (!notifications.hasNoti(roomId)) return;
|
||||
if (noti === null) noti = { total: 0, highlight: 0 };
|
||||
const childNoti = notifications.getNoti(roomId);
|
||||
noti.total += childNoti.total;
|
||||
noti.highlight += childNoti.highlight;
|
||||
});
|
||||
|
||||
return noti;
|
||||
}
|
||||
function getDMsNoti() {
|
||||
if (roomList.directs.size === 0) return null;
|
||||
let noti = null;
|
||||
|
||||
[...roomList.directs].forEach((roomId) => {
|
||||
if (!notifications.hasNoti(roomId)) return;
|
||||
if (noti === null) noti = { total: 0, highlight: 0 };
|
||||
const childNoti = notifications.getNoti(roomId);
|
||||
noti.total += childNoti.total;
|
||||
noti.highlight += childNoti.highlight;
|
||||
});
|
||||
|
||||
return noti;
|
||||
}
|
||||
|
||||
// TODO: bellow operations are heavy.
|
||||
// refactor this component into more smaller components.
|
||||
const dmsNoti = getDMsNoti();
|
||||
const homeNoti = getHomeNoti();
|
||||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
<div className="sidebar__scrollable">
|
||||
<ScrollView invisible>
|
||||
<div className="scrollable-content">
|
||||
<div className="featured-container">
|
||||
<SidebarAvatar active={selectedTab === cons.tabs.HOME} onClick={() => selectTab(cons.tabs.HOME)} tooltip="Home" iconSrc={HomeIC} />
|
||||
<SidebarAvatar active={selectedTab === cons.tabs.DIRECTS} onClick={() => selectTab(cons.tabs.DIRECTS)} tooltip="People" iconSrc={UserIC} />
|
||||
<SidebarAvatar
|
||||
active={selectedTab === cons.tabs.HOME}
|
||||
onClick={() => selectTab(cons.tabs.HOME)}
|
||||
tooltip="Home"
|
||||
iconSrc={HomeIC}
|
||||
isUnread={homeNoti !== null}
|
||||
notificationCount={homeNoti !== null ? abbreviateNumber(homeNoti.total) : 0}
|
||||
isAlert={homeNoti?.highlight > 0}
|
||||
/>
|
||||
<SidebarAvatar
|
||||
active={selectedTab === cons.tabs.DIRECTS}
|
||||
onClick={() => selectTab(cons.tabs.DIRECTS)}
|
||||
tooltip="People"
|
||||
iconSrc={UserIC}
|
||||
isUnread={dmsNoti !== null}
|
||||
notificationCount={dmsNoti !== null ? abbreviateNumber(dmsNoti.total) : 0}
|
||||
isAlert={dmsNoti?.highlight > 0}
|
||||
/>
|
||||
<SidebarAvatar onClick={() => openPublicRooms()} tooltip="Public rooms" iconSrc={HashSearchIC} />
|
||||
</div>
|
||||
<div className="sidebar-divider" />
|
||||
|
|
@ -117,6 +168,9 @@ function SideBar() {
|
|||
bgColor={colorMXID(room.roomId)}
|
||||
imageSrc={room.getAvatarUrl(initMatrix.matrixClient.baseUrl, 42, 42, 'crop') || null}
|
||||
text={room.name.slice(0, 1)}
|
||||
isUnread={notifications.hasNoti(sRoomId)}
|
||||
notificationCount={abbreviateNumber(notifications.getTotalNoti(sRoomId))}
|
||||
isAlert={notifications.getHighlightNoti(sRoomId) !== 0}
|
||||
onClick={() => selectTab(shortcut)}
|
||||
/>
|
||||
);
|
||||
|
|
@ -131,7 +185,9 @@ function SideBar() {
|
|||
<div className="sticky-container">
|
||||
{ totalInvites !== 0 && (
|
||||
<SidebarAvatar
|
||||
notifyCount={totalInvites}
|
||||
isUnread
|
||||
notificationCount={totalInvites}
|
||||
isAlert
|
||||
onClick={() => openInviteList()}
|
||||
tooltip="Invites"
|
||||
iconSrc={InviteIC}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue