import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import './SideBar.scss';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import colorMXID from '../../../util/colorMXID';
import {
selectTab, openShortcutSpaces, openInviteList,
openSearch, openSettings, openReusableContextMenu,
} from '../../../client/action/navigation';
import { moveSpaceShortcut } from '../../../client/action/accountData';
import { abbreviateNumber, getEventCords } from '../../../util/common';
import { isCrossVerified } from '../../../util/matrixUtil';
import Avatar from '../../atoms/avatar/Avatar';
import NotificationBadge from '../../atoms/badge/NotificationBadge';
import ScrollView from '../../atoms/scroll/ScrollView';
import SidebarAvatar from '../../molecules/sidebar-avatar/SidebarAvatar';
import SpaceOptions from '../../molecules/space-options/SpaceOptions';
import HomeIC from '../../../../public/res/ic/outlined/home.svg';
import UserIC from '../../../../public/res/ic/outlined/user.svg';
import AddPinIC from '../../../../public/res/ic/outlined/add-pin.svg';
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import InviteIC from '../../../../public/res/ic/outlined/invite.svg';
import ShieldUserIC from '../../../../public/res/ic/outlined/shield-user.svg';
import { useSelectedTab } from '../../hooks/useSelectedTab';
import { useDeviceList } from '../../hooks/useDeviceList';
import { tabText as settingTabText } from '../settings/Settings';
function useNotificationUpdate() {
const { notifications } = initMatrix;
const [, forceUpdate] = useState({});
useEffect(() => {
function onNotificationChanged(roomId, total, prevTotal) {
if (total === prevTotal) return;
forceUpdate({});
}
notifications.on(cons.events.notifications.NOTI_CHANGED, onNotificationChanged);
return () => {
notifications.removeListener(cons.events.notifications.NOTI_CHANGED, onNotificationChanged);
};
}, []);
}
function ProfileAvatarMenu() {
const mx = initMatrix.matrixClient;
const [profile, setProfile] = useState({
avatarUrl: null,
displayName: mx.getUser(mx.getUserId()).displayName,
});
useEffect(() => {
const user = mx.getUser(mx.getUserId());
const setNewProfile = (avatarUrl, displayName) => setProfile({
avatarUrl: avatarUrl || null,
displayName: displayName || profile.displayName,
});
const onAvatarChange = (event, myUser) => {
setNewProfile(myUser.avatarUrl, myUser.displayName);
};
mx.getProfileInfo(mx.getUserId()).then((info) => {
setNewProfile(info.avatar_url, info.displayname);
});
user.on('User.avatarUrl', onAvatarChange);
return () => {
user.removeListener('User.avatarUrl', onAvatarChange);
};
}, []);
return (
)}
/>
);
}
function CrossSigninAlert() {
const deviceList = useDeviceList();
const unverified = deviceList?.filter((device) => !isCrossVerified(device.device_id));
if (!unverified?.length) return null;
return (
openSettings(settingTabText.SECURITY)}
avatar={}
/>
);
}
function FeaturedTab() {
const { roomList, accountData, notifications } = initMatrix;
const [selectedTab] = useSelectedTab();
useNotificationUpdate();
function getHomeNoti() {
const orphans = roomList.getOrphans();
let noti = null;
orphans.forEach((roomId) => {
if (accountData.spaceShortcut.has(roomId)) return;
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;
}
const dmsNoti = getDMsNoti();
const homeNoti = getHomeNoti();
return (
<>
selectTab(cons.tabs.HOME)}
avatar={}
notificationBadge={homeNoti ? (
0}
content={abbreviateNumber(homeNoti.total) || null}
/>
) : null}
/>
selectTab(cons.tabs.DIRECTS)}
avatar={}
notificationBadge={dmsNoti ? (
0}
content={abbreviateNumber(dmsNoti.total) || null}
/>
) : null}
/>
>
);
}
function DraggableSpaceShortcut({
isActive, spaceId, index, moveShortcut, onDrop,
}) {
const mx = initMatrix.matrixClient;
const { notifications } = initMatrix;
const room = mx.getRoom(spaceId);
const shortcutRef = useRef(null);
const avatarRef = useRef(null);
const openSpaceOptions = (e, sId) => {
e.preventDefault();
openReusableContextMenu(
'right',
getEventCords(e, '.sidebar-avatar'),
(closeMenu) => ,
);
};
const [, drop] = useDrop({
accept: 'SPACE_SHORTCUT',
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
};
},
drop(item) {
onDrop(item.index, item.spaceId);
},
hover(item, monitor) {
if (!shortcutRef.current) return;
const dragIndex = item.index;
const hoverIndex = index;
if (dragIndex === hoverIndex) return;
const hoverBoundingRect = shortcutRef.current?.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
moveShortcut(dragIndex, hoverIndex);
// eslint-disable-next-line no-param-reassign
item.index = hoverIndex;
},
});
const [{ isDragging }, drag] = useDrag({
type: 'SPACE_SHORTCUT',
item: () => ({ spaceId, index }),
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
drag(avatarRef);
drop(shortcutRef);
if (shortcutRef.current) {
if (isDragging) shortcutRef.current.style.opacity = 0;
else shortcutRef.current.style.opacity = 1;
}
return (
selectTab(spaceId)}
onContextMenu={(e) => openSpaceOptions(e, spaceId)}
avatar={(
)}
notificationBadge={notifications.hasNoti(spaceId) ? (
0}
content={abbreviateNumber(notifications.getTotalNoti(spaceId)) || null}
/>
) : null}
/>
);
}
DraggableSpaceShortcut.propTypes = {
spaceId: PropTypes.string.isRequired,
isActive: PropTypes.bool.isRequired,
index: PropTypes.number.isRequired,
moveShortcut: PropTypes.func.isRequired,
onDrop: PropTypes.func.isRequired,
};
function SpaceShortcut() {
const { accountData } = initMatrix;
const [selectedTab] = useSelectedTab();
useNotificationUpdate();
const [spaceShortcut, setSpaceShortcut] = useState([...accountData.spaceShortcut]);
useEffect(() => {
const handleShortcut = () => setSpaceShortcut([...accountData.spaceShortcut]);
accountData.on(cons.events.accountData.SPACE_SHORTCUT_UPDATED, handleShortcut);
return () => {
accountData.removeListener(cons.events.accountData.SPACE_SHORTCUT_UPDATED, handleShortcut);
};
}, []);
const moveShortcut = (dragIndex, hoverIndex) => {
const dragSpaceId = spaceShortcut[dragIndex];
const newShortcuts = [...spaceShortcut];
newShortcuts.splice(dragIndex, 1);
newShortcuts.splice(hoverIndex, 0, dragSpaceId);
setSpaceShortcut(newShortcuts);
};
const handleDrop = (dragIndex, dragSpaceId) => {
if ([...accountData.spaceShortcut][dragIndex] === dragSpaceId) return;
moveSpaceShortcut(dragSpaceId, dragIndex);
};
return (
{
spaceShortcut.map((shortcut, index) => (
))
}
);
}
function useTotalInvites() {
const { roomList } = initMatrix;
const totalInviteCount = () => roomList.inviteRooms.size
+ roomList.inviteSpaces.size
+ roomList.inviteDirects.size;
const [totalInvites, updateTotalInvites] = useState(totalInviteCount());
useEffect(() => {
const onInviteListChange = () => {
updateTotalInvites(totalInviteCount());
};
roomList.on(cons.events.roomList.INVITELIST_UPDATED, onInviteListChange);
return () => {
roomList.removeListener(cons.events.roomList.INVITELIST_UPDATED, onInviteListChange);
};
}, []);
return [totalInvites];
}
function SideBar() {
const [totalInvites] = useTotalInvites();
return (
openShortcutSpaces()}
avatar={}
/>
openSearch()}
avatar={}
/>
{ totalInvites !== 0 && (
openInviteList()}
avatar={}
notificationBadge={}
/>
)}
);
}
export default SideBar;