Compare commits

...
12 Commits
Author SHA1 Message Date
Ajay Bura de2680c764 Merge branch 'dev' into ghcr-registry 2022-08-20 20:54:28 +05:30
Ajay BuraandKrishan 7e28aa1474 Update sidebar on room/space switch (#768)
* Select last room on space/tab change (#353)

* Update sidbar on room select from search (#374)

* Select last room on space/tab change (#353)

* Update sidbar on room select from search (#374)

* Fix wrong space gets selected with some rooms

* Fix auto select room in categorized space

* Fix room remain selected on leave

* Fix leaved room appear in category & search

* Remove globally exposed vars

* Hide pin spaces from home

* Fix selecting dm always open dm tab

* Order category by AtoZ (#769)

Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
2022-08-20 20:51:37 +05:30
Krishan a6d5bfe967 Merge branch 'dev' into ghcr-registry 2022-08-20 19:00:19 +05:30
jameskitt616 1deef51df0 Add cancel edit-message on Escape Key press (#765) 2022-08-20 18:56:37 +05:30
Ajay Bura 6f7934badc Fix more css bugs 2022-08-19 15:19:12 +05:30
Ajay Bura 2f2680be3c Fix minor css bugs 2022-08-19 13:57:24 +05:30
Ajay Bura 4cd8f4a94c Open image in lightbox (#767)
* Add lightbox

* Fix vertical media height (#467)

* Update dialog animation

* Fix overlay opacity

* Fix dialog animation

* Update open in new tab icon
2022-08-19 12:15:22 +05:30
Krishan 046f109ed8 add permission to token to write package to ghcr 2022-08-18 10:40:54 +05:30
Krishan f63caa14a3 Fix secret name 2022-08-18 08:43:10 +05:30
Krishan a3eeecdf93 Push Docker image to ghcr registry 2022-08-17 16:19:57 +05:30
James a417980a81 Improve MIME type handling on File Upload and in Message Component (#688)
* move allowed MIME types to own util file

* add check for safe MIME type before choosing how to upload

* check for allowed blob type to decide what component to load

* re-add check for safe mimetype

* fix bracket positioning
2022-08-14 16:31:17 +05:30
Andrew Morgan af69955801 Fix grammar of read receipt text (#744)
'other' should be plural in this context.
2022-08-12 16:03:56 +05:30
22 changed files with 501 additions and 129 deletions

No files matched your search

+11 -2
View File
@@ -64,10 +64,11 @@ jobs:
NETLIFY_DEPLOY_TO_PROD: true
push-to-dockerhub:
name: Push Docker image to Docker Hub
name: Push Docker image to Docker Hub, ghcr
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3.0.2
@@ -80,11 +81,19 @@ jobs:
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to the Container registry
uses: docker/login-action@v2.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4.0.1
with:
images: ajbura/cinny
images: |
${{ secrets.DOCKER_USERNAME }}/cinny
ghcr.io/${{ github.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@v3.1.1
with:
+3 -11
View File
@@ -1,12 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<polygon points="14,3 14,5 17.8,5 12.9,9.9 14.3,11.3 19,6.6 19,10.2 21,10.2 21,3 "/>
<path d="M3,10.2h2V5h5V3H5C3.9,3,3,3.9,3,5V10.2z"/>
<path d="M5,14.2H3V19c0,1.1,0.9,2,2,2h5v-2H5V14.2z"/>
<path d="M19,19h-5v2h5c1.1,0,2-0.9,2-2v-4.8h-2V19z"/>
</g>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 3V5H17.8L12.9 9.9L14.3 11.3L19 6.6V10.2H21V3H14Z" fill="black"/>
<path d="M5 5H10V3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V14.2H19V19H5V5Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 297 B

-1
View File
@@ -42,7 +42,6 @@ function RawModal({
shouldCloseOnEsc={closeFromOutside}
shouldCloseOnOverlayClick={closeFromOutside}
shouldReturnFocusAfterClose={false}
closeTimeoutMS={300}
>
{children}
</Modal>
+29 -26
View File
@@ -1,33 +1,9 @@
.ReactModal__Overlay {
opacity: 0;
transition: opacity 200ms var(--fluid-slide-up);
}
.ReactModal__Overlay--after-open{
opacity: 1;
}
.ReactModal__Overlay--before-close{
opacity: 0;
}
.ReactModal__Content {
transform: translateY(100%);
transition: transform 200ms var(--fluid-slide-up);
}
.ReactModal__Content--after-open{
transform: translateY(0);
}
.ReactModal__Content--before-close{
transform: translateY(100%);
}
.raw-modal {
--small-modal-width: 525px;
--medium-modal-width: 712px;
--large-modal-width: 1024px;
position: relative;
width: 100%;
max-height: 100%;
border-radius: var(--bo-radius);
@@ -60,4 +36,31 @@
height: 100%;
background-color: var(--bg-overlay);
}
}
}
.ReactModal__Overlay {
animation: raw-modal--overlay 150ms;
}
.ReactModal__Content {
animation: raw-modal--content 150ms;
}
@keyframes raw-modal--content {
0% {
transform: translateY(100px);
opacity: .5;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
@keyframes raw-modal--overlay {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import './ImageLightbox.scss';
import FileSaver from 'file-saver';
import Text from '../../atoms/text/Text';
import RawModal from '../../atoms/modal/RawModal';
import IconButton from '../../atoms/button/IconButton';
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
function ImageLightbox({
url, alt, isOpen, onRequestClose,
}) {
const handleDownload = () => {
FileSaver.saveAs(url, alt);
};
return (
<RawModal
className="image-lightbox__modal"
overlayClassName="image-lightbox__overlay"
isOpen={isOpen}
onRequestClose={onRequestClose}
size="large"
>
<div className="image-lightbox__header">
<Text variant="b2" weight="medium">{alt}</Text>
<IconButton onClick={() => window.open(url)} size="small" src={ExternalSVG} />
<IconButton onClick={handleDownload} size="small" src={DownloadSVG} />
</div>
<div className="image-lightbox__content">
<img src={url} alt={alt} />
</div>
</RawModal>
);
}
ImageLightbox.propTypes = {
url: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
isOpen: PropTypes.bool.isRequired,
onRequestClose: PropTypes.func.isRequired,
};
export default ImageLightbox;
@@ -0,0 +1,50 @@
@use '../../partials/flex';
@use '../../partials/text';
.image-lightbox__modal {
box-shadow: none;
width: unset;
gap: var(--sp-normal);
border-radius: 0;
pointer-events: none;
& .text {
color: white;
}
& .ic-raw {
background-color: white;
}
}
.image-lightbox__overlay {
background-color: var(--bg-overlay-low);
}
.image-lightbox__header > *,
.image-lightbox__content > * {
pointer-events: all;
}
.image-lightbox__header {
display: flex;
align-items: center;
& > .text {
@extend .cp-fx__item-one;
@extend .cp-txt__ellipsis;
}
}
.image-lightbox__content {
display: flex;
justify-content: center;
max-height: 90vh;
& img {
background-color: var(--bg-surface-low);
object-fit: contain;
max-width: 100%;
max-height: 100%;
border-radius: var(--bo-radius);
}
}
+37 -42
View File
@@ -8,47 +8,13 @@ import { BlurhashCanvas } from 'react-blurhash';
import Text from '../../atoms/text/Text';
import IconButton from '../../atoms/button/IconButton';
import Spinner from '../../atoms/spinner/Spinner';
import ImageLightbox from '../image-lightbox/ImageLightbox';
import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
import PlaySVG from '../../../../public/res/ic/outlined/play.svg';
// https://github.com/matrix-org/matrix-react-sdk/blob/cd15e08fc285da42134817cce50de8011809cd53/src/utils/blobs.ts#L73
const ALLOWED_BLOB_MIMETYPES = [
'image/jpeg',
'image/gif',
'image/png',
'image/apng',
'image/webp',
'image/avif',
'video/mp4',
'video/webm',
'video/ogg',
'video/quicktime',
'audio/mp4',
'audio/webm',
'audio/aac',
'audio/mpeg',
'audio/ogg',
'audio/wave',
'audio/wav',
'audio/x-wav',
'audio/x-pn-wav',
'audio/flac',
'audio/x-flac',
];
function getBlobSafeMimeType(mimetype) {
if (!ALLOWED_BLOB_MIMETYPES.includes(mimetype)) {
return 'application/octet-stream';
}
// Required for Chromium browsers
if (mimetype === 'video/quicktime') {
return 'video/mp4';
}
return mimetype;
}
import { getBlobSafeMimeType } from '../../../util/mimetypes';
async function getDecryptedBlob(response, type, decryptData) {
const arrayBuffer = await response.arrayBuffer();
@@ -159,6 +125,7 @@ function Image({
}) {
const [url, setUrl] = useState(null);
const [blur, setBlur] = useState(true);
const [lightbox, setLightbox] = useState(false);
useEffect(() => {
let unmounted = false;
@@ -173,14 +140,42 @@ function Image({
};
}, []);
const toggleLightbox = () => {
if (!url) return;
setLightbox(!lightbox);
};
return (
<div className="file-container">
<FileHeader name={name} link={url || link} type={type} external />
<div style={{ height: width !== null ? getNativeHeight(width, height) : 'unset' }} className="image-container">
{ blurhash && blur && <BlurhashCanvas hash={blurhash} punch={1} />}
{ url !== null && <img style={{ display: blur ? 'none' : 'unset' }} onLoad={() => setBlur(false)} src={url || link} alt={name} />}
<>
<div className="file-container">
<div
style={{ height: width !== null ? getNativeHeight(width, height) : 'unset' }}
className="image-container"
role="button"
tabIndex="0"
onClick={toggleLightbox}
onKeyDown={toggleLightbox}
>
{ blurhash && blur && <BlurhashCanvas hash={blurhash} punch={1} />}
{ url !== null && (
<img
style={{ display: blur ? 'none' : 'unset' }}
onLoad={() => setBlur(false)}
src={url || link}
alt={name}
/>
)}
</div>
</div>
</div>
{url && (
<ImageLightbox
url={url}
alt={name}
isOpen={lightbox}
onRequestClose={toggleLightbox}
/>
)}
</>
);
}
Image.defaultProps = {
+7
View File
@@ -62,6 +62,13 @@
margin: 0 !important;
}
}
.image-container {
max-height: 460px;
img {
cursor: pointer;
object-fit: cover;
}
}
.video-container {
position: relative;
+12 -1
View File
@@ -37,6 +37,7 @@ import CmdIC from '../../../../public/res/ic/outlined/cmd.svg';
import BinIC from '../../../../public/res/ic/outlined/bin.svg';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import { getBlobSafeMimeType } from '../../../util/mimetypes';
function PlaceholderMessage() {
return (
@@ -289,6 +290,11 @@ function MessageEdit({ body, onSave, onCancel }) {
}, []);
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
e.preventDefault();
onCancel();
}
if (e.key === 'Enter' && e.shiftKey === false) {
e.preventDefault();
onSave(editInputRef.current.value);
@@ -621,7 +627,12 @@ function genMediaContent(mE) {
if (typeof mediaMXC === 'undefined' || mediaMXC === '') return <span style={{ color: 'var(--bg-danger)' }}>Malformed event</span>;
let msgType = mE.getContent()?.msgtype;
if (mE.getType() === 'm.sticker') msgType = 'm.sticker';
const safeMimetype = getBlobSafeMimeType(mContent.info?.mimetype);
if (mE.getType() === 'm.sticker') {
msgType = 'm.sticker';
} else if (safeMimetype === 'application/octet-stream') {
msgType = 'm.file';
}
const blurhash = mContent?.info?.['xyz.amorgan.blurhash'];
@@ -38,10 +38,10 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
const handleMarkAsRead = () => {
const spaceChildren = roomList.getCategorizedSpaces([roomId]);
spaceChildren?.forEach((childIds, spaceId) => {
spaceChildren?.forEach((childIds) => {
childIds?.forEach((childId) => {
markAsRead(childId);
})
});
});
afterOptionSelect();
};
+2 -2
View File
@@ -6,7 +6,7 @@ import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import { join } from '../../../client/action/room';
import { selectRoom, selectSpace } from '../../../client/action/navigation';
import { selectRoom, selectTab } from '../../../client/action/navigation';
import Text from '../../atoms/text/Text';
import IconButton from '../../atoms/button/IconButton';
@@ -32,7 +32,7 @@ function JoinAliasContent({ term, requestClose }) {
const openRoom = (roomId) => {
const room = mx.getRoom(roomId);
if (!room) return;
if (room.isSpaceRoom()) selectSpace(roomId);
if (room.isSpaceRoom()) selectTab(roomId);
else selectRoom(roomId);
requestClose();
};
@@ -6,7 +6,7 @@ import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import { selectSpace } from '../../../client/action/navigation';
import { selectTab, selectSpace } from '../../../client/action/navigation';
import navigation from '../../../client/state/navigation';
import { abbreviateNumber } from '../../../util/common';
@@ -107,7 +107,10 @@ function DrawerBreadcrumb({ spaceId }) {
{ index !== 0 && <RawIcon size="extra-small" src={ChevronRightIC} />}
<Button
className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''}
onClick={() => selectSpace(id)}
onClick={() => {
if (id === cons.tabs.HOME) selectTab(id);
else selectSpace(id);
}}
>
<Text variant="b2">{id === cons.tabs.HOME ? 'Home' : twemojify(mx.getRoom(id).name)}</Text>
{ noti !== null && (
+3 -3
View File
@@ -30,7 +30,7 @@ function Home({ spaceId }) {
roomIds = spaceChildIds.filter((roomId) => rooms.has(roomId));
directIds = spaceChildIds.filter((roomId) => directs.has(roomId));
} else {
spaceIds = roomList.getOrphanSpaces();
spaceIds = roomList.getOrphanSpaces().filter((id) => !accountData.spaceShortcut.has(id));
roomIds = roomList.getOrphanRooms();
}
@@ -80,10 +80,10 @@ function Home({ spaceId }) {
<RoomsCategory name="People" roomIds={directIds.sort(roomIdByActivity)} drawerPostie={drawerPostie} />
)}
{ isCategorized && [...categories].map(([catId, childIds]) => {
{ isCategorized && [...categories.keys()].sort(roomIdByAtoZ).map((catId) => {
const rms = [];
const dms = [];
childIds.forEach((id) => {
categories.get(catId).forEach((id) => {
if (directs.has(id)) dms.push(id);
else rms.push(id);
});
@@ -70,6 +70,7 @@
&__preview > img {
max-height: 40px;
border-radius: var(--bo-radius);
max-width: 150px;
}
&__icon {
padding: var(--sp-extra-tight);
+1 -1
View File
@@ -164,7 +164,7 @@ function getUsersActionJsx(roomId, userIds, actionStr) {
const othersCount = userIds.length - MAX_VISIBLE_COUNT;
// eslint-disable-next-line react/jsx-one-expression-per-line
return <>{u1Jsx}, {u2Jsx}, {u3Jsx} and {othersCount} other are {actionStr}</>;
return <>{u1Jsx}, {u2Jsx}, {u3Jsx} and {othersCount} others are {actionStr}</>;
}
function parseTimelineChange(mEvent) {
+13
View File
@@ -4,6 +4,17 @@ import { selectTab, selectSpace, selectRoom } from '../action/navigation';
function initRoomListListener(roomList) {
const listenRoomLeave = (roomId) => {
const parents = roomList.roomIdToParents.get(roomId);
if (parents) {
[...parents].forEach((pId) => {
const data = navigation.spaceToRoom.get(pId);
if (data?.roomId === roomId) {
navigation.spaceToRoom.delete(pId);
}
});
}
if (navigation.selectedRoomId === roomId) {
selectRoom(null);
}
@@ -13,6 +24,8 @@ function initRoomListListener(roomList) {
if (idIndex === 0) selectTab(cons.tabs.HOME);
else selectSpace(navigation.selectedSpacePath[idIndex - 1]);
}
navigation.removeRecentRoom(roomId);
};
roomList.on(cons.events.roomList.ROOM_LEAVED, listenRoomLeave);
+7
View File
@@ -8,12 +8,19 @@ import AccountData from './state/AccountData';
import RoomsInput from './state/RoomsInput';
import Notifications from './state/Notifications';
import { cryptoCallbacks } from './state/secretStorageKeys';
import navigation from './state/navigation';
global.Olm = require('@matrix-org/olm');
// logger.disableAll();
class InitMatrix extends EventEmitter {
constructor() {
super();
navigation.initMatrix = this;
}
async init() {
await this.startClient();
this.setupSync();
+1 -1
View File
@@ -89,7 +89,7 @@ class RoomList extends EventEmitter {
child.forEach((childId) => {
const room = this.matrixClient.getRoom(childId);
if (room === null) return;
if (room === null || room.getMyMembership() !== 'join') return;
if (room.isSpaceRoom()) categorizeSpace(childId);
else mappedChild.add(childId);
});
+2 -1
View File
@@ -6,6 +6,7 @@ import { math } from 'micromark-extension-math';
import { encode } from 'blurhash';
import { getShortcodeToEmoji } from '../../app/organisms/emoji-board/custom-emoji';
import { mathExtensionHtml, spoilerExtension, spoilerExtensionHtml } from '../../util/markdown';
import { getBlobSafeMimeType } from '../../util/mimetypes';
import { sanitizeText } from '../../util/sanitize';
import cons from './cons';
import settings from './settings';
@@ -355,7 +356,7 @@ class RoomsInput extends EventEmitter {
}
async sendFile(roomId, file) {
const fileType = file.type.slice(0, file.type.indexOf('/'));
const fileType = getBlobSafeMimeType(file.type).slice(0, file.type.indexOf('/'));
const info = {
mimetype: file.type,
size: file.size,
+228 -32
View File
@@ -5,6 +5,8 @@ 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;
@@ -14,14 +16,20 @@ class Navigation extends EventEmitter {
this.isRoomSettings = false;
this.recentRooms = [];
this.spaceToRoom = new Map();
this.rawModelStack = [];
}
_setSpacePath(roomId) {
if (roomId === null || roomId === cons.tabs.HOME) {
_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);
@@ -30,6 +38,215 @@ class Navigation extends EventEmitter {
this.selectedSpacePath.push(roomId);
}
_mapRoomToSpace(roomId) {
const { roomList } = 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;
[...parents].forEach((pId) => {
this.spaceToRoom.set(pId, {
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);
if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
this.isRoomSettings = !this.isRoomSettings;
this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
}
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);
@@ -59,40 +276,19 @@ class Navigation extends EventEmitter {
navigate(action) {
const actions = {
[cons.actions.navigation.SELECT_TAB]: () => {
this.selectedTab = action.tabId;
if (this.selectedTab !== cons.tabs.DIRECTS) {
if (this.selectedTab === cons.tabs.HOME) {
this.selectedSpacePath = [cons.tabs.HOME];
this.selectedSpaceId = null;
} else {
this.selectedSpacePath = [this.selectedTab];
this.selectedSpaceId = this.selectedTab;
}
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
} else this.selectedSpaceId = null;
this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
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._setSpacePath(action.roomId);
this.selectedSpaceId = action.roomId === cons.tabs.HOME ? null : action.roomId;
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
this._selectSpace(action.roomId, false);
},
[cons.actions.navigation.SELECT_ROOM]: () => {
const prevSelectedRoomId = this.selectedRoomId;
this.selectedRoomId = action.roomId;
this.removeRecentRoom(prevSelectedRoomId);
this.addRecentRoom(prevSelectedRoomId);
this.removeRecentRoom(this.selectedRoomId);
if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
this.isRoomSettings = !this.isRoomSettings;
this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
}
this.emit(
cons.events.navigation.ROOM_SELECTED,
this.selectedRoomId,
prevSelectedRoomId,
action.eventId,
);
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);
+3 -2
View File
@@ -105,7 +105,7 @@
/* shadow and overlay */
--bg-overlay: rgba(0, 0, 0, 20%);
--bg-overlay-low: rgba(0, 0, 0, 85%);
--bg-overlay-low: rgba(0, 0, 0, 50%);
--bs-popup: 0 0 16px rgba(0, 0, 0, 10%);
@@ -264,7 +264,8 @@
}
/* shadow and overlay */
--bg-overlay: rgba(0, 0, 0, 50%);
--bg-overlay: rgba(0, 0, 0, 60%);
--bg-overlay-low: rgba(0, 0, 0, 80%);
--bs-popup: 0 0 16px rgba(0, 0, 0, 25%);
+37
View File
@@ -0,0 +1,37 @@
// https://github.com/matrix-org/matrix-react-sdk/blob/cd15e08fc285da42134817cce50de8011809cd53/src/utils/blobs.ts
export const ALLOWED_BLOB_MIMETYPES = [
'image/jpeg',
'image/gif',
'image/png',
'image/apng',
'image/webp',
'image/avif',
'video/mp4',
'video/webm',
'video/ogg',
'video/quicktime',
'audio/mp4',
'audio/webm',
'audio/aac',
'audio/mpeg',
'audio/ogg',
'audio/wave',
'audio/wav',
'audio/x-wav',
'audio/x-pn-wav',
'audio/flac',
'audio/x-flac',
];
export function getBlobSafeMimeType(mimetype) {
if (!ALLOWED_BLOB_MIMETYPES.includes(mimetype)) {
return 'application/octet-stream';
}
// Required for Chromium browsers
if (mimetype === 'video/quicktime') {
return 'video/mp4';
}
return mimetype;
}