mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-05 06:50:28 +03:00
(chore) remove outdated code (#1765)
* optimize room typing members hook * remove unused code - WIP * remove old code from initMatrix * remove twemojify function * remove old sanitize util * delete old markdown util * delete Math atom component * uninstall unused dependencies * remove old notification system * decrypt message in inbox notification center and fix refresh in background * improve notification --------- Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
This commit is contained in:
parent
60e022035f
commit
4f09e6bbb5
147 changed files with 1164 additions and 15330 deletions
|
|
@ -1,144 +0,0 @@
|
|||
import EventEmitter from 'events';
|
||||
import appDispatcher from '../dispatcher';
|
||||
import cons from './cons';
|
||||
|
||||
class AccountData extends EventEmitter {
|
||||
constructor(roomList) {
|
||||
super();
|
||||
|
||||
this.matrixClient = roomList.matrixClient;
|
||||
this.roomList = roomList;
|
||||
this.spaces = roomList.spaces;
|
||||
|
||||
this.spaceShortcut = new Set();
|
||||
this._populateSpaceShortcut();
|
||||
|
||||
this.categorizedSpaces = new Set();
|
||||
this._populateCategorizedSpaces();
|
||||
|
||||
this._listenEvents();
|
||||
|
||||
appDispatcher.register(this.accountActions.bind(this));
|
||||
}
|
||||
|
||||
_getAccountData() {
|
||||
return this.matrixClient.getAccountData(cons.IN_CINNY_SPACES)?.getContent() || {};
|
||||
}
|
||||
|
||||
_populateSpaceShortcut() {
|
||||
this.spaceShortcut.clear();
|
||||
const spacesContent = this._getAccountData();
|
||||
|
||||
if (spacesContent?.shortcut === undefined) return;
|
||||
|
||||
spacesContent.shortcut.forEach((shortcut) => {
|
||||
if (this.spaces.has(shortcut)) this.spaceShortcut.add(shortcut);
|
||||
});
|
||||
if (spacesContent.shortcut.length !== this.spaceShortcut.size) {
|
||||
// update shortcut list from account data if shortcut space doesn't exist.
|
||||
// TODO: we can wait for sync to complete or else we may end up removing valid space id
|
||||
this._updateSpaceShortcutData([...this.spaceShortcut]);
|
||||
}
|
||||
}
|
||||
|
||||
_updateSpaceShortcutData(shortcutList) {
|
||||
const spaceContent = this._getAccountData();
|
||||
spaceContent.shortcut = shortcutList;
|
||||
this.matrixClient.setAccountData(cons.IN_CINNY_SPACES, spaceContent);
|
||||
}
|
||||
|
||||
_populateCategorizedSpaces() {
|
||||
this.categorizedSpaces.clear();
|
||||
const spaceContent = this._getAccountData();
|
||||
|
||||
if (spaceContent?.categorized === undefined) return;
|
||||
|
||||
spaceContent.categorized.forEach((spaceId) => {
|
||||
if (this.spaces.has(spaceId)) this.categorizedSpaces.add(spaceId);
|
||||
});
|
||||
if (spaceContent.categorized.length !== this.categorizedSpaces.size) {
|
||||
// TODO: we can wait for sync to complete or else we may end up removing valid space id
|
||||
this._updateCategorizedSpacesData([...this.categorizedSpaces]);
|
||||
}
|
||||
}
|
||||
|
||||
_updateCategorizedSpacesData(categorizedSpaceList) {
|
||||
const spaceContent = this._getAccountData();
|
||||
spaceContent.categorized = categorizedSpaceList;
|
||||
this.matrixClient.setAccountData(cons.IN_CINNY_SPACES, spaceContent);
|
||||
}
|
||||
|
||||
accountActions(action) {
|
||||
const actions = {
|
||||
[cons.actions.accountData.CREATE_SPACE_SHORTCUT]: () => {
|
||||
const addRoomId = (id) => {
|
||||
if (this.spaceShortcut.has(id)) return;
|
||||
this.spaceShortcut.add(id);
|
||||
};
|
||||
if (Array.isArray(action.roomId)) {
|
||||
action.roomId.forEach(addRoomId);
|
||||
} else {
|
||||
addRoomId(action.roomId);
|
||||
}
|
||||
this._updateSpaceShortcutData([...this.spaceShortcut]);
|
||||
this.emit(cons.events.accountData.SPACE_SHORTCUT_UPDATED, action.roomId);
|
||||
},
|
||||
[cons.actions.accountData.DELETE_SPACE_SHORTCUT]: () => {
|
||||
if (!this.spaceShortcut.has(action.roomId)) return;
|
||||
this.spaceShortcut.delete(action.roomId);
|
||||
this._updateSpaceShortcutData([...this.spaceShortcut]);
|
||||
this.emit(cons.events.accountData.SPACE_SHORTCUT_UPDATED, action.roomId);
|
||||
},
|
||||
[cons.actions.accountData.MOVE_SPACE_SHORTCUTS]: () => {
|
||||
const { roomId, toIndex } = action;
|
||||
if (!this.spaceShortcut.has(roomId)) return;
|
||||
this.spaceShortcut.delete(roomId);
|
||||
const ssList = [...this.spaceShortcut];
|
||||
if (toIndex >= ssList.length) ssList.push(roomId);
|
||||
else ssList.splice(toIndex, 0, roomId);
|
||||
this.spaceShortcut = new Set(ssList);
|
||||
this._updateSpaceShortcutData(ssList);
|
||||
this.emit(cons.events.accountData.SPACE_SHORTCUT_UPDATED, roomId);
|
||||
},
|
||||
[cons.actions.accountData.CATEGORIZE_SPACE]: () => {
|
||||
if (this.categorizedSpaces.has(action.roomId)) return;
|
||||
this.categorizedSpaces.add(action.roomId);
|
||||
this._updateCategorizedSpacesData([...this.categorizedSpaces]);
|
||||
this.emit(cons.events.accountData.CATEGORIZE_SPACE_UPDATED, action.roomId);
|
||||
},
|
||||
[cons.actions.accountData.UNCATEGORIZE_SPACE]: () => {
|
||||
if (!this.categorizedSpaces.has(action.roomId)) return;
|
||||
this.categorizedSpaces.delete(action.roomId);
|
||||
this._updateCategorizedSpacesData([...this.categorizedSpaces]);
|
||||
this.emit(cons.events.accountData.CATEGORIZE_SPACE_UPDATED, action.roomId);
|
||||
},
|
||||
};
|
||||
actions[action.type]?.();
|
||||
}
|
||||
|
||||
_listenEvents() {
|
||||
this.matrixClient.on('accountData', (event) => {
|
||||
if (event.getType() !== cons.IN_CINNY_SPACES) return;
|
||||
this._populateSpaceShortcut();
|
||||
this.emit(cons.events.accountData.SPACE_SHORTCUT_UPDATED);
|
||||
this._populateCategorizedSpaces();
|
||||
this.emit(cons.events.accountData.CATEGORIZE_SPACE_UPDATED);
|
||||
});
|
||||
|
||||
this.roomList.on(cons.events.roomList.ROOM_LEAVED, (roomId) => {
|
||||
if (this.spaceShortcut.has(roomId)) {
|
||||
// if deleted space has shortcut remove it.
|
||||
this.spaceShortcut.delete(roomId);
|
||||
this._updateSpaceShortcutData([...this.spaceShortcut]);
|
||||
this.emit(cons.events.accountData.SPACE_SHORTCUT_UPDATED, roomId);
|
||||
}
|
||||
if (this.categorizedSpaces.has(roomId)) {
|
||||
this.categorizedSpaces.delete(roomId);
|
||||
this._updateCategorizedSpacesData([...this.categorizedSpaces]);
|
||||
this.emit(cons.events.accountData.CATEGORIZE_SPACE_UPDATED, roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default AccountData;
|
||||
|
|
@ -1,412 +0,0 @@
|
|||
import EventEmitter from 'events';
|
||||
import renderAvatar from '../../app/atoms/avatar/render';
|
||||
import { cssColorMXID } from '../../util/colorMXID';
|
||||
import { selectRoom } from '../action/navigation';
|
||||
import cons from './cons';
|
||||
import navigation from './navigation';
|
||||
import settings from './settings';
|
||||
import { setFavicon } from '../../util/common';
|
||||
|
||||
import LogoSVG from '../../../public/res/svg/cinny.svg';
|
||||
import LogoUnreadSVG from '../../../public/res/svg/cinny-unread.svg';
|
||||
import LogoHighlightSVG from '../../../public/res/svg/cinny-highlight.svg';
|
||||
import { html, plain } from '../../util/markdown';
|
||||
|
||||
function isNotifEvent(mEvent) {
|
||||
const eType = mEvent.getType();
|
||||
if (!cons.supportEventTypes.includes(eType)) return false;
|
||||
if (eType === 'm.room.member') return false;
|
||||
|
||||
if (mEvent.isRedacted()) return false;
|
||||
if (mEvent.getRelation()?.rel_type === 'm.replace') return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isMutedRule(rule) {
|
||||
return rule.actions[0] === 'dont_notify' && rule.conditions[0].kind === 'event_match';
|
||||
}
|
||||
|
||||
function findMutedRule(overrideRules, roomId) {
|
||||
return overrideRules.find((rule) => (
|
||||
rule.rule_id === roomId
|
||||
&& isMutedRule(rule)
|
||||
));
|
||||
}
|
||||
|
||||
class Notifications extends EventEmitter {
|
||||
constructor(roomList) {
|
||||
super();
|
||||
|
||||
this.initialized = false;
|
||||
this.favicon = LogoSVG;
|
||||
this.matrixClient = roomList.matrixClient;
|
||||
this.roomList = roomList;
|
||||
|
||||
this.roomIdToNoti = new Map();
|
||||
this.roomIdToPopupNotis = new Map();
|
||||
this.eventIdToPopupNoti = new Map();
|
||||
|
||||
// this._initNoti();
|
||||
this._listenEvents();
|
||||
|
||||
// Ask for permission by default after loading
|
||||
window.Notification?.requestPermission();
|
||||
}
|
||||
|
||||
async _initNoti() {
|
||||
this.initialized = false;
|
||||
this.roomIdToNoti = new Map();
|
||||
|
||||
const addNoti = (roomId) => {
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
if (this.getNotiType(room.roomId) === cons.notifs.MUTE) return;
|
||||
if (this.doesRoomHaveUnread(room) === false) return;
|
||||
|
||||
const total = room.getUnreadNotificationCount('total');
|
||||
const highlight = room.getUnreadNotificationCount('highlight');
|
||||
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||
};
|
||||
[...this.roomList.rooms].forEach(addNoti);
|
||||
[...this.roomList.directs].forEach(addNoti);
|
||||
|
||||
this.initialized = true;
|
||||
this._updateFavicon();
|
||||
}
|
||||
|
||||
doesRoomHaveUnread(room) {
|
||||
const userId = this.matrixClient.getUserId();
|
||||
const readUpToId = room.getEventReadUpTo(userId);
|
||||
const liveEvents = room.getLiveTimeline().getEvents();
|
||||
|
||||
if (liveEvents[liveEvents.length - 1]?.getSender() === userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
|
||||
const event = liveEvents[i];
|
||||
if (event.getId() === readUpToId) return false;
|
||||
if (isNotifEvent(event)) return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
getNotiType(roomId) {
|
||||
const mx = this.matrixClient;
|
||||
let pushRule;
|
||||
try {
|
||||
pushRule = mx.getRoomPushRule('global', roomId);
|
||||
} catch {
|
||||
pushRule = undefined;
|
||||
}
|
||||
|
||||
if (pushRule === undefined) {
|
||||
const overrideRules = mx.getAccountData('m.push_rules')?.getContent()?.global?.override;
|
||||
if (overrideRules === undefined) return cons.notifs.DEFAULT;
|
||||
|
||||
const isMuted = findMutedRule(overrideRules, roomId);
|
||||
|
||||
return isMuted ? cons.notifs.MUTE : cons.notifs.DEFAULT;
|
||||
}
|
||||
if (pushRule.actions[0] === 'notify') return cons.notifs.ALL_MESSAGES;
|
||||
return cons.notifs.MENTIONS_AND_KEYWORDS;
|
||||
}
|
||||
|
||||
getNoti(roomId) {
|
||||
return this.roomIdToNoti.get(roomId) || { total: 0, highlight: 0, from: null };
|
||||
}
|
||||
|
||||
getTotalNoti(roomId) {
|
||||
const { total } = this.getNoti(roomId);
|
||||
return total;
|
||||
}
|
||||
|
||||
getHighlightNoti(roomId) {
|
||||
const { highlight } = this.getNoti(roomId);
|
||||
return highlight;
|
||||
}
|
||||
|
||||
getFromNoti(roomId) {
|
||||
const { from } = this.getNoti(roomId);
|
||||
return from;
|
||||
}
|
||||
|
||||
hasNoti(roomId) {
|
||||
return this.roomIdToNoti.has(roomId);
|
||||
}
|
||||
|
||||
deleteNoti(roomId) {
|
||||
if (this.hasNoti(roomId)) {
|
||||
const noti = this.getNoti(roomId);
|
||||
this._deleteNoti(roomId, noti.total, noti.highlight);
|
||||
}
|
||||
}
|
||||
|
||||
async _updateFavicon() {
|
||||
if (!this.initialized) return;
|
||||
let unread = false;
|
||||
let highlight = false;
|
||||
[...this.roomIdToNoti.values()].find((noti) => {
|
||||
if (!unread) {
|
||||
unread = noti.total > 0 || noti.highlight > 0;
|
||||
}
|
||||
highlight = noti.highlight > 0;
|
||||
if (unread && highlight) return true;
|
||||
return false;
|
||||
});
|
||||
let newFavicon = LogoSVG;
|
||||
if (unread && !highlight) {
|
||||
newFavicon = LogoUnreadSVG;
|
||||
}
|
||||
if (unread && highlight) {
|
||||
newFavicon = LogoHighlightSVG;
|
||||
}
|
||||
if (newFavicon === this.favicon) return;
|
||||
this.favicon = newFavicon;
|
||||
setFavicon(this.favicon);
|
||||
}
|
||||
|
||||
_setNoti(roomId, total, highlight) {
|
||||
const addNoti = (id, t, h, fromId) => {
|
||||
const prevTotal = this.roomIdToNoti.get(id)?.total ?? null;
|
||||
const noti = this.getNoti(id);
|
||||
|
||||
noti.total += t;
|
||||
noti.highlight += h;
|
||||
|
||||
if (fromId) {
|
||||
if (noti.from === null) noti.from = new Set();
|
||||
noti.from.add(fromId);
|
||||
}
|
||||
this.roomIdToNoti.set(id, noti);
|
||||
this.emit(cons.events.notifications.NOTI_CHANGED, id, noti.total, prevTotal);
|
||||
};
|
||||
|
||||
const noti = this.getNoti(roomId);
|
||||
const addT = (highlight > total ? highlight : total) - noti.total;
|
||||
const addH = highlight - noti.highlight;
|
||||
if (addT < 0 || addH < 0) return;
|
||||
|
||||
addNoti(roomId, addT, addH);
|
||||
const allParentSpaces = this.roomList.getAllParentSpaces(roomId);
|
||||
allParentSpaces.forEach((spaceId) => {
|
||||
addNoti(spaceId, addT, addH, roomId);
|
||||
});
|
||||
this._updateFavicon();
|
||||
}
|
||||
|
||||
_deleteNoti(roomId, total, highlight) {
|
||||
const removeNoti = (id, t, h, fromId) => {
|
||||
if (this.roomIdToNoti.has(id) === false) return;
|
||||
|
||||
const noti = this.getNoti(id);
|
||||
const prevTotal = noti.total;
|
||||
noti.total -= t;
|
||||
noti.highlight -= h;
|
||||
if (noti.total < 0) {
|
||||
noti.total = 0;
|
||||
noti.highlight = 0;
|
||||
}
|
||||
if (fromId && noti.from !== null) {
|
||||
if (!this.hasNoti(fromId)) noti.from.delete(fromId);
|
||||
}
|
||||
if (noti.from === null || noti.from.size === 0) {
|
||||
this.roomIdToNoti.delete(id);
|
||||
this.emit(cons.events.notifications.FULL_READ, id);
|
||||
this.emit(cons.events.notifications.NOTI_CHANGED, id, null, prevTotal);
|
||||
} else {
|
||||
this.roomIdToNoti.set(id, noti);
|
||||
this.emit(cons.events.notifications.NOTI_CHANGED, id, noti.total, prevTotal);
|
||||
}
|
||||
};
|
||||
|
||||
removeNoti(roomId, total, highlight);
|
||||
const allParentSpaces = this.roomList.getAllParentSpaces(roomId);
|
||||
allParentSpaces.forEach((spaceId) => {
|
||||
removeNoti(spaceId, total, highlight, roomId);
|
||||
});
|
||||
this._updateFavicon();
|
||||
}
|
||||
|
||||
async _displayPopupNoti(mEvent, room) {
|
||||
if (!settings.showNotifications && !settings.isNotificationSounds) return;
|
||||
|
||||
const actions = this.matrixClient.getPushActionsForEvent(mEvent);
|
||||
if (!actions?.notify) return;
|
||||
|
||||
if (navigation.selectedRoomId === room.roomId && document.hasFocus()) return;
|
||||
|
||||
if (mEvent.isEncrypted()) {
|
||||
await mEvent.attemptDecryption(this.matrixClient.crypto);
|
||||
}
|
||||
|
||||
if (settings.showNotifications) {
|
||||
let title;
|
||||
if (!mEvent.sender || room.name === mEvent.sender.name) {
|
||||
title = room.name;
|
||||
} else if (mEvent.sender) {
|
||||
title = `${mEvent.sender.name} (${room.name})`;
|
||||
}
|
||||
|
||||
const iconSize = 36;
|
||||
const icon = await renderAvatar({
|
||||
text: mEvent.sender.name,
|
||||
bgColor: cssColorMXID(mEvent.getSender()),
|
||||
imageSrc: mEvent.sender?.getAvatarUrl(this.matrixClient.baseUrl, iconSize, iconSize, 'crop'),
|
||||
size: iconSize,
|
||||
borderRadius: 8,
|
||||
scale: 8,
|
||||
});
|
||||
|
||||
const content = mEvent.getContent();
|
||||
|
||||
const state = { kind: 'notification', onlyPlain: true };
|
||||
let body;
|
||||
if (content.format === 'org.matrix.custom.html') {
|
||||
body = html(content.formatted_body, state);
|
||||
} else {
|
||||
body = plain(content.body, state);
|
||||
}
|
||||
|
||||
const noti = new window.Notification(title, {
|
||||
body: body.plain,
|
||||
icon,
|
||||
tag: mEvent.getId(),
|
||||
silent: settings.isNotificationSounds,
|
||||
});
|
||||
if (settings.isNotificationSounds) {
|
||||
noti.onshow = () => this._playNotiSound();
|
||||
}
|
||||
noti.onclick = () => selectRoom(room.roomId, mEvent.getId());
|
||||
|
||||
this.eventIdToPopupNoti.set(mEvent.getId(), noti);
|
||||
if (this.roomIdToPopupNotis.has(room.roomId)) {
|
||||
this.roomIdToPopupNotis.get(room.roomId).push(noti);
|
||||
} else {
|
||||
this.roomIdToPopupNotis.set(room.roomId, [noti]);
|
||||
}
|
||||
} else {
|
||||
this._playNotiSound();
|
||||
}
|
||||
}
|
||||
|
||||
_deletePopupNoti(eventId) {
|
||||
this.eventIdToPopupNoti.get(eventId)?.close();
|
||||
this.eventIdToPopupNoti.delete(eventId);
|
||||
}
|
||||
|
||||
_deletePopupRoomNotis(roomId) {
|
||||
this.roomIdToPopupNotis.get(roomId)?.forEach((n) => {
|
||||
this.eventIdToPopupNoti.delete(n.tag);
|
||||
n.close();
|
||||
});
|
||||
this.roomIdToPopupNotis.delete(roomId);
|
||||
}
|
||||
|
||||
_playNotiSound() {
|
||||
if (!this._notiAudio) {
|
||||
this._notiAudio = document.getElementById('notificationSound');
|
||||
}
|
||||
this._notiAudio.play();
|
||||
}
|
||||
|
||||
_playInviteSound() {
|
||||
if (!this._inviteAudio) {
|
||||
this._inviteAudio = document.getElementById('inviteSound');
|
||||
}
|
||||
this._inviteAudio.play();
|
||||
}
|
||||
|
||||
_listenEvents() {
|
||||
this.matrixClient.on('Room.timeline', (mEvent, room) => {
|
||||
if (mEvent.isRedaction()) this._deletePopupNoti(mEvent.event.redacts);
|
||||
|
||||
if (room.isSpaceRoom()) return;
|
||||
if (!isNotifEvent(mEvent)) return;
|
||||
|
||||
const liveEvents = room.getLiveTimeline().getEvents();
|
||||
|
||||
const lastTimelineEvent = liveEvents[liveEvents.length - 1];
|
||||
if (lastTimelineEvent.getId() !== mEvent.getId()) return;
|
||||
if (mEvent.getSender() === this.matrixClient.getUserId()) return;
|
||||
|
||||
const total = room.getUnreadNotificationCount('total');
|
||||
const highlight = room.getUnreadNotificationCount('highlight');
|
||||
|
||||
if (this.getNotiType(room.roomId) === cons.notifs.MUTE) {
|
||||
this.deleteNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||
return;
|
||||
}
|
||||
|
||||
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||
|
||||
if (this.matrixClient.getSyncState() === 'SYNCING') {
|
||||
this._displayPopupNoti(mEvent, room);
|
||||
}
|
||||
});
|
||||
|
||||
this.matrixClient.on('accountData', (mEvent, oldMEvent) => {
|
||||
if (mEvent.getType() === 'm.push_rules') {
|
||||
const override = mEvent?.getContent()?.global?.override;
|
||||
const oldOverride = oldMEvent?.getContent()?.global?.override;
|
||||
if (!override || !oldOverride) return;
|
||||
|
||||
const isMuteToggled = (rule, otherOverride) => {
|
||||
const roomId = rule.rule_id;
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
if (room === null) return false;
|
||||
if (room.isSpaceRoom()) return false;
|
||||
|
||||
const isMuted = isMutedRule(rule);
|
||||
if (!isMuted) return false;
|
||||
const isOtherMuted = findMutedRule(otherOverride, roomId);
|
||||
if (isOtherMuted) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const mutedRules = override.filter((rule) => isMuteToggled(rule, oldOverride));
|
||||
const unMutedRules = oldOverride.filter((rule) => isMuteToggled(rule, override));
|
||||
|
||||
mutedRules.forEach((rule) => {
|
||||
this.emit(cons.events.notifications.MUTE_TOGGLED, rule.rule_id, true);
|
||||
this.deleteNoti(rule.rule_id);
|
||||
});
|
||||
unMutedRules.forEach((rule) => {
|
||||
this.emit(cons.events.notifications.MUTE_TOGGLED, rule.rule_id, false);
|
||||
const room = this.matrixClient.getRoom(rule.rule_id);
|
||||
if (!this.doesRoomHaveUnread(room)) return;
|
||||
const total = room.getUnreadNotificationCount('total');
|
||||
const highlight = room.getUnreadNotificationCount('highlight');
|
||||
this._setNoti(room.roomId, total ?? 0, highlight ?? 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.matrixClient.on('Room.receipt', (mEvent, room) => {
|
||||
if (mEvent.getType() !== 'm.receipt' || room.isSpaceRoom()) return;
|
||||
const content = mEvent.getContent();
|
||||
const userId = this.matrixClient.getUserId();
|
||||
|
||||
Object.keys(content).forEach((eventId) => {
|
||||
Object.entries(content[eventId]).forEach(([receiptType, receipt]) => {
|
||||
if (!cons.supportReceiptTypes.includes(receiptType)) return;
|
||||
if (Object.keys(receipt || {}).includes(userId)) {
|
||||
this.deleteNoti(room.roomId);
|
||||
this._deletePopupRoomNotis(room.roomId);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.matrixClient.on('Room.myMembership', (room, membership) => {
|
||||
if (membership === 'leave' && this.hasNoti(room.roomId)) {
|
||||
this.deleteNoti(room.roomId);
|
||||
}
|
||||
if (membership === 'invite') {
|
||||
this._playInviteSound();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Notifications;
|
||||
|
|
@ -1,383 +0,0 @@
|
|||
import EventEmitter from 'events';
|
||||
import appDispatcher from '../dispatcher';
|
||||
import cons from './cons';
|
||||
|
||||
function isMEventSpaceChild(mEvent) {
|
||||
return mEvent.getType() === 'm.space.child' && Object.keys(mEvent.getContent()).length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => boolean} callback if return true wait will over else callback will be called again.
|
||||
* @param {number} timeout timeout to callback
|
||||
* @param {number} maxTry maximum callback try > 0. -1 means no limit
|
||||
*/
|
||||
async function waitFor(callback, timeout = 400, maxTry = -1) {
|
||||
if (maxTry === 0) return false;
|
||||
const isOver = async () => new Promise((resolve) => {
|
||||
setTimeout(() => resolve(callback()), timeout);
|
||||
});
|
||||
|
||||
if (await isOver()) return true;
|
||||
return waitFor(callback, timeout, maxTry - 1);
|
||||
}
|
||||
|
||||
class RoomList extends EventEmitter {
|
||||
constructor(matrixClient) {
|
||||
super();
|
||||
this.matrixClient = matrixClient;
|
||||
this.mDirects = this.getMDirects();
|
||||
|
||||
// Contains roomId to parent spaces roomId mapping of all spaces children.
|
||||
// No matter if you have joined those children rooms or not.
|
||||
this.roomIdToParents = new Map();
|
||||
|
||||
this.inviteDirects = new Set();
|
||||
this.inviteSpaces = new Set();
|
||||
this.inviteRooms = new Set();
|
||||
|
||||
this.directs = new Set();
|
||||
this.spaces = new Set();
|
||||
this.rooms = new Set();
|
||||
|
||||
this.processingRooms = new Map();
|
||||
|
||||
this._populateRooms();
|
||||
this._listenEvents();
|
||||
|
||||
appDispatcher.register(this.roomActions.bind(this));
|
||||
}
|
||||
|
||||
isOrphan(roomId) {
|
||||
return !this.roomIdToParents.has(roomId);
|
||||
}
|
||||
|
||||
getOrphanSpaces() {
|
||||
return [...this.spaces].filter((roomId) => !this.roomIdToParents.has(roomId));
|
||||
}
|
||||
|
||||
getOrphanRooms() {
|
||||
return [...this.rooms].filter((roomId) => !this.roomIdToParents.has(roomId));
|
||||
}
|
||||
|
||||
getOrphans() {
|
||||
const rooms = [...this.spaces].concat([...this.rooms]);
|
||||
return rooms.filter((roomId) => !this.roomIdToParents.has(roomId));
|
||||
}
|
||||
|
||||
getSpaceChildren(roomId) {
|
||||
const space = this.matrixClient.getRoom(roomId);
|
||||
if (space === null) return null;
|
||||
const mSpaceChild = space?.currentState.getStateEvents('m.space.child');
|
||||
|
||||
const children = [];
|
||||
mSpaceChild.forEach((mEvent) => {
|
||||
const childId = mEvent.event.state_key;
|
||||
if (isMEventSpaceChild(mEvent)) children.push(childId);
|
||||
});
|
||||
return children;
|
||||
}
|
||||
|
||||
getCategorizedSpaces(spaceIds) {
|
||||
const categorized = new Map();
|
||||
|
||||
const categorizeSpace = (spaceId) => {
|
||||
if (categorized.has(spaceId)) return;
|
||||
const mappedChild = new Set();
|
||||
categorized.set(spaceId, mappedChild);
|
||||
|
||||
const child = this.getSpaceChildren(spaceId);
|
||||
|
||||
child.forEach((childId) => {
|
||||
const room = this.matrixClient.getRoom(childId);
|
||||
if (room === null || room.getMyMembership() !== 'join') return;
|
||||
if (room.isSpaceRoom()) categorizeSpace(childId);
|
||||
else mappedChild.add(childId);
|
||||
});
|
||||
};
|
||||
spaceIds.forEach(categorizeSpace);
|
||||
|
||||
return categorized;
|
||||
}
|
||||
|
||||
addToRoomIdToParents(roomId, parentRoomId) {
|
||||
if (!this.roomIdToParents.has(roomId)) {
|
||||
this.roomIdToParents.set(roomId, new Set());
|
||||
}
|
||||
const parents = this.roomIdToParents.get(roomId);
|
||||
parents.add(parentRoomId);
|
||||
}
|
||||
|
||||
removeFromRoomIdToParents(roomId, parentRoomId) {
|
||||
if (!this.roomIdToParents.has(roomId)) return;
|
||||
const parents = this.roomIdToParents.get(roomId);
|
||||
parents.delete(parentRoomId);
|
||||
if (parents.size === 0) this.roomIdToParents.delete(roomId);
|
||||
}
|
||||
|
||||
getAllParentSpaces(roomId) {
|
||||
const allParents = new Set();
|
||||
|
||||
const addAllParentIds = (rId) => {
|
||||
if (allParents.has(rId)) return;
|
||||
allParents.add(rId);
|
||||
|
||||
const parents = this.roomIdToParents.get(rId);
|
||||
if (parents === undefined) return;
|
||||
|
||||
parents.forEach((id) => addAllParentIds(id));
|
||||
};
|
||||
addAllParentIds(roomId);
|
||||
allParents.delete(roomId);
|
||||
return allParents;
|
||||
}
|
||||
|
||||
addToSpaces(roomId) {
|
||||
this.spaces.add(roomId);
|
||||
|
||||
const allParentSpaces = this.getAllParentSpaces(roomId);
|
||||
const spaceChildren = this.getSpaceChildren(roomId);
|
||||
spaceChildren?.forEach((childId) => {
|
||||
if (allParentSpaces.has(childId)) return;
|
||||
this.addToRoomIdToParents(childId, roomId);
|
||||
});
|
||||
}
|
||||
|
||||
deleteFromSpaces(roomId) {
|
||||
this.spaces.delete(roomId);
|
||||
|
||||
const spaceChildren = this.getSpaceChildren(roomId);
|
||||
spaceChildren?.forEach((childId) => {
|
||||
this.removeFromRoomIdToParents(childId, roomId);
|
||||
});
|
||||
}
|
||||
|
||||
roomActions(action) {
|
||||
const addRoom = (roomId, isDM) => {
|
||||
const myRoom = this.matrixClient.getRoom(roomId);
|
||||
if (myRoom === null) return false;
|
||||
|
||||
if (isDM) this.directs.add(roomId);
|
||||
else if (myRoom.isSpaceRoom()) this.addToSpaces(roomId);
|
||||
else this.rooms.add(roomId);
|
||||
return true;
|
||||
};
|
||||
const actions = {
|
||||
[cons.actions.room.JOIN]: () => {
|
||||
if (addRoom(action.roomId, action.isDM)) {
|
||||
setTimeout(() => {
|
||||
this.emit(cons.events.roomList.ROOM_JOINED, action.roomId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
}, 100);
|
||||
} else {
|
||||
this.processingRooms.set(action.roomId, {
|
||||
roomId: action.roomId,
|
||||
isDM: action.isDM,
|
||||
task: 'JOIN',
|
||||
});
|
||||
}
|
||||
},
|
||||
[cons.actions.room.CREATE]: () => {
|
||||
if (addRoom(action.roomId, action.isDM)) {
|
||||
setTimeout(() => {
|
||||
this.emit(cons.events.roomList.ROOM_CREATED, action.roomId);
|
||||
this.emit(cons.events.roomList.ROOM_JOINED, action.roomId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
}, 100);
|
||||
} else {
|
||||
this.processingRooms.set(action.roomId, {
|
||||
roomId: action.roomId,
|
||||
isDM: action.isDM,
|
||||
task: 'CREATE',
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
actions[action.type]?.();
|
||||
}
|
||||
|
||||
getMDirects() {
|
||||
const mDirectsId = new Set();
|
||||
const mDirect = this.matrixClient
|
||||
.getAccountData('m.direct')
|
||||
?.getContent();
|
||||
|
||||
if (typeof mDirect === 'undefined') return mDirectsId;
|
||||
|
||||
Object.keys(mDirect).forEach((direct) => {
|
||||
mDirect[direct].forEach((directId) => mDirectsId.add(directId));
|
||||
});
|
||||
|
||||
return mDirectsId;
|
||||
}
|
||||
|
||||
_populateRooms() {
|
||||
this.directs.clear();
|
||||
this.roomIdToParents.clear();
|
||||
this.spaces.clear();
|
||||
this.rooms.clear();
|
||||
this.inviteDirects.clear();
|
||||
this.inviteSpaces.clear();
|
||||
this.inviteRooms.clear();
|
||||
this.matrixClient.getRooms().forEach((room) => {
|
||||
const { roomId } = room;
|
||||
|
||||
if (room.getMyMembership() === 'invite') {
|
||||
if (this._isDMInvite(room)) this.inviteDirects.add(roomId);
|
||||
else if (room.isSpaceRoom()) this.inviteSpaces.add(roomId);
|
||||
else this.inviteRooms.add(roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (room.getMyMembership() !== 'join') return;
|
||||
|
||||
if (this.mDirects.has(roomId)) this.directs.add(roomId);
|
||||
else if (room.isSpaceRoom()) this.addToSpaces(roomId);
|
||||
else this.rooms.add(roomId);
|
||||
});
|
||||
}
|
||||
|
||||
_isDMInvite(room) {
|
||||
if (this.mDirects.has(room.roomId)) return true;
|
||||
const me = room.getMember(this.matrixClient.getUserId());
|
||||
const myEventContent = me.events.member.getContent();
|
||||
return myEventContent.membership === 'invite' && myEventContent.is_direct;
|
||||
}
|
||||
|
||||
_listenEvents() {
|
||||
// Update roomList when m.direct changes
|
||||
this.matrixClient.on('accountData', (event) => {
|
||||
if (event.getType() !== 'm.direct') return;
|
||||
|
||||
const latestMDirects = this.getMDirects();
|
||||
|
||||
latestMDirects.forEach((directId) => {
|
||||
if (this.mDirects.has(directId)) return;
|
||||
this.mDirects.add(directId);
|
||||
|
||||
const myRoom = this.matrixClient.getRoom(directId);
|
||||
if (myRoom === null) return;
|
||||
if (myRoom.getMyMembership() === 'join') {
|
||||
this.directs.add(directId);
|
||||
this.rooms.delete(directId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
}
|
||||
});
|
||||
|
||||
[...this.directs].forEach((directId) => {
|
||||
if (latestMDirects.has(directId)) return;
|
||||
this.mDirects.delete(directId);
|
||||
|
||||
const myRoom = this.matrixClient.getRoom(directId);
|
||||
if (myRoom === null) return;
|
||||
if (myRoom.getMyMembership() === 'join') {
|
||||
this.directs.delete(directId);
|
||||
this.rooms.add(directId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.matrixClient.on('Room.name', (room) => {
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
this.emit(cons.events.roomList.ROOM_PROFILE_UPDATED, room.roomId);
|
||||
});
|
||||
|
||||
this.matrixClient.on('RoomState.events', (mEvent, state) => {
|
||||
if (mEvent.getType() === 'm.space.child') {
|
||||
const roomId = mEvent.event.room_id;
|
||||
const childId = mEvent.event.state_key;
|
||||
if (isMEventSpaceChild(mEvent)) {
|
||||
const allParentSpaces = this.getAllParentSpaces(roomId);
|
||||
// only add if it doesn't make a cycle
|
||||
if (!allParentSpaces.has(childId)) {
|
||||
this.addToRoomIdToParents(childId, roomId);
|
||||
}
|
||||
} else {
|
||||
this.removeFromRoomIdToParents(childId, roomId);
|
||||
}
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
return;
|
||||
}
|
||||
if (mEvent.getType() === 'm.room.join_rules') {
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
return;
|
||||
}
|
||||
if (['m.room.avatar', 'm.room.topic'].includes(mEvent.getType())) {
|
||||
if (mEvent.getType() === 'm.room.avatar') {
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
}
|
||||
this.emit(cons.events.roomList.ROOM_PROFILE_UPDATED, state.roomId);
|
||||
}
|
||||
});
|
||||
|
||||
this.matrixClient.on('Room.myMembership', async (room, membership, prevMembership) => {
|
||||
// room => prevMembership = null | invite | join | leave | kick | ban | unban
|
||||
// room => membership = invite | join | leave | kick | ban | unban
|
||||
const { roomId } = room;
|
||||
const isRoomReady = () => this.matrixClient.getRoom(roomId) !== null;
|
||||
if (['join', 'invite'].includes(membership) && isRoomReady() === false) {
|
||||
if (await waitFor(isRoomReady, 200, 100) === false) return;
|
||||
}
|
||||
|
||||
if (membership === 'unban') return;
|
||||
|
||||
if (membership === 'invite') {
|
||||
if (this._isDMInvite(room)) this.inviteDirects.add(roomId);
|
||||
else if (room.isSpaceRoom()) this.inviteSpaces.add(roomId);
|
||||
else this.inviteRooms.add(roomId);
|
||||
|
||||
this.emit(cons.events.roomList.INVITELIST_UPDATED, roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevMembership === 'invite') {
|
||||
if (this.inviteDirects.has(roomId)) this.inviteDirects.delete(roomId);
|
||||
else if (this.inviteSpaces.has(roomId)) this.inviteSpaces.delete(roomId);
|
||||
else this.inviteRooms.delete(roomId);
|
||||
|
||||
this.emit(cons.events.roomList.INVITELIST_UPDATED, roomId);
|
||||
}
|
||||
|
||||
if (['leave', 'kick', 'ban'].includes(membership)) {
|
||||
if (this.directs.has(roomId)) this.directs.delete(roomId);
|
||||
else if (this.spaces.has(roomId)) this.deleteFromSpaces(roomId);
|
||||
else this.rooms.delete(roomId);
|
||||
this.emit(cons.events.roomList.ROOM_LEAVED, roomId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
return;
|
||||
}
|
||||
|
||||
// when user create room/DM OR accept room/dm invite from this client.
|
||||
// we will update this.rooms/this.directs with user action
|
||||
if (membership === 'join' && this.processingRooms.has(roomId)) {
|
||||
const procRoomInfo = this.processingRooms.get(roomId);
|
||||
|
||||
if (procRoomInfo.isDM) this.directs.add(roomId);
|
||||
else if (room.isSpaceRoom()) this.addToSpaces(roomId);
|
||||
else this.rooms.add(roomId);
|
||||
|
||||
if (procRoomInfo.task === 'CREATE') this.emit(cons.events.roomList.ROOM_CREATED, roomId);
|
||||
this.emit(cons.events.roomList.ROOM_JOINED, roomId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
|
||||
this.processingRooms.delete(roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mDirects.has(roomId) && membership === 'join') {
|
||||
this.directs.add(roomId);
|
||||
this.emit(cons.events.roomList.ROOM_JOINED, roomId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (membership === 'join') {
|
||||
if (room.isSpaceRoom()) this.addToSpaces(roomId);
|
||||
else this.rooms.add(roomId);
|
||||
this.emit(cons.events.roomList.ROOM_JOINED, roomId);
|
||||
this.emit(cons.events.roomList.ROOMLIST_UPDATED);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
export default RoomList;
|
||||
|
|
@ -1,407 +0,0 @@
|
|||
import EventEmitter from 'events';
|
||||
import initMatrix from '../initMatrix';
|
||||
import cons from './cons';
|
||||
|
||||
import settings from './settings';
|
||||
|
||||
function isEdited(mEvent) {
|
||||
return mEvent.getRelation()?.rel_type === 'm.replace';
|
||||
}
|
||||
|
||||
function isReaction(mEvent) {
|
||||
return mEvent.getType() === 'm.reaction';
|
||||
}
|
||||
|
||||
function hideMemberEvents(mEvent) {
|
||||
const content = mEvent.getContent();
|
||||
const prevContent = mEvent.getPrevContent();
|
||||
const { membership } = content;
|
||||
if (settings.hideMembershipEvents) {
|
||||
if (membership === 'invite' || membership === 'ban' || membership === 'leave') return true;
|
||||
if (prevContent.membership !== 'join') return true;
|
||||
}
|
||||
if (settings.hideNickAvatarEvents) {
|
||||
if (membership === 'join' && prevContent.membership === 'join') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getRelateToId(mEvent) {
|
||||
const relation = mEvent.getRelation();
|
||||
return relation && relation.event_id;
|
||||
}
|
||||
|
||||
function addToMap(myMap, mEvent) {
|
||||
const relateToId = getRelateToId(mEvent);
|
||||
if (relateToId === null) return null;
|
||||
const mEventId = mEvent.getId();
|
||||
|
||||
if (typeof myMap.get(relateToId) === 'undefined') myMap.set(relateToId, []);
|
||||
const mEvents = myMap.get(relateToId);
|
||||
if (mEvents.find((ev) => ev.getId() === mEventId)) return mEvent;
|
||||
mEvents.push(mEvent);
|
||||
return mEvent;
|
||||
}
|
||||
|
||||
function getFirstLinkedTimeline(timeline) {
|
||||
let tm = timeline;
|
||||
while (tm.prevTimeline) {
|
||||
tm = tm.prevTimeline;
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
function getLastLinkedTimeline(timeline) {
|
||||
let tm = timeline;
|
||||
while (tm.nextTimeline) {
|
||||
tm = tm.nextTimeline;
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
|
||||
function iterateLinkedTimelines(timeline, backwards, callback) {
|
||||
let tm = timeline;
|
||||
while (tm) {
|
||||
callback(tm);
|
||||
if (backwards) tm = tm.prevTimeline;
|
||||
else tm = tm.nextTimeline;
|
||||
}
|
||||
}
|
||||
|
||||
function isTimelineLinked(tm1, tm2) {
|
||||
let tm = getFirstLinkedTimeline(tm1);
|
||||
while (tm) {
|
||||
if (tm === tm2) return true;
|
||||
tm = tm.nextTimeline;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class RoomTimeline extends EventEmitter {
|
||||
constructor(roomId) {
|
||||
super();
|
||||
// These are local timelines
|
||||
this.timeline = [];
|
||||
this.editedTimeline = new Map();
|
||||
this.reactionTimeline = new Map();
|
||||
this.typingMembers = new Set();
|
||||
|
||||
this.matrixClient = initMatrix.matrixClient;
|
||||
this.roomId = roomId;
|
||||
this.room = this.matrixClient.getRoom(roomId);
|
||||
|
||||
this.liveTimeline = this.room.getLiveTimeline();
|
||||
this.activeTimeline = this.liveTimeline;
|
||||
|
||||
this.isOngoingPagination = false;
|
||||
this.ongoingDecryptionCount = 0;
|
||||
this.initialized = false;
|
||||
|
||||
setTimeout(() => this.room.loadMembersIfNeeded());
|
||||
|
||||
// TODO: remove below line
|
||||
window.selectedRoom = this;
|
||||
}
|
||||
|
||||
isServingLiveTimeline() {
|
||||
return getLastLinkedTimeline(this.activeTimeline) === this.liveTimeline;
|
||||
}
|
||||
|
||||
canPaginateBackward() {
|
||||
if (this.timeline[0]?.getType() === 'm.room.create') return false;
|
||||
const tm = getFirstLinkedTimeline(this.activeTimeline);
|
||||
return tm.getPaginationToken('b') !== null;
|
||||
}
|
||||
|
||||
canPaginateForward() {
|
||||
return !this.isServingLiveTimeline();
|
||||
}
|
||||
|
||||
isEncrypted() {
|
||||
return this.matrixClient.isRoomEncrypted(this.roomId);
|
||||
}
|
||||
|
||||
clearLocalTimelines() {
|
||||
this.timeline = [];
|
||||
}
|
||||
|
||||
addToTimeline(mEvent) {
|
||||
if (mEvent.getType() === 'm.room.member' && hideMemberEvents(mEvent)) {
|
||||
return;
|
||||
}
|
||||
if (mEvent.isRedacted()) return;
|
||||
if (isReaction(mEvent)) {
|
||||
addToMap(this.reactionTimeline, mEvent);
|
||||
return;
|
||||
}
|
||||
if (!cons.supportEventTypes.includes(mEvent.getType())) return;
|
||||
if (isEdited(mEvent)) {
|
||||
addToMap(this.editedTimeline, mEvent);
|
||||
return;
|
||||
}
|
||||
this.timeline.push(mEvent);
|
||||
}
|
||||
|
||||
_populateAllLinkedEvents(timeline) {
|
||||
const firstTimeline = getFirstLinkedTimeline(timeline);
|
||||
iterateLinkedTimelines(firstTimeline, false, (tm) => {
|
||||
tm.getEvents().forEach((mEvent) => this.addToTimeline(mEvent));
|
||||
});
|
||||
}
|
||||
|
||||
_populateTimelines() {
|
||||
this.clearLocalTimelines();
|
||||
this._populateAllLinkedEvents(this.activeTimeline);
|
||||
}
|
||||
|
||||
async _reset() {
|
||||
if (this.isEncrypted()) await this.decryptAllEventsOfTimeline(this.activeTimeline);
|
||||
this._populateTimelines();
|
||||
if (!this.initialized) {
|
||||
this.initialized = true;
|
||||
this._listenEvents();
|
||||
}
|
||||
}
|
||||
|
||||
async loadLiveTimeline() {
|
||||
this.activeTimeline = this.liveTimeline;
|
||||
await this._reset();
|
||||
this.emit(cons.events.roomTimeline.READY, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
async loadEventTimeline(eventId) {
|
||||
// we use first unfiltered EventTimelineSet for room pagination.
|
||||
const timelineSet = this.getUnfilteredTimelineSet();
|
||||
try {
|
||||
const eventTimeline = await this.matrixClient.getEventTimeline(timelineSet, eventId);
|
||||
this.activeTimeline = eventTimeline;
|
||||
await this._reset();
|
||||
this.emit(cons.events.roomTimeline.READY, eventId);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async paginateTimeline(backwards = false, limit = 30) {
|
||||
if (this.initialized === false) return false;
|
||||
if (this.isOngoingPagination) return false;
|
||||
|
||||
this.isOngoingPagination = true;
|
||||
|
||||
const timelineToPaginate = backwards
|
||||
? getFirstLinkedTimeline(this.activeTimeline)
|
||||
: getLastLinkedTimeline(this.activeTimeline);
|
||||
|
||||
if (timelineToPaginate.getPaginationToken(backwards ? 'b' : 'f') === null) {
|
||||
this.emit(cons.events.roomTimeline.PAGINATED, backwards, 0);
|
||||
this.isOngoingPagination = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const oldSize = this.timeline.length;
|
||||
try {
|
||||
await this.matrixClient.paginateEventTimeline(timelineToPaginate, { backwards, limit });
|
||||
|
||||
if (this.isEncrypted()) await this.decryptAllEventsOfTimeline(this.activeTimeline);
|
||||
this._populateTimelines();
|
||||
|
||||
const loaded = this.timeline.length - oldSize;
|
||||
this.emit(cons.events.roomTimeline.PAGINATED, backwards, loaded);
|
||||
this.isOngoingPagination = false;
|
||||
return true;
|
||||
} catch {
|
||||
this.emit(cons.events.roomTimeline.PAGINATED, backwards, 0);
|
||||
this.isOngoingPagination = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
decryptAllEventsOfTimeline(eventTimeline) {
|
||||
const decryptionPromises = eventTimeline
|
||||
.getEvents()
|
||||
.filter((event) => event.isEncrypted() && !event.clearEvent)
|
||||
.reverse()
|
||||
.map((event) => event.attemptDecryption(this.matrixClient.crypto, { isRetry: true }));
|
||||
|
||||
return Promise.allSettled(decryptionPromises);
|
||||
}
|
||||
|
||||
hasEventInTimeline(eventId, timeline = this.activeTimeline) {
|
||||
const timelineSet = this.getUnfilteredTimelineSet();
|
||||
const eventTimeline = timelineSet.getTimelineForEvent(eventId);
|
||||
if (!eventTimeline) return false;
|
||||
return isTimelineLinked(eventTimeline, timeline);
|
||||
}
|
||||
|
||||
getUnfilteredTimelineSet() {
|
||||
return this.room.getUnfilteredTimelineSet();
|
||||
}
|
||||
|
||||
getEventReaders(mEvent) {
|
||||
const liveEvents = this.liveTimeline.getEvents();
|
||||
const readers = [];
|
||||
if (!mEvent) return [];
|
||||
|
||||
for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
|
||||
readers.splice(readers.length, 0, ...this.room.getUsersReadUpTo(liveEvents[i]));
|
||||
if (mEvent === liveEvents[i]) break;
|
||||
}
|
||||
|
||||
return [...new Set(readers)];
|
||||
}
|
||||
|
||||
getLiveReaders() {
|
||||
const liveEvents = this.liveTimeline.getEvents();
|
||||
const getLatestVisibleEvent = () => {
|
||||
for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
|
||||
const mEvent = liveEvents[i];
|
||||
if (mEvent.getType() === 'm.room.member' && hideMemberEvents(mEvent)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
if (!mEvent.isRedacted()
|
||||
&& !isReaction(mEvent)
|
||||
&& !isEdited(mEvent)
|
||||
&& cons.supportEventTypes.includes(mEvent.getType())
|
||||
) return mEvent;
|
||||
}
|
||||
return liveEvents[liveEvents.length - 1];
|
||||
};
|
||||
|
||||
return this.getEventReaders(getLatestVisibleEvent());
|
||||
}
|
||||
|
||||
getUnreadEventIndex(readUpToEventId) {
|
||||
if (!this.hasEventInTimeline(readUpToEventId)) return -1;
|
||||
|
||||
const readUpToEvent = this.findEventByIdInTimelineSet(readUpToEventId);
|
||||
if (!readUpToEvent) return -1;
|
||||
const rTs = readUpToEvent.getTs();
|
||||
|
||||
const tLength = this.timeline.length;
|
||||
|
||||
for (let i = 0; i < tLength; i += 1) {
|
||||
const mEvent = this.timeline[i];
|
||||
if (mEvent.getTs() > rTs) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
getReadUpToEventId() {
|
||||
return this.room.getEventReadUpTo(this.matrixClient.getUserId());
|
||||
}
|
||||
|
||||
getEventIndex(eventId) {
|
||||
return this.timeline.findIndex((mEvent) => mEvent.getId() === eventId);
|
||||
}
|
||||
|
||||
findEventByIdInTimelineSet(eventId, eventTimelineSet = this.getUnfilteredTimelineSet()) {
|
||||
return eventTimelineSet.findEventById(eventId);
|
||||
}
|
||||
|
||||
findEventById(eventId) {
|
||||
return this.timeline[this.getEventIndex(eventId)] ?? null;
|
||||
}
|
||||
|
||||
deleteFromTimeline(eventId) {
|
||||
const i = this.getEventIndex(eventId);
|
||||
if (i === -1) return undefined;
|
||||
return this.timeline.splice(i, 1)[0];
|
||||
}
|
||||
|
||||
_listenEvents() {
|
||||
this._listenRoomTimeline = (event, room, toStartOfTimeline, removed, data) => {
|
||||
if (room.roomId !== this.roomId) return;
|
||||
if (this.isOngoingPagination) return;
|
||||
|
||||
// User is currently viewing the old events probably
|
||||
// no need to add new event and emit changes.
|
||||
// only add reactions and edited messages
|
||||
if (this.isServingLiveTimeline() === false) {
|
||||
if (!isReaction(event) && !isEdited(event)) return;
|
||||
}
|
||||
|
||||
// We only process live events here
|
||||
if (!data.liveEvent) return;
|
||||
|
||||
if (event.isEncrypted()) {
|
||||
// We will add this event after it is being decrypted.
|
||||
this.ongoingDecryptionCount += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: An unencrypted plain event can come
|
||||
// while previous event is still decrypting
|
||||
// and has not been added to timeline
|
||||
// causing unordered timeline view.
|
||||
|
||||
this.addToTimeline(event);
|
||||
this.emit(cons.events.roomTimeline.EVENT, event);
|
||||
};
|
||||
|
||||
this._listenDecryptEvent = (event) => {
|
||||
if (event.getRoomId() !== this.roomId) return;
|
||||
if (this.isOngoingPagination) return;
|
||||
|
||||
// Not a live event.
|
||||
// so we don't need to process it here
|
||||
if (this.ongoingDecryptionCount === 0) return;
|
||||
|
||||
if (this.ongoingDecryptionCount > 0) {
|
||||
this.ongoingDecryptionCount -= 1;
|
||||
}
|
||||
this.addToTimeline(event);
|
||||
this.emit(cons.events.roomTimeline.EVENT, event);
|
||||
};
|
||||
|
||||
this._listenRedaction = (mEvent, room) => {
|
||||
if (room.roomId !== this.roomId) return;
|
||||
const rEvent = this.deleteFromTimeline(mEvent.event.redacts);
|
||||
this.editedTimeline.delete(mEvent.event.redacts);
|
||||
this.reactionTimeline.delete(mEvent.event.redacts);
|
||||
this.emit(cons.events.roomTimeline.EVENT_REDACTED, rEvent, mEvent);
|
||||
};
|
||||
|
||||
this._listenTypingEvent = (event, member) => {
|
||||
if (member.roomId !== this.roomId) return;
|
||||
|
||||
const isTyping = member.typing;
|
||||
if (isTyping) this.typingMembers.add(member.userId);
|
||||
else this.typingMembers.delete(member.userId);
|
||||
this.emit(cons.events.roomTimeline.TYPING_MEMBERS_UPDATED, new Set([...this.typingMembers]));
|
||||
};
|
||||
this._listenReciptEvent = (event, room) => {
|
||||
// we only process receipt for latest message here.
|
||||
if (room.roomId !== this.roomId) return;
|
||||
const receiptContent = event.getContent();
|
||||
|
||||
const mEvents = this.liveTimeline.getEvents();
|
||||
const lastMEvent = mEvents[mEvents.length - 1];
|
||||
const lastEventId = lastMEvent.getId();
|
||||
const lastEventRecipt = receiptContent[lastEventId];
|
||||
|
||||
if (typeof lastEventRecipt === 'undefined') return;
|
||||
if (lastEventRecipt['m.read']) {
|
||||
this.emit(cons.events.roomTimeline.LIVE_RECEIPT);
|
||||
}
|
||||
};
|
||||
|
||||
this.matrixClient.on('Room.timeline', this._listenRoomTimeline);
|
||||
this.matrixClient.on('Room.redaction', this._listenRedaction);
|
||||
this.matrixClient.on('Event.decrypted', this._listenDecryptEvent);
|
||||
this.matrixClient.on('RoomMember.typing', this._listenTypingEvent);
|
||||
this.matrixClient.on('Room.receipt', this._listenReciptEvent);
|
||||
}
|
||||
|
||||
removeInternalListeners() {
|
||||
if (!this.initialized) return;
|
||||
this.matrixClient.removeListener('Room.timeline', this._listenRoomTimeline);
|
||||
this.matrixClient.removeListener('Room.redaction', this._listenRedaction);
|
||||
this.matrixClient.removeListener('Event.decrypted', this._listenDecryptEvent);
|
||||
this.matrixClient.removeListener('RoomMember.typing', this._listenTypingEvent);
|
||||
this.matrixClient.removeListener('Room.receipt', this._listenReciptEvent);
|
||||
}
|
||||
}
|
||||
|
||||
export default RoomTimeline;
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import { RoomHierarchy } from 'matrix-js-sdk/lib/room-hierarchy';
|
||||
|
||||
class RoomsHierarchy {
|
||||
constructor(matrixClient, limit = 20, maxDepth = 1, suggestedOnly = false) {
|
||||
this.matrixClient = matrixClient;
|
||||
this._maxDepth = maxDepth;
|
||||
this._suggestedOnly = suggestedOnly;
|
||||
this._limit = limit;
|
||||
|
||||
this.roomIdToHierarchy = new Map();
|
||||
}
|
||||
|
||||
getHierarchy(roomId) {
|
||||
return this.roomIdToHierarchy.get(roomId);
|
||||
}
|
||||
|
||||
removeHierarchy(roomId) {
|
||||
return this.roomIdToHierarchy.delete(roomId);
|
||||
}
|
||||
|
||||
canLoadMore(roomId) {
|
||||
const roomHierarchy = this.getHierarchy(roomId);
|
||||
if (!roomHierarchy) return true;
|
||||
return roomHierarchy.canLoadMore;
|
||||
}
|
||||
|
||||
async load(roomId, limit = this._limit) {
|
||||
let roomHierarchy = this.getHierarchy(roomId);
|
||||
|
||||
if (!roomHierarchy) {
|
||||
roomHierarchy = new RoomHierarchy(
|
||||
{ roomId, client: this.matrixClient },
|
||||
limit,
|
||||
this._maxDepth,
|
||||
this._suggestedOnly,
|
||||
);
|
||||
this.roomIdToHierarchy.set(roomId, roomHierarchy);
|
||||
}
|
||||
|
||||
try {
|
||||
await roomHierarchy.load(limit);
|
||||
return roomHierarchy.rooms;
|
||||
} catch {
|
||||
return roomHierarchy.rooms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default RoomsHierarchy;
|
||||
|
|
@ -1,423 +0,0 @@
|
|||
import EventEmitter from 'events';
|
||||
import encrypt from 'browser-encrypt-attachment';
|
||||
import { encode } from 'blurhash';
|
||||
import { getShortcodeToEmoji } from '../../app/organisms/emoji-board/custom-emoji';
|
||||
import { getBlobSafeMimeType } from '../../util/mimetypes';
|
||||
import { sanitizeText } from '../../util/sanitize';
|
||||
import cons from './cons';
|
||||
import settings from './settings';
|
||||
import { markdown, plain } from '../../util/markdown';
|
||||
|
||||
const blurhashField = 'xyz.amorgan.blurhash';
|
||||
|
||||
function encodeBlurhash(img) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 100;
|
||||
canvas.height = 100;
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
const data = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||
return encode(data.data, data.width, data.height, 4, 4);
|
||||
}
|
||||
|
||||
function loadImage(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.onerror = (err) => reject(err);
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
|
||||
function loadVideo(videoFile) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const video = document.createElement('video');
|
||||
video.preload = 'metadata';
|
||||
video.playsInline = true;
|
||||
video.muted = true;
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = (ev) => {
|
||||
// Wait until we have enough data to thumbnail the first frame.
|
||||
video.onloadeddata = async () => {
|
||||
resolve(video);
|
||||
video.pause();
|
||||
};
|
||||
video.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
|
||||
video.src = ev.target.result;
|
||||
video.load();
|
||||
video.play();
|
||||
};
|
||||
reader.onerror = (e) => {
|
||||
reject(e);
|
||||
};
|
||||
if (videoFile.type === 'video/quicktime') {
|
||||
const quicktimeVideoFile = new File([videoFile], videoFile.name, { type: 'video/mp4' });
|
||||
reader.readAsDataURL(quicktimeVideoFile);
|
||||
} else {
|
||||
reader.readAsDataURL(videoFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
function getVideoThumbnail(video, width, height, mimeType) {
|
||||
return new Promise((resolve) => {
|
||||
const MAX_WIDTH = 800;
|
||||
const MAX_HEIGHT = 600;
|
||||
let targetWidth = width;
|
||||
let targetHeight = height;
|
||||
if (targetHeight > MAX_HEIGHT) {
|
||||
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
|
||||
targetHeight = MAX_HEIGHT;
|
||||
}
|
||||
if (targetWidth > MAX_WIDTH) {
|
||||
targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth));
|
||||
targetWidth = MAX_WIDTH;
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
const context = canvas.getContext('2d');
|
||||
context.drawImage(video, 0, 0, targetWidth, targetHeight);
|
||||
|
||||
canvas.toBlob((thumbnail) => {
|
||||
resolve({
|
||||
thumbnail,
|
||||
info: {
|
||||
w: targetWidth,
|
||||
h: targetHeight,
|
||||
mimetype: thumbnail.type,
|
||||
size: thumbnail.size,
|
||||
},
|
||||
});
|
||||
}, mimeType);
|
||||
});
|
||||
}
|
||||
|
||||
class RoomsInput extends EventEmitter {
|
||||
constructor(mx, roomList) {
|
||||
super();
|
||||
|
||||
this.matrixClient = mx;
|
||||
this.roomList = roomList;
|
||||
this.roomIdToInput = new Map();
|
||||
}
|
||||
|
||||
cleanEmptyEntry(roomId) {
|
||||
const input = this.getInput(roomId);
|
||||
const isEmpty = typeof input.attachment === 'undefined'
|
||||
&& typeof input.replyTo === 'undefined'
|
||||
&& (typeof input.message === 'undefined' || input.message === '');
|
||||
if (isEmpty) {
|
||||
this.roomIdToInput.delete(roomId);
|
||||
}
|
||||
}
|
||||
|
||||
getInput(roomId) {
|
||||
return this.roomIdToInput.get(roomId) || {};
|
||||
}
|
||||
|
||||
setMessage(roomId, message) {
|
||||
const input = this.getInput(roomId);
|
||||
input.message = message;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
if (message === '') this.cleanEmptyEntry(roomId);
|
||||
}
|
||||
|
||||
getMessage(roomId) {
|
||||
const input = this.getInput(roomId);
|
||||
if (typeof input.message === 'undefined') return '';
|
||||
return input.message;
|
||||
}
|
||||
|
||||
setReplyTo(roomId, replyTo) {
|
||||
const input = this.getInput(roomId);
|
||||
input.replyTo = replyTo;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
}
|
||||
|
||||
getReplyTo(roomId) {
|
||||
const input = this.getInput(roomId);
|
||||
if (typeof input.replyTo === 'undefined') return null;
|
||||
return input.replyTo;
|
||||
}
|
||||
|
||||
cancelReplyTo(roomId) {
|
||||
const input = this.getInput(roomId);
|
||||
if (typeof input.replyTo === 'undefined') return;
|
||||
delete input.replyTo;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
}
|
||||
|
||||
setAttachment(roomId, file) {
|
||||
const input = this.getInput(roomId);
|
||||
input.attachment = {
|
||||
file,
|
||||
};
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
}
|
||||
|
||||
getAttachment(roomId) {
|
||||
const input = this.getInput(roomId);
|
||||
if (typeof input.attachment === 'undefined') return null;
|
||||
return input.attachment.file;
|
||||
}
|
||||
|
||||
cancelAttachment(roomId) {
|
||||
const input = this.getInput(roomId);
|
||||
if (typeof input.attachment === 'undefined') return;
|
||||
|
||||
const { uploadingPromise } = input.attachment;
|
||||
|
||||
if (uploadingPromise) {
|
||||
this.matrixClient.cancelUpload(uploadingPromise);
|
||||
delete input.attachment.uploadingPromise;
|
||||
}
|
||||
delete input.attachment;
|
||||
delete input.isSending;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
this.emit(cons.events.roomsInput.ATTACHMENT_CANCELED, roomId);
|
||||
}
|
||||
|
||||
isSending(roomId) {
|
||||
return this.roomIdToInput.get(roomId)?.isSending || false;
|
||||
}
|
||||
|
||||
getContent(roomId, options, message, reply, edit) {
|
||||
const msgType = options?.msgType || 'm.text';
|
||||
const autoMarkdown = options?.autoMarkdown ?? true;
|
||||
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
|
||||
const userNames = room.currentState.userIdsToDisplayNames;
|
||||
const parentIds = this.roomList.getAllParentSpaces(room.roomId);
|
||||
const parentRooms = [...parentIds].map((id) => this.matrixClient.getRoom(id));
|
||||
const emojis = getShortcodeToEmoji(this.matrixClient, [room, ...parentRooms]);
|
||||
|
||||
const output = settings.isMarkdown && autoMarkdown ? markdown : plain;
|
||||
const body = output(message, { userNames, emojis });
|
||||
|
||||
const content = {
|
||||
body: body.plain,
|
||||
msgtype: msgType,
|
||||
};
|
||||
|
||||
if (!body.onlyPlain || reply) {
|
||||
content.format = 'org.matrix.custom.html';
|
||||
content.formatted_body = body.html;
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
content['m.new_content'] = { ...content };
|
||||
content['m.relates_to'] = {
|
||||
event_id: edit.getId(),
|
||||
rel_type: 'm.replace',
|
||||
};
|
||||
|
||||
const isReply = edit.getWireContent()['m.relates_to']?.['m.in_reply_to'];
|
||||
if (isReply) {
|
||||
content.format = 'org.matrix.custom.html';
|
||||
content.formatted_body = body.html;
|
||||
}
|
||||
|
||||
content.body = ` * ${content.body}`;
|
||||
if (content.formatted_body) content.formatted_body = ` * ${content.formatted_body}`;
|
||||
|
||||
if (isReply) {
|
||||
const eBody = edit.getContent().body;
|
||||
const replyHead = eBody.substring(0, eBody.indexOf('\n\n'));
|
||||
if (replyHead) content.body = `${replyHead}\n\n${content.body}`;
|
||||
|
||||
const eFBody = edit.getContent().formatted_body;
|
||||
const fReplyHead = eFBody.substring(0, eFBody.indexOf('</mx-reply>'));
|
||||
if (fReplyHead) content.formatted_body = `${fReplyHead}</mx-reply>${content.formatted_body}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
content['m.relates_to'] = {
|
||||
'm.in_reply_to': {
|
||||
event_id: reply.eventId,
|
||||
},
|
||||
};
|
||||
|
||||
content.body = `> <${reply.userId}> ${reply.body.replace(/\n/g, '\n> ')}\n\n${content.body}`;
|
||||
|
||||
const replyToLink = `<a href="https://matrix.to/#/${encodeURIComponent(roomId)}/${encodeURIComponent(reply.eventId)}">In reply to</a>`;
|
||||
const userLink = `<a href="https://matrix.to/#/${encodeURIComponent(reply.userId)}">${sanitizeText(reply.userId)}</a>`;
|
||||
const fallback = `<mx-reply><blockquote>${replyToLink}${userLink}<br />${reply.formattedBody || sanitizeText(reply.body)}</blockquote></mx-reply>`;
|
||||
content.formatted_body = fallback + content.formatted_body;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
async sendInput(roomId, options) {
|
||||
const input = this.getInput(roomId);
|
||||
input.isSending = true;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
if (input.attachment) {
|
||||
await this.sendFile(roomId, input.attachment.file);
|
||||
if (!this.isSending(roomId)) return;
|
||||
}
|
||||
|
||||
if (this.getMessage(roomId).trim() !== '') {
|
||||
const content = this.getContent(roomId, options, input.message, input.replyTo);
|
||||
this.matrixClient.sendMessage(roomId, content);
|
||||
}
|
||||
|
||||
if (this.isSending(roomId)) this.roomIdToInput.delete(roomId);
|
||||
this.emit(cons.events.roomsInput.MESSAGE_SENT, roomId);
|
||||
}
|
||||
|
||||
async sendSticker(roomId, data) {
|
||||
const { mxc: url, body, httpUrl } = data;
|
||||
const info = {};
|
||||
|
||||
const img = new Image();
|
||||
img.src = httpUrl;
|
||||
|
||||
try {
|
||||
const res = await fetch(httpUrl);
|
||||
const blob = await res.blob();
|
||||
info.w = img.width;
|
||||
info.h = img.height;
|
||||
info.mimetype = blob.type;
|
||||
info.size = blob.size;
|
||||
info.thumbnail_info = { ...info };
|
||||
info.thumbnail_url = url;
|
||||
} catch {
|
||||
// send sticker without info
|
||||
}
|
||||
|
||||
this.matrixClient.sendEvent(roomId, 'm.sticker', {
|
||||
body,
|
||||
url,
|
||||
info,
|
||||
});
|
||||
this.emit(cons.events.roomsInput.MESSAGE_SENT, roomId);
|
||||
}
|
||||
|
||||
async sendFile(roomId, file) {
|
||||
const fileType = getBlobSafeMimeType(file.type).slice(0, file.type.indexOf('/'));
|
||||
const info = {
|
||||
mimetype: file.type,
|
||||
size: file.size,
|
||||
};
|
||||
const content = { info };
|
||||
let uploadData = null;
|
||||
|
||||
if (fileType === 'image') {
|
||||
const img = await loadImage(URL.createObjectURL(file));
|
||||
|
||||
info.w = img.width;
|
||||
info.h = img.height;
|
||||
info[blurhashField] = encodeBlurhash(img);
|
||||
|
||||
content.msgtype = 'm.image';
|
||||
content.body = file.name || 'Image';
|
||||
} else if (fileType === 'video') {
|
||||
content.msgtype = 'm.video';
|
||||
content.body = file.name || 'Video';
|
||||
|
||||
try {
|
||||
const video = await loadVideo(file);
|
||||
|
||||
info.w = video.videoWidth;
|
||||
info.h = video.videoHeight;
|
||||
info[blurhashField] = encodeBlurhash(video);
|
||||
|
||||
const thumbnailData = await getVideoThumbnail(video, video.videoWidth, video.videoHeight, 'image/jpeg');
|
||||
const thumbnailUploadData = await this.uploadFile(roomId, thumbnailData.thumbnail);
|
||||
info.thumbnail_info = thumbnailData.info;
|
||||
if (this.matrixClient.isRoomEncrypted(roomId)) {
|
||||
info.thumbnail_file = thumbnailUploadData.file;
|
||||
} else {
|
||||
info.thumbnail_url = thumbnailUploadData.url;
|
||||
}
|
||||
} catch (e) {
|
||||
this.emit(cons.events.roomsInput.FILE_UPLOAD_CANCELED, roomId);
|
||||
return;
|
||||
}
|
||||
} else if (fileType === 'audio') {
|
||||
content.msgtype = 'm.audio';
|
||||
content.body = file.name || 'Audio';
|
||||
} else {
|
||||
content.msgtype = 'm.file';
|
||||
content.body = file.name || 'File';
|
||||
}
|
||||
|
||||
try {
|
||||
uploadData = await this.uploadFile(roomId, file, (data) => {
|
||||
// data have two properties: data.loaded, data.total
|
||||
this.emit(cons.events.roomsInput.UPLOAD_PROGRESS_CHANGES, roomId, data);
|
||||
});
|
||||
this.emit(cons.events.roomsInput.FILE_UPLOADED, roomId);
|
||||
} catch (e) {
|
||||
this.emit(cons.events.roomsInput.FILE_UPLOAD_CANCELED, roomId);
|
||||
return;
|
||||
}
|
||||
if (this.matrixClient.isRoomEncrypted(roomId)) {
|
||||
content.file = uploadData.file;
|
||||
await this.matrixClient.sendMessage(roomId, content);
|
||||
} else {
|
||||
content.url = uploadData.url;
|
||||
await this.matrixClient.sendMessage(roomId, content);
|
||||
}
|
||||
}
|
||||
|
||||
async uploadFile(roomId, file, progressHandler) {
|
||||
const isEncryptedRoom = this.matrixClient.isRoomEncrypted(roomId);
|
||||
|
||||
let encryptInfo = null;
|
||||
let encryptBlob = null;
|
||||
|
||||
if (isEncryptedRoom) {
|
||||
const dataBuffer = await file.arrayBuffer();
|
||||
if (typeof this.getInput(roomId).attachment === 'undefined') throw new Error('Attachment canceled');
|
||||
const encryptedResult = await encrypt.encryptAttachment(dataBuffer);
|
||||
if (typeof this.getInput(roomId).attachment === 'undefined') throw new Error('Attachment canceled');
|
||||
encryptInfo = encryptedResult.info;
|
||||
encryptBlob = new Blob([encryptedResult.data]);
|
||||
}
|
||||
|
||||
const uploadingPromise = this.matrixClient.uploadContent(isEncryptedRoom ? encryptBlob : file, {
|
||||
// don't send filename if room is encrypted.
|
||||
includeFilename: !isEncryptedRoom,
|
||||
progressHandler,
|
||||
});
|
||||
|
||||
const input = this.getInput(roomId);
|
||||
input.attachment.uploadingPromise = uploadingPromise;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
|
||||
const { content_uri: url } = await uploadingPromise;
|
||||
|
||||
delete input.attachment.uploadingPromise;
|
||||
this.roomIdToInput.set(roomId, input);
|
||||
|
||||
if (isEncryptedRoom) {
|
||||
encryptInfo.url = url;
|
||||
if (file.type) encryptInfo.mimetype = file.type;
|
||||
return { file: encryptInfo };
|
||||
}
|
||||
return { url };
|
||||
}
|
||||
|
||||
async sendEditedMessage(roomId, mEvent, editedBody) {
|
||||
const content = this.getContent(
|
||||
roomId,
|
||||
{ msgType: mEvent.getWireContent().msgtype },
|
||||
editedBody,
|
||||
null,
|
||||
mEvent,
|
||||
);
|
||||
this.matrixClient.sendMessage(roomId, content);
|
||||
}
|
||||
}
|
||||
|
||||
export default RoomsInput;
|
||||
|
|
@ -8,10 +8,6 @@ const cons = {
|
|||
},
|
||||
DEVICE_DISPLAY_NAME: 'Cinny Web',
|
||||
IN_CINNY_SPACES: 'in.cinny.spaces',
|
||||
tabs: {
|
||||
HOME: 'home',
|
||||
DIRECTS: 'dm',
|
||||
},
|
||||
supportEventTypes: [
|
||||
'm.room.create',
|
||||
'm.room.message',
|
||||
|
|
@ -37,43 +33,19 @@ const cons = {
|
|||
},
|
||||
actions: {
|
||||
navigation: {
|
||||
SELECT_TAB: 'SELECT_TAB',
|
||||
SELECT_SPACE: 'SELECT_SPACE',
|
||||
SELECT_ROOM: 'SELECT_ROOM',
|
||||
OPEN_SPACE_SETTINGS: 'OPEN_SPACE_SETTINGS',
|
||||
OPEN_SPACE_MANAGE: 'OPEN_SPACE_MANAGE',
|
||||
OPEN_SPACE_ADDEXISTING: 'OPEN_SPACE_ADDEXISTING',
|
||||
TOGGLE_ROOM_SETTINGS: 'TOGGLE_ROOM_SETTINGS',
|
||||
OPEN_SHORTCUT_SPACES: 'OPEN_SHORTCUT_SPACES',
|
||||
OPEN_INVITE_LIST: 'OPEN_INVITE_LIST',
|
||||
OPEN_PUBLIC_ROOMS: 'OPEN_PUBLIC_ROOMS',
|
||||
OPEN_CREATE_ROOM: 'OPEN_CREATE_ROOM',
|
||||
OPEN_JOIN_ALIAS: 'OPEN_JOIN_ALIAS',
|
||||
OPEN_INVITE_USER: 'OPEN_INVITE_USER',
|
||||
OPEN_PROFILE_VIEWER: 'OPEN_PROFILE_VIEWER',
|
||||
OPEN_SETTINGS: 'OPEN_SETTINGS',
|
||||
OPEN_EMOJIBOARD: 'OPEN_EMOJIBOARD',
|
||||
OPEN_READRECEIPTS: 'OPEN_READRECEIPTS',
|
||||
OPEN_VIEWSOURCE: 'OPEN_VIEWSOURCE',
|
||||
CLICK_REPLY_TO: 'CLICK_REPLY_TO',
|
||||
OPEN_SEARCH: 'OPEN_SEARCH',
|
||||
OPEN_REUSABLE_CONTEXT_MENU: 'OPEN_REUSABLE_CONTEXT_MENU',
|
||||
OPEN_NAVIGATION: 'OPEN_NAVIGATION',
|
||||
OPEN_REUSABLE_DIALOG: 'OPEN_REUSABLE_DIALOG',
|
||||
OPEN_EMOJI_VERIFICATION: 'OPEN_EMOJI_VERIFICATION',
|
||||
},
|
||||
room: {
|
||||
JOIN: 'JOIN',
|
||||
LEAVE: 'LEAVE',
|
||||
CREATE: 'CREATE',
|
||||
},
|
||||
accountData: {
|
||||
CREATE_SPACE_SHORTCUT: 'CREATE_SPACE_SHORTCUT',
|
||||
DELETE_SPACE_SHORTCUT: 'DELETE_SPACE_SHORTCUT',
|
||||
MOVE_SPACE_SHORTCUTS: 'MOVE_SPACE_SHORTCUTS',
|
||||
CATEGORIZE_SPACE: 'CATEGORIZE_SPACE',
|
||||
UNCATEGORIZE_SPACE: 'UNCATEGORIZE_SPACE',
|
||||
},
|
||||
settings: {
|
||||
TOGGLE_SYSTEM_THEME: 'TOGGLE_SYSTEM_THEME',
|
||||
TOGGLE_MARKDOWN: 'TOGGLE_MARKDOWN',
|
||||
|
|
@ -86,66 +58,23 @@ const cons = {
|
|||
},
|
||||
events: {
|
||||
navigation: {
|
||||
TAB_SELECTED: 'TAB_SELECTED',
|
||||
SPACE_SELECTED: 'SPACE_SELECTED',
|
||||
ROOM_SELECTED: 'ROOM_SELECTED',
|
||||
SPACE_SETTINGS_OPENED: 'SPACE_SETTINGS_OPENED',
|
||||
SPACE_MANAGE_OPENED: 'SPACE_MANAGE_OPENED',
|
||||
SPACE_ADDEXISTING_OPENED: 'SPACE_ADDEXISTING_OPENED',
|
||||
ROOM_SETTINGS_TOGGLED: 'ROOM_SETTINGS_TOGGLED',
|
||||
SHORTCUT_SPACES_OPENED: 'SHORTCUT_SPACES_OPENED',
|
||||
INVITE_LIST_OPENED: 'INVITE_LIST_OPENED',
|
||||
PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED',
|
||||
CREATE_ROOM_OPENED: 'CREATE_ROOM_OPENED',
|
||||
JOIN_ALIAS_OPENED: 'JOIN_ALIAS_OPENED',
|
||||
INVITE_USER_OPENED: 'INVITE_USER_OPENED',
|
||||
SETTINGS_OPENED: 'SETTINGS_OPENED',
|
||||
PROFILE_VIEWER_OPENED: 'PROFILE_VIEWER_OPENED',
|
||||
EMOJIBOARD_OPENED: 'EMOJIBOARD_OPENED',
|
||||
READRECEIPTS_OPENED: 'READRECEIPTS_OPENED',
|
||||
VIEWSOURCE_OPENED: 'VIEWSOURCE_OPENED',
|
||||
REPLY_TO_CLICKED: 'REPLY_TO_CLICKED',
|
||||
SEARCH_OPENED: 'SEARCH_OPENED',
|
||||
REUSABLE_CONTEXT_MENU_OPENED: 'REUSABLE_CONTEXT_MENU_OPENED',
|
||||
NAVIGATION_OPENED: 'NAVIGATION_OPENED',
|
||||
REUSABLE_DIALOG_OPENED: 'REUSABLE_DIALOG_OPENED',
|
||||
EMOJI_VERIFICATION_OPENED: 'EMOJI_VERIFICATION_OPENED',
|
||||
},
|
||||
roomList: {
|
||||
ROOMLIST_UPDATED: 'ROOMLIST_UPDATED',
|
||||
INVITELIST_UPDATED: 'INVITELIST_UPDATED',
|
||||
ROOM_JOINED: 'ROOM_JOINED',
|
||||
ROOM_LEAVED: 'ROOM_LEAVED',
|
||||
ROOM_CREATED: 'ROOM_CREATED',
|
||||
ROOM_PROFILE_UPDATED: 'ROOM_PROFILE_UPDATED',
|
||||
},
|
||||
accountData: {
|
||||
SPACE_SHORTCUT_UPDATED: 'SPACE_SHORTCUT_UPDATED',
|
||||
CATEGORIZE_SPACE_UPDATED: 'CATEGORIZE_SPACE_UPDATED',
|
||||
},
|
||||
notifications: {
|
||||
NOTI_CHANGED: 'NOTI_CHANGED',
|
||||
FULL_READ: 'FULL_READ',
|
||||
MUTE_TOGGLED: 'MUTE_TOGGLED',
|
||||
},
|
||||
roomTimeline: {
|
||||
READY: 'READY',
|
||||
EVENT: 'EVENT',
|
||||
PAGINATED: 'PAGINATED',
|
||||
TYPING_MEMBERS_UPDATED: 'TYPING_MEMBERS_UPDATED',
|
||||
LIVE_RECEIPT: 'LIVE_RECEIPT',
|
||||
EVENT_REDACTED: 'EVENT_REDACTED',
|
||||
AT_BOTTOM: 'AT_BOTTOM',
|
||||
SCROLL_TO_LIVE: 'SCROLL_TO_LIVE',
|
||||
},
|
||||
roomsInput: {
|
||||
MESSAGE_SENT: 'MESSAGE_SENT',
|
||||
ATTACHMENT_SET: 'ATTACHMENT_SET',
|
||||
FILE_UPLOADED: 'FILE_UPLOADED',
|
||||
UPLOAD_PROGRESS_CHANGES: 'UPLOAD_PROGRESS_CHANGES',
|
||||
FILE_UPLOAD_CANCELED: 'FILE_UPLOAD_CANCELED',
|
||||
ATTACHMENT_CANCELED: 'ATTACHMENT_CANCELED',
|
||||
},
|
||||
settings: {
|
||||
SYSTEM_THEME_TOGGLED: 'SYSTEM_THEME_TOGGLED',
|
||||
MARKDOWN_TOGGLED: 'MARKDOWN_TOGGLED',
|
||||
|
|
|
|||
|
|
@ -5,268 +5,9 @@ import cons from './cons';
|
|||
class Navigation extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
// this will attached by initMatrix
|
||||
this.initMatrix = {};
|
||||
|
||||
this.selectedTab = cons.tabs.HOME;
|
||||
this.selectedSpaceId = null;
|
||||
this.selectedSpacePath = [cons.tabs.HOME];
|
||||
|
||||
this.selectedRoomId = null;
|
||||
this.recentRooms = [];
|
||||
|
||||
this.spaceToRoom = new Map();
|
||||
|
||||
this.rawModelStack = [];
|
||||
}
|
||||
|
||||
_addToSpacePath(roomId, asRoot) {
|
||||
if (typeof roomId !== 'string') {
|
||||
this.selectedSpacePath = [cons.tabs.HOME];
|
||||
return;
|
||||
}
|
||||
if (asRoot) {
|
||||
this.selectedSpacePath = [roomId];
|
||||
return;
|
||||
}
|
||||
if (this.selectedSpacePath.includes(roomId)) {
|
||||
const spIndex = this.selectedSpacePath.indexOf(roomId);
|
||||
this.selectedSpacePath = this.selectedSpacePath.slice(0, spIndex + 1);
|
||||
return;
|
||||
}
|
||||
this.selectedSpacePath.push(roomId);
|
||||
}
|
||||
|
||||
_mapRoomToSpace(roomId) {
|
||||
const { roomList, accountData } = this.initMatrix;
|
||||
if (
|
||||
this.selectedTab === cons.tabs.HOME
|
||||
&& roomList.rooms.has(roomId)
|
||||
&& !roomList.roomIdToParents.has(roomId)
|
||||
) {
|
||||
this.spaceToRoom.set(cons.tabs.HOME, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.selectedTab === cons.tabs.DIRECTS && roomList.directs.has(roomId)) {
|
||||
this.spaceToRoom.set(cons.tabs.DIRECTS, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const parents = roomList.roomIdToParents.get(roomId);
|
||||
if (!parents) return;
|
||||
if (parents.has(this.selectedSpaceId)) {
|
||||
this.spaceToRoom.set(this.selectedSpaceId, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} else if (accountData.categorizedSpaces.has(this.selectedSpaceId)) {
|
||||
const categories = roomList.getCategorizedSpaces([this.selectedSpaceId]);
|
||||
const parent = [...parents].find((pId) => categories.has(pId));
|
||||
if (parent) {
|
||||
this.spaceToRoom.set(parent, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_selectRoom(roomId, eventId) {
|
||||
const prevSelectedRoomId = this.selectedRoomId;
|
||||
this.selectedRoomId = roomId;
|
||||
if (prevSelectedRoomId !== roomId) this._mapRoomToSpace(roomId);
|
||||
this.removeRecentRoom(prevSelectedRoomId);
|
||||
this.addRecentRoom(prevSelectedRoomId);
|
||||
this.removeRecentRoom(this.selectedRoomId);
|
||||
this.emit(
|
||||
cons.events.navigation.ROOM_SELECTED,
|
||||
this.selectedRoomId,
|
||||
prevSelectedRoomId,
|
||||
eventId,
|
||||
);
|
||||
}
|
||||
|
||||
_selectTabWithRoom(roomId) {
|
||||
const { roomList, accountData } = this.initMatrix;
|
||||
const { categorizedSpaces } = accountData;
|
||||
|
||||
if (roomList.isOrphan(roomId)) {
|
||||
if (roomList.directs.has(roomId)) {
|
||||
this._selectSpace(null, true, false);
|
||||
this._selectTab(cons.tabs.DIRECTS, false);
|
||||
return;
|
||||
}
|
||||
this._selectSpace(null, true, false);
|
||||
this._selectTab(cons.tabs.HOME, false);
|
||||
return;
|
||||
}
|
||||
|
||||
const parents = roomList.roomIdToParents.get(roomId);
|
||||
|
||||
if (parents.has(this.selectedSpaceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (categorizedSpaces.has(this.selectedSpaceId)) {
|
||||
const categories = roomList.getCategorizedSpaces([this.selectedSpaceId]);
|
||||
if ([...parents].find((pId) => categories.has(pId))) {
|
||||
// No need to select tab
|
||||
// As one of parent is child of selected categorized space.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const spaceInPath = [...this.selectedSpacePath].reverse().find((sId) => parents.has(sId));
|
||||
if (spaceInPath) {
|
||||
this._selectSpace(spaceInPath, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomList.directs.has(roomId)) {
|
||||
this._selectSpace(null, true, false);
|
||||
this._selectTab(cons.tabs.DIRECTS, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parents.size > 0) {
|
||||
const sortedParents = [...parents].sort((p1, p2) => {
|
||||
const t1 = this.spaceToRoom.get(p1)?.timestamp ?? 0;
|
||||
const t2 = this.spaceToRoom.get(p2)?.timestamp ?? 0;
|
||||
return t2 - t1;
|
||||
});
|
||||
this._selectSpace(sortedParents[0], true, false);
|
||||
this._selectTab(sortedParents[0], false);
|
||||
}
|
||||
}
|
||||
|
||||
_getLatestActiveRoomId(roomIds) {
|
||||
const mx = this.initMatrix.matrixClient;
|
||||
|
||||
let ts = 0;
|
||||
let roomId = null;
|
||||
roomIds.forEach((childId) => {
|
||||
const room = mx.getRoom(childId);
|
||||
if (!room) return;
|
||||
const newTs = room.getLastActiveTimestamp();
|
||||
if (newTs > ts) {
|
||||
ts = newTs;
|
||||
roomId = childId;
|
||||
}
|
||||
});
|
||||
return roomId;
|
||||
}
|
||||
|
||||
_getLatestSelectedRoomId(spaceIds) {
|
||||
let ts = 0;
|
||||
let roomId = null;
|
||||
|
||||
spaceIds.forEach((sId) => {
|
||||
const data = this.spaceToRoom.get(sId);
|
||||
if (!data) return;
|
||||
const newTs = data.timestamp;
|
||||
if (newTs > ts) {
|
||||
ts = newTs;
|
||||
roomId = data.roomId;
|
||||
}
|
||||
});
|
||||
return roomId;
|
||||
}
|
||||
|
||||
_selectTab(tabId, selectRoom = true) {
|
||||
this.selectedTab = tabId;
|
||||
if (selectRoom) this._selectRoomWithTab(this.selectedTab);
|
||||
this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
|
||||
}
|
||||
|
||||
_selectSpace(roomId, asRoot, selectRoom = true) {
|
||||
this._addToSpacePath(roomId, asRoot);
|
||||
this.selectedSpaceId = roomId;
|
||||
if (!asRoot && selectRoom) this._selectRoomWithSpace(this.selectedSpaceId);
|
||||
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
|
||||
}
|
||||
|
||||
_selectRoomWithSpace(spaceId) {
|
||||
if (!spaceId) return;
|
||||
const { roomList, accountData, matrixClient } = this.initMatrix;
|
||||
const { categorizedSpaces } = accountData;
|
||||
|
||||
const data = this.spaceToRoom.get(spaceId);
|
||||
if (data && !categorizedSpaces.has(spaceId)) {
|
||||
this._selectRoom(data.roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
const children = [];
|
||||
|
||||
if (categorizedSpaces.has(spaceId)) {
|
||||
const categories = roomList.getCategorizedSpaces([spaceId]);
|
||||
|
||||
const latestSelectedRoom = this._getLatestSelectedRoomId([...categories.keys()]);
|
||||
|
||||
if (latestSelectedRoom) {
|
||||
this._selectRoom(latestSelectedRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
categories?.forEach((categoryId) => {
|
||||
categoryId?.forEach((childId) => {
|
||||
children.push(childId);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
roomList.getSpaceChildren(spaceId).forEach((id) => {
|
||||
if (matrixClient.getRoom(id)?.isSpaceRoom() === false) {
|
||||
children.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!children) {
|
||||
this._selectRoom(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectRoom(this._getLatestActiveRoomId(children));
|
||||
}
|
||||
|
||||
_selectRoomWithTab(tabId) {
|
||||
const { roomList } = this.initMatrix;
|
||||
if (tabId === cons.tabs.HOME || tabId === cons.tabs.DIRECTS) {
|
||||
const data = this.spaceToRoom.get(tabId);
|
||||
if (data) {
|
||||
this._selectRoom(data.roomId);
|
||||
return;
|
||||
}
|
||||
const children = tabId === cons.tabs.HOME ? roomList.getOrphanRooms() : [...roomList.directs];
|
||||
this._selectRoom(this._getLatestActiveRoomId(children));
|
||||
return;
|
||||
}
|
||||
this._selectRoomWithSpace(tabId);
|
||||
}
|
||||
|
||||
removeRecentRoom(roomId) {
|
||||
if (typeof roomId !== 'string') return;
|
||||
const roomIdIndex = this.recentRooms.indexOf(roomId);
|
||||
if (roomIdIndex >= 0) {
|
||||
this.recentRooms.splice(roomIdIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
addRecentRoom(roomId) {
|
||||
if (typeof roomId !== 'string') return;
|
||||
|
||||
this.recentRooms.push(roomId);
|
||||
if (this.recentRooms.length > 10) {
|
||||
this.recentRooms.splice(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
get isRawModalVisible() {
|
||||
return this.rawModelStack.length > 0;
|
||||
}
|
||||
|
|
@ -278,27 +19,9 @@ class Navigation extends EventEmitter {
|
|||
|
||||
navigate(action) {
|
||||
const actions = {
|
||||
[cons.actions.navigation.SELECT_TAB]: () => {
|
||||
const roomId = (
|
||||
action.tabId !== cons.tabs.HOME && action.tabId !== cons.tabs.DIRECTS
|
||||
) ? action.tabId : null;
|
||||
|
||||
this._selectSpace(roomId, true);
|
||||
this._selectTab(action.tabId);
|
||||
},
|
||||
[cons.actions.navigation.SELECT_SPACE]: () => {
|
||||
this._selectSpace(action.roomId, false);
|
||||
},
|
||||
[cons.actions.navigation.SELECT_ROOM]: () => {
|
||||
if (action.roomId) this._selectTabWithRoom(action.roomId);
|
||||
this._selectRoom(action.roomId, action.eventId);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_SPACE_SETTINGS]: () => {
|
||||
this.emit(cons.events.navigation.SPACE_SETTINGS_OPENED, action.roomId, action.tabText);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_SPACE_MANAGE]: () => {
|
||||
this.emit(cons.events.navigation.SPACE_MANAGE_OPENED, action.roomId);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_SPACE_ADDEXISTING]: () => {
|
||||
this.emit(cons.events.navigation.SPACE_ADDEXISTING_OPENED, action.roomId, action.spaces);
|
||||
},
|
||||
|
|
@ -309,15 +32,6 @@ class Navigation extends EventEmitter {
|
|||
action.tabText
|
||||
);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_SHORTCUT_SPACES]: () => {
|
||||
this.emit(cons.events.navigation.SHORTCUT_SPACES_OPENED);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_INVITE_LIST]: () => {
|
||||
this.emit(cons.events.navigation.INVITE_LIST_OPENED);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_PUBLIC_ROOMS]: () => {
|
||||
this.emit(cons.events.navigation.PUBLIC_ROOMS_OPENED, action.searchTerm);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_CREATE_ROOM]: () => {
|
||||
this.emit(
|
||||
cons.events.navigation.CREATE_ROOM_OPENED,
|
||||
|
|
@ -340,38 +54,6 @@ class Navigation extends EventEmitter {
|
|||
[cons.actions.navigation.OPEN_SETTINGS]: () => {
|
||||
this.emit(cons.events.navigation.SETTINGS_OPENED, action.tabText);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_NAVIGATION]: () => {
|
||||
this.emit(cons.events.navigation.NAVIGATION_OPENED);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_EMOJIBOARD]: () => {
|
||||
this.emit(
|
||||
cons.events.navigation.EMOJIBOARD_OPENED,
|
||||
action.cords,
|
||||
action.requestEmojiCallback,
|
||||
);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_READRECEIPTS]: () => {
|
||||
this.emit(
|
||||
cons.events.navigation.READRECEIPTS_OPENED,
|
||||
action.roomId,
|
||||
action.userIds,
|
||||
);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_VIEWSOURCE]: () => {
|
||||
this.emit(
|
||||
cons.events.navigation.VIEWSOURCE_OPENED,
|
||||
action.event,
|
||||
);
|
||||
},
|
||||
[cons.actions.navigation.CLICK_REPLY_TO]: () => {
|
||||
this.emit(
|
||||
cons.events.navigation.REPLY_TO_CLICKED,
|
||||
action.userId,
|
||||
action.eventId,
|
||||
action.body,
|
||||
action.formattedBody,
|
||||
);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_SEARCH]: () => {
|
||||
this.emit(
|
||||
cons.events.navigation.SEARCH_OPENED,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue