-
-
- {name}
- {parentName && (
-
- {' — '}
- {parentName}
-
- )}
-
- {isUnread && (
-
- )}
- >
- }
- options={options}
- onClick={onClick}
- onContextMenu={onContextMenu}
- />
- );
-}
-RoomSelector.defaultProps = {
- parentName: null,
- isSelected: false,
- imageSrc: null,
- iconSrc: null,
- isMuted: false,
- options: null,
- onContextMenu: null,
-};
-RoomSelector.propTypes = {
- name: PropTypes.string.isRequired,
- parentName: PropTypes.string,
- roomId: PropTypes.string.isRequired,
- imageSrc: PropTypes.string,
- iconSrc: PropTypes.string,
- isSelected: PropTypes.bool,
- isMuted: PropTypes.bool,
- isUnread: PropTypes.bool.isRequired,
- notificationCount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
- isAlert: PropTypes.bool.isRequired,
- options: PropTypes.node,
- onClick: PropTypes.func.isRequired,
- onContextMenu: PropTypes.func,
-};
-
-export default RoomSelector;
diff --git a/src/app/molecules/room-selector/RoomSelector.scss b/src/app/molecules/room-selector/RoomSelector.scss
deleted file mode 100644
index 59e47473..00000000
--- a/src/app/molecules/room-selector/RoomSelector.scss
+++ /dev/null
@@ -1,87 +0,0 @@
-@use '../../partials/flex';
-@use '../../partials/text';
-@use '../../partials/dir';
-
-.room-selector {
- @extend .cp-fx__row--s-c;
-
- border: 1px solid transparent;
- border-radius: var(--bo-radius);
- cursor: pointer;
-
- &--muted {
- opacity: 0.6;
- }
-
- &--unread {
- .room-selector__content > .text {
- color: var(--tc-surface-high);
- }
- }
-
- &--selected {
- background-color: var(--bg-surface);
- border-color: var(--bg-surface-border);
-
- & .room-selector__options {
- display: flex;
- }
- }
-
- @media (hover: hover) {
- &:hover {
- background-color: var(--bg-surface-hover);
- & .room-selector__options {
- display: flex;
- }
- }
- }
- &:focus-within {
- background-color: var(--bg-surface-hover);
- & button {
- outline: none;
- }
- }
- &:active {
- background-color: var(--bg-surface-active);
- }
- &--selected:hover,
- &--selected:focus,
- &--selected:active {
- background-color: var(--bg-surface);
- }
-}
-
-.room-selector__content {
- @extend .cp-fx__item-one;
- @extend .cp-fx__row--s-c;
- padding: 0 var(--sp-extra-tight);
- min-height: 40px;
- cursor: inherit;
-
- & > .avatar-container .avatar__border--active {
- box-shadow: none;
- }
-
- & > .text {
- @extend .cp-fx__item-one;
- @extend .cp-txt__ellipsis;
- margin: 0 var(--sp-extra-tight);
-
- color: var(--tc-surface-normal-low);
- }
-}
-.room-selector__options {
- @extend .cp-fx__row--s-c;
- @include dir.side(margin, 0, var(--sp-ultra-tight));
- display: none;
-
- &:empty {
- margin: 0 !important;
- }
-
- & .ic-btn {
- padding: 6px;
- border-radius: calc(var(--bo-radius) / 2);
- }
-}
\ No newline at end of file
diff --git a/src/app/molecules/room-tile/RoomTile.jsx b/src/app/molecules/room-tile/RoomTile.jsx
deleted file mode 100644
index 2e0a63f6..00000000
--- a/src/app/molecules/room-tile/RoomTile.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import './RoomTile.scss';
-
-import colorMXID from '../../../util/colorMXID';
-
-import Text from '../../atoms/text/Text';
-import Avatar from '../../atoms/avatar/Avatar';
-
-function RoomTile({ avatarSrc, name, id, inviterName, memberCount, desc, options }) {
- return (
-
-
-
- {name}
-
- {inviterName !== null
- ? `Invited by ${inviterName} to ${id}${
- memberCount === null ? '' : ` • ${memberCount} members`
- }`
- : id + (memberCount === null ? '' : ` • ${memberCount} members`)}
-
- {desc !== null && typeof desc === 'string' ? (
-
- {desc}
-
- ) : (
- desc
- )}
-
- {options !== null &&
{options}
}
-
- );
-}
-
-RoomTile.defaultProps = {
- avatarSrc: null,
- inviterName: null,
- options: null,
- desc: null,
- memberCount: null,
-};
-RoomTile.propTypes = {
- avatarSrc: PropTypes.string,
- name: PropTypes.string.isRequired,
- id: PropTypes.string.isRequired,
- inviterName: PropTypes.string,
- memberCount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- desc: PropTypes.node,
- options: PropTypes.node,
-};
-
-export default RoomTile;
diff --git a/src/app/molecules/room-tile/RoomTile.scss b/src/app/molecules/room-tile/RoomTile.scss
deleted file mode 100644
index bbed7107..00000000
--- a/src/app/molecules/room-tile/RoomTile.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-.room-tile {
- display: flex;
-
- &__content {
- flex: 1;
- min-width: 0;
-
- margin: 0 var(--sp-normal);
-
- &__desc {
- white-space: pre-wrap;
- & a {
- white-space: wrap;
- }
- }
-
- & .text:not(:first-child) {
- margin-top: var(--sp-ultra-tight);
- }
- }
-}
\ No newline at end of file
diff --git a/src/app/molecules/setting-tile/SettingTile.jsx b/src/app/molecules/setting-tile/SettingTile.jsx
deleted file mode 100644
index 6b221965..00000000
--- a/src/app/molecules/setting-tile/SettingTile.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import './SettingTile.scss';
-
-import Text from '../../atoms/text/Text';
-
-function SettingTile({ title, options, content }) {
- return (
-
-
-
- {
- typeof title === 'string'
- ? {title}
- : title
- }
-
- {content}
-
- {options !== null &&
{options}
}
-
- );
-}
-
-SettingTile.defaultProps = {
- options: null,
- content: null,
-};
-
-SettingTile.propTypes = {
- title: PropTypes.node.isRequired,
- options: PropTypes.node,
- content: PropTypes.node,
-};
-
-export default SettingTile;
diff --git a/src/app/molecules/setting-tile/SettingTile.scss b/src/app/molecules/setting-tile/SettingTile.scss
deleted file mode 100644
index 322f0019..00000000
--- a/src/app/molecules/setting-tile/SettingTile.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-@use '../../partials/dir';
-
-.setting-tile {
- display: flex;
- &__content {
- flex: 1;
- min-width: 0;
- }
- &__title {
- margin-bottom: var(--sp-ultra-tight);
- }
- &__options {
- @include dir.side(margin, var(--sp-tight), 0);
- }
-}
\ No newline at end of file
diff --git a/src/app/molecules/space-add-existing/SpaceAddExisting.jsx b/src/app/molecules/space-add-existing/SpaceAddExisting.jsx
deleted file mode 100644
index 05b8d85f..00000000
--- a/src/app/molecules/space-add-existing/SpaceAddExisting.jsx
+++ /dev/null
@@ -1,239 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import PropTypes from 'prop-types';
-import { useAtomValue } from 'jotai';
-import './SpaceAddExisting.scss';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import { joinRuleToIconSrc } from '../../../util/matrixUtil';
-import { Debounce } from '../../../util/common';
-
-import Text from '../../atoms/text/Text';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import Button from '../../atoms/button/Button';
-import IconButton from '../../atoms/button/IconButton';
-import Checkbox from '../../atoms/button/Checkbox';
-import Input from '../../atoms/input/Input';
-import Spinner from '../../atoms/spinner/Spinner';
-import RoomSelector from '../room-selector/RoomSelector';
-import Dialog from '../dialog/Dialog';
-
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-import SearchIC from '../../../../public/res/ic/outlined/search.svg';
-
-import { roomToParentsAtom } from '../../state/room/roomToParents';
-import { useDirects, useRooms, useSpaces } from '../../state/hooks/roomList';
-import { allRoomsAtom } from '../../state/room-list/roomList';
-import { mDirectAtom } from '../../state/mDirectList';
-import { useMatrixClient } from '../../hooks/useMatrixClient';
-import { getViaServers } from '../../plugins/via-servers';
-import { rateLimitedActions } from '../../utils/matrix';
-import { useAlive } from '../../hooks/useAlive';
-
-function SpaceAddExistingContent({ roomId, spaces: onlySpaces }) {
- const alive = useAlive();
- const [debounce] = useState(new Debounce());
- const [process, setProcess] = useState(null);
- const [allRoomIds, setAllRoomIds] = useState([]);
- const [selected, setSelected] = useState([]);
- const [searchIds, setSearchIds] = useState(null);
- const mx = useMatrixClient();
- const roomIdToParents = useAtomValue(roomToParentsAtom);
- const mDirects = useAtomValue(mDirectAtom);
- const spaces = useSpaces(mx, allRoomsAtom);
- const rooms = useRooms(mx, allRoomsAtom, mDirects);
- const directs = useDirects(mx, allRoomsAtom, mDirects);
-
- useEffect(() => {
- const roomIds = onlySpaces ? [...spaces] : [...rooms, ...directs];
- const allIds = roomIds.filter(
- (rId) => rId !== roomId && !roomIdToParents.get(rId)?.has(roomId)
- );
- setAllRoomIds(allIds);
- }, [spaces, rooms, directs, roomIdToParents, roomId, onlySpaces]);
-
- const toggleSelection = (rId) => {
- if (process !== null) return;
- const newSelected = [...selected];
- const selectedIndex = newSelected.indexOf(rId);
-
- if (selectedIndex > -1) {
- newSelected.splice(selectedIndex, 1);
- setSelected(newSelected);
- return;
- }
- newSelected.push(rId);
- setSelected(newSelected);
- };
-
- const handleAdd = async () => {
- setProcess(`Adding ${selected.length} items...`);
-
- await rateLimitedActions(selected, async (rId) => {
- const room = mx.getRoom(rId);
- const via = getViaServers(room);
-
- await mx.sendStateEvent(
- roomId,
- 'm.space.child',
- {
- auto_join: false,
- suggested: false,
- via,
- },
- rId
- );
- });
-
- if (!alive()) return;
-
- const roomIds = onlySpaces ? [...spaces] : [...rooms, ...directs];
- const allIds = roomIds.filter(
- (rId) => rId !== roomId && !roomIdToParents.get(rId)?.has(roomId) && !selected.includes(rId)
- );
- setAllRoomIds(allIds);
- setProcess(null);
- setSelected([]);
- };
-
- const handleSearch = (ev) => {
- const term = ev.target.value.toLocaleLowerCase().replace(/\s/g, '');
- if (term === '') {
- setSearchIds(null);
- return;
- }
-
- debounce._(() => {
- const searchedIds = allRoomIds.filter((rId) => {
- let name = mx.getRoom(rId)?.name;
- if (!name) return false;
- name = name.normalize('NFKC').toLocaleLowerCase().replace(/\s/g, '');
- return name.includes(term);
- });
- setSearchIds(searchedIds);
- }, 200)();
- };
- const handleSearchClear = (ev) => {
- const btn = ev.currentTarget;
- btn.parentElement.searchInput.value = '';
- setSearchIds(null);
- };
-
- return (
- <>
-
- {searchIds?.length === 0 && No results found}
- {(searchIds || allRoomIds).map((rId) => {
- const room = mx.getRoom(rId);
- let imageSrc =
- room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
- if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
-
- const parentSet = roomIdToParents.get(rId);
- const parentNames = parentSet
- ? [...parentSet].map((parentId) => mx.getRoom(parentId).name)
- : undefined;
- const parents = parentNames ? parentNames.join(', ') : null;
-
- const handleSelect = () => toggleSelection(rId);
-
- return (
-
- }
- />
- );
- })}
- {selected.length !== 0 && (
-
- {process && }
- {process || `${selected.length} item selected`}
- {!process && (
-
- )}
-
- )}
- >
- );
-}
-SpaceAddExistingContent.propTypes = {
- roomId: PropTypes.string.isRequired,
- spaces: PropTypes.bool.isRequired,
-};
-
-function useVisibilityToggle() {
- const [data, setData] = useState(null);
-
- useEffect(() => {
- const handleOpen = (roomId, spaces) =>
- setData({
- roomId,
- spaces,
- });
- navigation.on(cons.events.navigation.SPACE_ADDEXISTING_OPENED, handleOpen);
- return () => {
- navigation.removeListener(cons.events.navigation.SPACE_ADDEXISTING_OPENED, handleOpen);
- };
- }, []);
-
- const requestClose = () => setData(null);
-
- return [data, requestClose];
-}
-
-function SpaceAddExisting() {
- const [data, requestClose] = useVisibilityToggle();
- const mx = useMatrixClient();
- const room = mx.getRoom(data?.roomId);
-
- return (
-
- );
-}
-
-export default SpaceAddExisting;
diff --git a/src/app/molecules/space-add-existing/SpaceAddExisting.scss b/src/app/molecules/space-add-existing/SpaceAddExisting.scss
deleted file mode 100644
index 3eb3d96a..00000000
--- a/src/app/molecules/space-add-existing/SpaceAddExisting.scss
+++ /dev/null
@@ -1,77 +0,0 @@
-@use '../../partials/dir';
-@use '../../partials/flex';
-
-.space-add-existing {
- height: 100%;
-
- .dialog__content-container {
- padding: 0;
- padding-bottom: 80px;
- @include dir.side(padding, var(--sp-extra-tight), 0);
-
- & > .text {
- margin: var(--sp-loose) var(--sp-normal);
- text-align: center;
- }
- }
-
- & form {
- @extend .cp-fx__row--s-c;
- padding: var(--sp-extra-tight);
- padding-top: var(--sp-normal);
-
- position: sticky;
- top: 0;
- z-index: 999;
- background-color: var(--bg-surface);
-
- & > .ic-raw,
- & > .ic-btn {
- position: absolute;
- }
- & > .ic-raw {
- margin: 0 var(--sp-tight);
- }
- & > .ic-btn {
- border-radius: calc(var(--bo-radius) / 2);
- @include dir.prop(right, var(--sp-tight), unset);
- @include dir.prop(left, unset, var(--sp-tight));
- }
- & input {
- padding: var(--sp-tight) 40px;
- }
- }
-
- .input-container {
- @extend .cp-fx__item-one;
- }
-
- .room-selector {
- margin: 0 var(--sp-extra-tight);
- }
- .room-selector__options {
- display: flex;
- margin: 0 10px;
- }
-}
-
-.space-add-existing__footer {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- padding: var(--sp-normal);
- background-color: var(--bg-surface);
- border-top: 1px solid var(--bg-surface-border);
- display: flex;
- align-items: center;
-
- & > .text {
- @extend .cp-fx__item-one;
- padding: 0 var(--sp-tight);
- }
-
- & > button {
- @include dir.side(margin, var(--sp-normal), 0);
- }
-}
\ No newline at end of file
diff --git a/src/app/organisms/create-room/CreateRoom.jsx b/src/app/organisms/create-room/CreateRoom.jsx
deleted file mode 100644
index 04b2faeb..00000000
--- a/src/app/organisms/create-room/CreateRoom.jsx
+++ /dev/null
@@ -1,307 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import './CreateRoom.scss';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import { openReusableContextMenu } from '../../../client/action/navigation';
-import * as roomActions from '../../../client/action/room';
-import { isRoomAliasAvailable, getIdServer } from '../../../util/matrixUtil';
-import { getEventCords } from '../../../util/common';
-
-import Text from '../../atoms/text/Text';
-import Button from '../../atoms/button/Button';
-import Toggle from '../../atoms/button/Toggle';
-import IconButton from '../../atoms/button/IconButton';
-import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
-import Input from '../../atoms/input/Input';
-import Spinner from '../../atoms/spinner/Spinner';
-import SegmentControl from '../../atoms/segmented-controls/SegmentedControls';
-import Dialog from '../../molecules/dialog/Dialog';
-import SettingTile from '../../molecules/setting-tile/SettingTile';
-
-import HashPlusIC from '../../../../public/res/ic/outlined/hash-plus.svg';
-import SpacePlusIC from '../../../../public/res/ic/outlined/space-plus.svg';
-import HashIC from '../../../../public/res/ic/outlined/hash.svg';
-import HashLockIC from '../../../../public/res/ic/outlined/hash-lock.svg';
-import HashGlobeIC from '../../../../public/res/ic/outlined/hash-globe.svg';
-import SpaceIC from '../../../../public/res/ic/outlined/space.svg';
-import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg';
-import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg';
-import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-import { useRoomNavigate } from '../../hooks/useRoomNavigate';
-import { useMatrixClient } from '../../hooks/useMatrixClient';
-
-function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
- const [joinRule, setJoinRule] = useState(parentId ? 'restricted' : 'invite');
- const [isEncrypted, setIsEncrypted] = useState(true);
- const [isCreatingRoom, setIsCreatingRoom] = useState(false);
- const [creatingError, setCreatingError] = useState(null);
- const { navigateRoom, navigateSpace } = useRoomNavigate();
-
- const [isValidAddress, setIsValidAddress] = useState(null);
- const [addressValue, setAddressValue] = useState(undefined);
- const [roleIndex, setRoleIndex] = useState(0);
-
- const addressRef = useRef(null);
-
- const mx = useMatrixClient();
- const userHs = getIdServer(mx.getUserId());
-
- const handleSubmit = async (evt) => {
- evt.preventDefault();
- const { target } = evt;
-
- if (isCreatingRoom) return;
- setIsCreatingRoom(true);
- setCreatingError(null);
-
- const name = target.name.value;
- let topic = target.topic.value;
- if (topic.trim() === '') topic = undefined;
- let roomAlias;
- if (joinRule === 'public') {
- roomAlias = addressRef?.current?.value;
- if (roomAlias.trim() === '') roomAlias = undefined;
- }
-
- const powerLevel = roleIndex === 1 ? 101 : undefined;
-
- try {
- const data = await roomActions.createRoom(mx, {
- name,
- topic,
- joinRule,
- alias: roomAlias,
- isEncrypted: isSpace || joinRule === 'public' ? false : isEncrypted,
- powerLevel,
- isSpace,
- parentId,
- });
- setIsCreatingRoom(false);
- setCreatingError(null);
- setIsValidAddress(null);
- setAddressValue(undefined);
- onRequestClose();
- if (isSpace) {
- navigateSpace(data.room_id);
- } else {
- navigateRoom(data.room_id);
- }
- } catch (e) {
- if (e.message === 'M_UNKNOWN: Invalid characters in room alias') {
- setCreatingError('ERROR: Invalid characters in address');
- setIsValidAddress(false);
- } else if (e.message === 'M_ROOM_IN_USE: Room alias already taken') {
- setCreatingError('ERROR: This address is already in use');
- setIsValidAddress(false);
- } else setCreatingError(e.message);
- setIsCreatingRoom(false);
- }
- };
-
- const validateAddress = (e) => {
- const myAddress = e.target.value;
- setIsValidAddress(null);
- setAddressValue(e.target.value);
- setCreatingError(null);
-
- setTimeout(async () => {
- if (myAddress !== addressRef.current.value) return;
- const roomAlias = addressRef.current.value;
- if (roomAlias === '') return;
- const roomAddress = `#${roomAlias}:${userHs}`;
-
- if (await isRoomAliasAvailable(mx, roomAddress)) {
- setIsValidAddress(true);
- } else {
- setIsValidAddress(false);
- }
- }, 1000);
- };
-
- const joinRules = ['invite', 'restricted', 'public'];
- const joinRuleShortText = ['Private', 'Restricted', 'Public'];
- const joinRuleText = [
- 'Private (invite only)',
- 'Restricted (space member can join)',
- 'Public (anyone can join)',
- ];
- const jrRoomIC = [HashLockIC, HashIC, HashGlobeIC];
- const jrSpaceIC = [SpaceLockIC, SpaceIC, SpaceGlobeIC];
- const handleJoinRule = (evt) => {
- openReusableContextMenu('bottom', getEventCords(evt, '.btn-surface'), (closeMenu) => (
- <>
- Visibility (who can join)
- {joinRules.map((rule) => (
-
- ))}
- >
- ));
- };
-
- return (
-
- );
-}
-CreateRoomContent.defaultProps = {
- parentId: null,
-};
-CreateRoomContent.propTypes = {
- isSpace: PropTypes.bool.isRequired,
- parentId: PropTypes.string,
- onRequestClose: PropTypes.func.isRequired,
-};
-
-function useWindowToggle() {
- const [create, setCreate] = useState(null);
-
- useEffect(() => {
- const handleOpen = (isSpace, parentId) => {
- setCreate({
- isSpace,
- parentId,
- });
- };
- navigation.on(cons.events.navigation.CREATE_ROOM_OPENED, handleOpen);
- return () => {
- navigation.removeListener(cons.events.navigation.CREATE_ROOM_OPENED, handleOpen);
- };
- }, []);
-
- const onRequestClose = () => setCreate(null);
-
- return [create, onRequestClose];
-}
-
-function CreateRoom() {
- const [create, onRequestClose] = useWindowToggle();
- const { isSpace, parentId } = create ?? {};
- const mx = useMatrixClient();
- const room = mx.getRoom(parentId);
-
- return (
-
- );
-}
-
-export default CreateRoom;
diff --git a/src/app/organisms/create-room/CreateRoom.scss b/src/app/organisms/create-room/CreateRoom.scss
deleted file mode 100644
index ccd3ea3b..00000000
--- a/src/app/organisms/create-room/CreateRoom.scss
+++ /dev/null
@@ -1,90 +0,0 @@
-@use '../../partials/flex';
-@use '../../partials/dir';
-
-.create-room {
- margin: var(--sp-normal);
- @include dir.side(margin, var(--sp-normal), var(--sp-extra-tight));
-
- &__form > * {
- margin-top: var(--sp-normal);
- &:first-child {
- margin-top: var(--sp-extra-tight);
- }
- }
-
- & .segment-btn {
- padding: var(--sp-ultra-tight) 0;
- &__base {
- padding: 0 var(--sp-tight);
- }
- }
-
- &__address {
- display: flex;
- &__label {
- color: var(--tc-surface-low);
- margin-bottom: var(--sp-ultra-tight);
- }
- &__tip {
- margin-top: var(--sp-ultra-tight);
- @include dir.side(margin, 46px, 0);
- }
- & .text {
- display: flex;
- align-items: center;
- padding: 0 var(--sp-normal);
- border: 1px solid var(--bg-surface-border);
- border-radius: var(--bo-radius);
- color: var(--tc-surface-low);
- }
- & *:nth-child(2) {
- flex: 1;
- min-width: 0;
- & .input {
- border-radius: 0;
- }
- }
- & .text:first-child {
- @include dir.prop(border-width, 1px 0 1px 1px, 1px 1px 1px 0);
- @include dir.prop(
- border-radius,
- var(--bo-radius) 0 0 var(--bo-radius),
- 0 var(--bo-radius) var(--bo-radius) 0,
- );
- }
- & .text:last-child {
- @include dir.prop(border-width, 1px 1px 1px 0, 1px 0 1px 1px);
- @include dir.prop(
- border-radius,
- 0 var(--bo-radius) var(--bo-radius) 0,
- var(--bo-radius) 0 0 var(--bo-radius),
- );
- }
- }
-
- &__name-wrapper {
- display: flex;
- align-items: flex-end;
-
- & .input-container {
- flex: 1;
- min-width: 0;
- @include dir.side(margin, 0, var(--sp-normal));
- }
- & .btn-primary {
- padding-top: 11px;
- padding-bottom: 11px;
- }
- }
-
- &__loading {
- @extend .cp-fx__row--c-c;
- & .text {
- @include dir.side(margin, var(--sp-normal), 0);
- }
- }
- &__error {
- text-align: center;
- color: var(--bg-danger) !important;
- }
-}
\ No newline at end of file
diff --git a/src/app/organisms/emoji-board/custom-emoji.js b/src/app/organisms/emoji-board/custom-emoji.js
deleted file mode 100644
index 4ca2088f..00000000
--- a/src/app/organisms/emoji-board/custom-emoji.js
+++ /dev/null
@@ -1,142 +0,0 @@
-// https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md
-
-export class ImagePack {
- static parsePack(eventId, packContent) {
- if (!eventId || typeof packContent?.images !== 'object') {
- return null;
- }
-
- return new ImagePack(eventId, packContent);
- }
-
- constructor(eventId, content) {
- this.id = eventId;
- this.content = JSON.parse(JSON.stringify(content));
-
- this.applyPack(content);
- this.applyImages(content);
- }
-
- applyPack(content) {
- const pack = content.pack ?? {};
-
- this.displayName = pack.display_name;
- this.avatarUrl = pack.avatar_url;
- this.usage = pack.usage ?? ['emoticon', 'sticker'];
- this.attribution = pack.attribution;
- }
-
- applyImages(content) {
- this.images = new Map();
- this.emoticons = [];
- this.stickers = [];
-
- Object.entries(content.images).forEach(([shortcode, data]) => {
- const mxc = data.url;
- const body = data.body ?? shortcode;
- const usage = data.usage ?? this.usage;
- const { info } = data;
-
- if (!mxc) return;
- const image = {
- shortcode, mxc, body, usage, info,
- };
-
- this.images.set(shortcode, image);
- if (usage.includes('emoticon')) {
- this.emoticons.push(image);
- }
- if (usage.includes('sticker')) {
- this.stickers.push(image);
- }
- });
- }
-
- getImages() {
- return this.images;
- }
-
- getEmojis() {
- return this.emoticons;
- }
-
- getStickers() {
- return this.stickers;
- }
-
- getContent() {
- return this.content;
- }
-
- _updatePackProperty(property, value) {
- if (this.content.pack === undefined) {
- this.content.pack = {};
- }
- this.content.pack[property] = value;
- this.applyPack(this.content);
- }
-
- setAvatarUrl(avatarUrl) {
- this._updatePackProperty('avatar_url', avatarUrl);
- }
-
- setDisplayName(displayName) {
- this._updatePackProperty('display_name', displayName);
- }
-
- setAttribution(attribution) {
- this._updatePackProperty('attribution', attribution);
- }
-
- setUsage(usage) {
- this._updatePackProperty('usage', usage);
- }
-
- addImage(key, imgContent) {
- this.content.images = {
- [key]: imgContent,
- ...this.content.images,
- };
- this.applyImages(this.content);
- }
-
- removeImage(key) {
- if (this.content.images[key] === undefined) return;
- delete this.content.images[key];
- this.applyImages(this.content);
- }
-
- updateImageKey(key, newKey) {
- if (this.content.images[key] === undefined) return;
- const copyImages = {};
- Object.keys(this.content.images).forEach((imgKey) => {
- copyImages[imgKey === key ? newKey : imgKey] = this.content.images[imgKey];
- });
- this.content.images = copyImages;
- this.applyImages(this.content);
- }
-
- _updateImageProperty(key, property, value) {
- if (this.content.images[key] === undefined) return;
- this.content.images[key][property] = value;
- this.applyImages(this.content);
- }
-
- setImageUrl(key, url) {
- this._updateImageProperty(key, 'url', url);
- }
-
- setImageBody(key, body) {
- this._updateImageProperty(key, 'body', body);
- }
-
- setImageInfo(key, info) {
- this._updateImageProperty(key, 'info', info);
- }
-
- setImageUsage(key, usage) {
- this._updateImageProperty(key, 'usage', usage);
- }
-}
-
-
diff --git a/src/app/organisms/invite-user/InviteUser.jsx b/src/app/organisms/invite-user/InviteUser.jsx
deleted file mode 100644
index 271c22a9..00000000
--- a/src/app/organisms/invite-user/InviteUser.jsx
+++ /dev/null
@@ -1,315 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import './InviteUser.scss';
-
-import * as roomActions from '../../../client/action/room';
-import { hasDevices } from '../../../util/matrixUtil';
-
-import Text from '../../atoms/text/Text';
-import Button from '../../atoms/button/Button';
-import IconButton from '../../atoms/button/IconButton';
-import Spinner from '../../atoms/spinner/Spinner';
-import Input from '../../atoms/input/Input';
-import PopupWindow from '../../molecules/popup-window/PopupWindow';
-import RoomTile from '../../molecules/room-tile/RoomTile';
-
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-import UserIC from '../../../../public/res/ic/outlined/user.svg';
-import { useRoomNavigate } from '../../hooks/useRoomNavigate';
-import { getDMRoomFor } from '../../utils/matrix';
-import { useMatrixClient } from '../../hooks/useMatrixClient';
-import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
-
-function InviteUser({ isOpen, roomId, searchTerm, onRequestClose }) {
- const [isSearching, updateIsSearching] = useState(false);
- const [searchQuery, updateSearchQuery] = useState({});
- const [users, updateUsers] = useState([]);
- const useAuthentication = useMediaAuthentication();
-
- const [procUsers, updateProcUsers] = useState(new Set()); // proc stands for processing.
- const [procUserError, updateUserProcError] = useState(new Map());
-
- const [createdDM, updateCreatedDM] = useState(new Map());
- const [roomIdToUserId, updateRoomIdToUserId] = useState(new Map());
-
- const [invitedUserIds, updateInvitedUserIds] = useState(new Set());
-
- const usernameRef = useRef(null);
-
- const mx = useMatrixClient();
- const { navigateRoom } = useRoomNavigate();
-
- function getMapCopy(myMap) {
- const newMap = new Map();
- myMap.forEach((data, key) => {
- newMap.set(key, data);
- });
- return newMap;
- }
- function addUserToProc(userId) {
- procUsers.add(userId);
- updateProcUsers(new Set(Array.from(procUsers)));
- }
- function deleteUserFromProc(userId) {
- procUsers.delete(userId);
- updateProcUsers(new Set(Array.from(procUsers)));
- }
-
- function onDMCreated(newRoomId) {
- const myDMPartnerId = roomIdToUserId.get(newRoomId);
- if (typeof myDMPartnerId === 'undefined') return;
-
- createdDM.set(myDMPartnerId, newRoomId);
- roomIdToUserId.delete(newRoomId);
-
- deleteUserFromProc(myDMPartnerId);
- updateCreatedDM(getMapCopy(createdDM));
- updateRoomIdToUserId(getMapCopy(roomIdToUserId));
- }
-
- async function searchUser(username) {
- const inputUsername = username.trim();
- if (isSearching || inputUsername === '' || inputUsername === searchQuery.username) return;
- const isInputUserId = inputUsername[0] === '@' && inputUsername.indexOf(':') > 1;
- updateIsSearching(true);
- updateSearchQuery({ username: inputUsername });
-
- if (isInputUserId) {
- try {
- const result = await mx.getProfileInfo(inputUsername);
- updateUsers([
- {
- user_id: inputUsername,
- display_name: result.displayname,
- avatar_url: result.avatar_url,
- },
- ]);
- } catch (e) {
- updateSearchQuery({ error: `${inputUsername} not found!` });
- }
- } else {
- try {
- const result = await mx.searchUserDirectory({
- term: inputUsername,
- limit: 20,
- });
- if (result.results.length === 0) {
- updateSearchQuery({ error: `No matches found for "${inputUsername}"!` });
- updateIsSearching(false);
- return;
- }
- updateUsers(result.results);
- } catch (e) {
- updateSearchQuery({ error: 'Something went wrong!' });
- }
- }
- updateIsSearching(false);
- }
-
- async function createDM(userId) {
- if (mx.getUserId() === userId) return;
- const dmRoomId = getDMRoomFor(mx, userId)?.roomId;
- if (dmRoomId) {
- navigateRoom(dmRoomId);
- onRequestClose();
- return;
- }
-
- try {
- addUserToProc(userId);
- procUserError.delete(userId);
- updateUserProcError(getMapCopy(procUserError));
-
- const result = await roomActions.createDM(mx, userId, await hasDevices(mx, userId));
- roomIdToUserId.set(result.room_id, userId);
- updateRoomIdToUserId(getMapCopy(roomIdToUserId));
- onDMCreated(result.room_id);
- } catch (e) {
- deleteUserFromProc(userId);
- if (typeof e.message === 'string') procUserError.set(userId, e.message);
- else procUserError.set(userId, 'Something went wrong!');
- updateUserProcError(getMapCopy(procUserError));
- }
- }
-
- async function inviteToRoom(userId) {
- if (typeof roomId === 'undefined') return;
- try {
- addUserToProc(userId);
- procUserError.delete(userId);
- updateUserProcError(getMapCopy(procUserError));
-
- await mx.invite(roomId, userId);
-
- invitedUserIds.add(userId);
- updateInvitedUserIds(new Set(Array.from(invitedUserIds)));
- deleteUserFromProc(userId);
- } catch (e) {
- deleteUserFromProc(userId);
- if (typeof e.message === 'string') procUserError.set(userId, e.message);
- else procUserError.set(userId, 'Something went wrong!');
- updateUserProcError(getMapCopy(procUserError));
- }
- }
-
- function renderUserList() {
- const renderOptions = (userId) => {
- const messageJSX = (message, isPositive) => (
-
-
- {message}
-
-
- );
-
- if (mx.getUserId() === userId) return null;
- if (procUsers.has(userId)) {
- return ;
- }
- if (createdDM.has(userId)) {
- // eslint-disable-next-line max-len
- return (
-
- );
- }
- if (invitedUserIds.has(userId)) {
- return messageJSX('Invited', true);
- }
- if (typeof roomId === 'string') {
- const member = mx.getRoom(roomId).getMember(userId);
- if (member !== null) {
- const userMembership = member.membership;
- switch (userMembership) {
- case 'join':
- return messageJSX('Already joined', true);
- case 'invite':
- return messageJSX('Already Invited', true);
- case 'ban':
- return messageJSX('Banned', false);
- default:
- }
- }
- }
- return typeof roomId === 'string' ? (
-
- ) : (
-
- );
- };
- const renderError = (userId) => {
- if (!procUserError.has(userId)) return null;
- return (
-
- {procUserError.get(userId)}
-
- );
- };
-
- return users.map((user) => {
- const userId = user.user_id;
- const name = typeof user.display_name === 'string' ? user.display_name : userId;
- return (
-
- );
- });
- }
-
- useEffect(() => {
- if (isOpen && typeof searchTerm === 'string') searchUser(searchTerm);
- return () => {
- updateIsSearching(false);
- updateSearchQuery({});
- updateUsers([]);
- updateProcUsers(new Set());
- updateUserProcError(new Map());
- updateCreatedDM(new Map());
- updateRoomIdToUserId(new Map());
- updateInvitedUserIds(new Set());
- };
- }, [isOpen, searchTerm]);
-
- return (
- }
- onRequestClose={onRequestClose}
- >
-
-
-
- {typeof searchQuery.username !== 'undefined' && isSearching && (
-
-
- {`Searching for user "${searchQuery.username}"...`}
-
- )}
- {typeof searchQuery.username !== 'undefined' && !isSearching && (
-
{`Search result for user "${searchQuery.username}"`}
- )}
- {searchQuery.error && (
-
- {searchQuery.error}
-
- )}
-
- {users.length !== 0 &&
{renderUserList()}
}
-
-
- );
-}
-
-InviteUser.defaultProps = {
- roomId: undefined,
- searchTerm: undefined,
-};
-
-InviteUser.propTypes = {
- isOpen: PropTypes.bool.isRequired,
- roomId: PropTypes.string,
- searchTerm: PropTypes.string,
- onRequestClose: PropTypes.func.isRequired,
-};
-
-export default InviteUser;
diff --git a/src/app/organisms/invite-user/InviteUser.scss b/src/app/organisms/invite-user/InviteUser.scss
deleted file mode 100644
index 30c9b921..00000000
--- a/src/app/organisms/invite-user/InviteUser.scss
+++ /dev/null
@@ -1,45 +0,0 @@
-@use '../../partials/dir';
-
-.invite-user {
- margin-top: var(--sp-extra-tight);
- @include dir.side(margin, var(--sp-normal), var(--sp-extra-tight));
-
- &__form {
- display: flex;
- align-items: flex-end;
-
- & .input-container {
- flex: 1;
- min-width: 0;
- @include dir.side(margin, 0, var(--sp-normal));
- }
-
- & .btn-primary {
- padding: {
- top: 11px;
- bottom: 11px;
- }
- }
- }
-
- &__search-status {
- margin-top: var(--sp-extra-loose);
- margin-bottom: var(--sp-tight);
- & .donut-spinner {
- margin: 0 var(--sp-tight);
- }
- }
- &__search-error {
- color: var(--bg-danger);
- }
- &__content {
- border-top: 1px solid var(--bg-surface-border);
- }
-
- & .room-tile {
- margin-top: var(--sp-normal);
- &__options {
- align-self: flex-end;
- }
- }
-}
\ No newline at end of file
diff --git a/src/app/organisms/join-alias/JoinAlias.jsx b/src/app/organisms/join-alias/JoinAlias.jsx
deleted file mode 100644
index d4e313af..00000000
--- a/src/app/organisms/join-alias/JoinAlias.jsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import PropTypes from 'prop-types';
-import './JoinAlias.scss';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import { join } from '../../../client/action/room';
-
-import Text from '../../atoms/text/Text';
-import IconButton from '../../atoms/button/IconButton';
-import Button from '../../atoms/button/Button';
-import Input from '../../atoms/input/Input';
-import Spinner from '../../atoms/spinner/Spinner';
-import Dialog from '../../molecules/dialog/Dialog';
-
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-
-import { useStore } from '../../hooks/useStore';
-import { useRoomNavigate } from '../../hooks/useRoomNavigate';
-import { useMatrixClient } from '../../hooks/useMatrixClient';
-
-const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/;
-
-function JoinAliasContent({ term, requestClose }) {
- const [process, setProcess] = useState(false);
- const [error, setError] = useState(undefined);
-
- const mx = useMatrixClient();
- const mountStore = useStore();
-
- const { navigateRoom } = useRoomNavigate();
-
- const openRoom = (roomId) => {
- navigateRoom(roomId);
- requestClose();
- };
-
- const handleSubmit = async (e) => {
- e.preventDefault();
- mountStore.setItem(true);
- const alias = e.target.alias.value;
- if (alias?.trim() === '') return;
- if (alias.match(ALIAS_OR_ID_REG) === null) {
- setError('Invalid address.');
- return;
- }
- setProcess('Looking for address...');
- setError(undefined);
- let via;
- if (alias.startsWith('#')) {
- try {
- const aliasData = await mx.getRoomIdForAlias(alias);
- via = aliasData?.servers.slice(0, 3) || [];
- if (mountStore.getItem()) {
- setProcess(`Joining ${alias}...`);
- }
- } catch (err) {
- if (!mountStore.getItem()) return;
- setProcess(false);
- setError(
- `Unable to find room/space with ${alias}. Either room/space is private or doesn't exist.`
- );
- }
- }
- try {
- const roomId = await join(mx, alias, false, via);
- if (!mountStore.getItem()) return;
- openRoom(roomId);
- } catch {
- if (!mountStore.getItem()) return;
- setProcess(false);
- setError(`Unable to join ${alias}. Either room/space is private or doesn't exist.`);
- }
- };
-
- return (
-
- );
-}
-JoinAliasContent.defaultProps = {
- term: undefined,
-};
-JoinAliasContent.propTypes = {
- term: PropTypes.string,
- requestClose: PropTypes.func.isRequired,
-};
-
-function useWindowToggle() {
- const [data, setData] = useState(null);
-
- useEffect(() => {
- const handleOpen = (term) => {
- setData({ term });
- };
- navigation.on(cons.events.navigation.JOIN_ALIAS_OPENED, handleOpen);
- return () => {
- navigation.removeListener(cons.events.navigation.JOIN_ALIAS_OPENED, handleOpen);
- };
- }, []);
-
- const onRequestClose = () => setData(null);
-
- return [data, onRequestClose];
-}
-
-function JoinAlias() {
- const [data, requestClose] = useWindowToggle();
-
- return (
-
- );
-}
-
-export default JoinAlias;
diff --git a/src/app/organisms/join-alias/JoinAlias.scss b/src/app/organisms/join-alias/JoinAlias.scss
deleted file mode 100644
index b3684b0b..00000000
--- a/src/app/organisms/join-alias/JoinAlias.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-@use '../../partials/dir';
-
-.join-alias {
- padding: var(--sp-normal);
- @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight));
-
- & > *:not(:first-child) {
- margin-top: var(--sp-normal);
- }
-
- &__error {
- color: var(--tc-danger-high);
- margin-top: var(--sp-extra-tight) !important;
- }
-
- &__btn {
- display: flex;
- gap: var(--sp-normal);
- }
-}
\ No newline at end of file
diff --git a/src/app/organisms/profile-viewer/ProfileViewer.jsx b/src/app/organisms/profile-viewer/ProfileViewer.jsx
deleted file mode 100644
index 1ed89692..00000000
--- a/src/app/organisms/profile-viewer/ProfileViewer.jsx
+++ /dev/null
@@ -1,439 +0,0 @@
-import React, { useState, useEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import './ProfileViewer.scss';
-import { EventTimeline } from 'matrix-js-sdk';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import { openReusableContextMenu } from '../../../client/action/navigation';
-import * as roomActions from '../../../client/action/room';
-
-import {
- getUsername,
- getUsernameOfRoomMember,
- getPowerLabel,
- hasDevices,
-} from '../../../util/matrixUtil';
-import { getEventCords } from '../../../util/common';
-import colorMXID from '../../../util/colorMXID';
-
-import Text from '../../atoms/text/Text';
-import Chip from '../../atoms/chip/Chip';
-import IconButton from '../../atoms/button/IconButton';
-import Input from '../../atoms/input/Input';
-import Avatar from '../../atoms/avatar/Avatar';
-import Button from '../../atoms/button/Button';
-import { MenuItem } from '../../atoms/context-menu/ContextMenu';
-import PowerLevelSelector from '../../molecules/power-level-selector/PowerLevelSelector';
-import Dialog from '../../molecules/dialog/Dialog';
-
-import ShieldEmptyIC from '../../../../public/res/ic/outlined/shield-empty.svg';
-import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg';
-import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-
-import { useForceUpdate } from '../../hooks/useForceUpdate';
-import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
-import { useRoomNavigate } from '../../hooks/useRoomNavigate';
-import { getDMRoomFor } from '../../utils/matrix';
-import { useMatrixClient } from '../../hooks/useMatrixClient';
-import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
-
-function ModerationTools({ roomId, userId }) {
- const mx = useMatrixClient();
- const room = mx.getRoom(roomId);
- const roomMember = room.getMember(userId);
-
- const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel || 0;
- const powerLevel = roomMember?.powerLevel || 0;
- const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
- const canIKick =
- roomMember?.membership === 'join' &&
- roomState?.hasSufficientPowerLevelFor('kick', myPowerLevel) &&
- powerLevel < myPowerLevel;
- const canIBan =
- ['join', 'leave'].includes(roomMember?.membership) &&
- roomState?.hasSufficientPowerLevelFor('ban', myPowerLevel) &&
- powerLevel < myPowerLevel;
-
- const handleKick = (e) => {
- e.preventDefault();
- const kickReason = e.target.elements['kick-reason']?.value.trim();
- mx.kick(roomId, userId, kickReason !== '' ? kickReason : undefined);
- };
-
- const handleBan = (e) => {
- e.preventDefault();
- const banReason = e.target.elements['ban-reason']?.value.trim();
- mx.ban(roomId, userId, banReason !== '' ? banReason : undefined);
- };
-
- return (
-
- {canIKick && (
-
- )}
- {canIBan && (
-
- )}
-
- );
-}
-ModerationTools.propTypes = {
- roomId: PropTypes.string.isRequired,
- userId: PropTypes.string.isRequired,
-};
-
-function SessionInfo({ userId }) {
- const [devices, setDevices] = useState(null);
- const [isVisible, setIsVisible] = useState(false);
- const mx = useMatrixClient();
-
- useEffect(() => {
- let isUnmounted = false;
-
- async function loadDevices() {
- try {
- const crypto = mx.getCrypto();
- const userToDevices = await crypto.getUserDeviceInfo([userId], true);
- const myDevices = Array.from(userToDevices.get(userId).values());
-
- if (isUnmounted) return;
- setDevices(myDevices);
- } catch {
- setDevices([]);
- }
- }
- loadDevices();
-
- return () => {
- isUnmounted = true;
- };
- }, [mx, userId]);
-
- function renderSessionChips() {
- if (!isVisible) return null;
- return (
-
- {devices === null && Loading sessions...}
- {devices?.length === 0 && No session found.}
- {devices !== null &&
- devices.map((device) => (
-
- ))}
-
- );
- }
-
- return (
-
-
- {renderSessionChips()}
-
- );
-}
-
-SessionInfo.propTypes = {
- userId: PropTypes.string.isRequired,
-};
-
-function ProfileFooter({ roomId, userId, onRequestClose }) {
- const [isCreatingDM, setIsCreatingDM] = useState(false);
- const [isIgnoring, setIsIgnoring] = useState(false);
- const mx = useMatrixClient();
- const [isUserIgnored, setIsUserIgnored] = useState(mx.isUserIgnored(userId));
-
- const isMountedRef = useRef(true);
- const { navigateRoom } = useRoomNavigate();
- const room = mx.getRoom(roomId);
- const member = room.getMember(userId);
- const isInvitable = member?.membership !== 'join' && member?.membership !== 'ban';
-
- const [isInviting, setIsInviting] = useState(false);
- const [isInvited, setIsInvited] = useState(member?.membership === 'invite');
-
- const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0;
- const userPL = room.getMember(userId)?.powerLevel || 0;
- const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
-
- const canIKick =
- roomState?.hasSufficientPowerLevelFor('kick', myPowerlevel) && userPL < myPowerlevel;
-
- const isBanned = member?.membership === 'ban';
-
- const onCreated = (dmRoomId) => {
- if (isMountedRef.current === false) return;
- setIsCreatingDM(false);
- navigateRoom(dmRoomId);
- onRequestClose();
- };
-
- useEffect(() => {
- setIsUserIgnored(mx.isUserIgnored(userId));
- setIsIgnoring(false);
- setIsInviting(false);
- }, [mx, userId]);
-
- const openDM = async () => {
- // Check and open if user already have a DM with userId.
- const dmRoomId = getDMRoomFor(mx, userId)?.roomId;
- if (dmRoomId) {
- navigateRoom(dmRoomId);
- onRequestClose();
- return;
- }
-
- // Create new DM
- try {
- setIsCreatingDM(true);
- const result = await roomActions.createDM(mx, userId, await hasDevices(mx, userId));
- onCreated(result.room_id);
- } catch {
- if (isMountedRef.current === false) return;
- setIsCreatingDM(false);
- }
- };
-
- const toggleIgnore = async () => {
- const isIgnored = mx.getIgnoredUsers().includes(userId);
-
- try {
- setIsIgnoring(true);
- if (isIgnored) {
- await roomActions.unignore(mx, [userId]);
- } else {
- await roomActions.ignore(mx, [userId]);
- }
-
- if (isMountedRef.current === false) return;
- setIsUserIgnored(!isIgnored);
- setIsIgnoring(false);
- } catch {
- setIsIgnoring(false);
- }
- };
-
- const toggleInvite = async () => {
- try {
- setIsInviting(true);
- let isInviteSent = false;
- if (isInvited) await mx.kick(roomId, userId);
- else {
- await mx.invite(roomId, userId);
- isInviteSent = true;
- }
- if (isMountedRef.current === false) return;
- setIsInvited(isInviteSent);
- setIsInviting(false);
- } catch {
- setIsInviting(false);
- }
- };
-
- return (
-
-
- {isBanned && canIKick && (
-
- )}
- {(isInvited ? canIKick : room.canInvite(mx.getUserId())) && isInvitable && (
-
- )}
-
-
- );
-}
-ProfileFooter.propTypes = {
- roomId: PropTypes.string.isRequired,
- userId: PropTypes.string.isRequired,
- onRequestClose: PropTypes.func.isRequired,
-};
-
-function useToggleDialog() {
- const [isOpen, setIsOpen] = useState(false);
- const [roomId, setRoomId] = useState(null);
- const [userId, setUserId] = useState(null);
-
- useEffect(() => {
- const loadProfile = (uId, rId) => {
- setIsOpen(true);
- setUserId(uId);
- setRoomId(rId);
- };
- navigation.on(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile);
- return () => {
- navigation.removeListener(cons.events.navigation.PROFILE_VIEWER_OPENED, loadProfile);
- };
- }, []);
-
- const closeDialog = () => setIsOpen(false);
-
- const afterClose = () => {
- setUserId(null);
- setRoomId(null);
- };
-
- return [isOpen, roomId, userId, closeDialog, afterClose];
-}
-
-function useRerenderOnProfileChange(roomId, userId) {
- const mx = useMatrixClient();
- const [, forceUpdate] = useForceUpdate();
- useEffect(() => {
- const handleProfileChange = (mEvent, member) => {
- if (
- mEvent.getRoomId() === roomId &&
- (member.userId === userId || member.userId === mx.getUserId())
- ) {
- forceUpdate();
- }
- };
- mx.on('RoomMember.powerLevel', handleProfileChange);
- mx.on('RoomMember.membership', handleProfileChange);
- return () => {
- mx.removeListener('RoomMember.powerLevel', handleProfileChange);
- mx.removeListener('RoomMember.membership', handleProfileChange);
- };
- }, [mx, roomId, userId]);
-}
-
-function ProfileViewer() {
- const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog();
- useRerenderOnProfileChange(roomId, userId);
- const useAuthentication = useMediaAuthentication();
-
- const mx = useMatrixClient();
- const room = mx.getRoom(roomId);
-
- const renderProfile = () => {
- const roomMember = room.getMember(userId);
- const username = roomMember ? getUsernameOfRoomMember(roomMember) : getUsername(mx, userId);
- const avatarMxc = roomMember?.getMxcAvatarUrl?.() || mx.getUser(userId)?.avatarUrl;
- const avatarUrl =
- avatarMxc && avatarMxc !== 'null'
- ? mx.mxcUrlToHttp(avatarMxc, 80, 80, 'crop', undefined, undefined, useAuthentication)
- : null;
-
- const powerLevel = roomMember?.powerLevel || 0;
- const myPowerLevel = room.getMember(mx.getUserId())?.powerLevel || 0;
-
- const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
- const canChangeRole =
- roomState?.maySendEvent('m.room.power_levels', mx.getUserId()) &&
- (powerLevel < myPowerLevel || userId === mx.getUserId());
-
- const handleChangePowerLevel = async (newPowerLevel) => {
- if (newPowerLevel === powerLevel) return;
- const SHARED_POWER_MSG =
- 'You will not be able to undo this change as you are promoting the user to have the same power level as yourself. Are you sure?';
- const DEMOTING_MYSELF_MSG =
- 'You will not be able to undo this change as you are demoting yourself. Are you sure?';
-
- const isSharedPower = newPowerLevel === myPowerLevel;
- const isDemotingMyself = userId === mx.getUserId();
- if (isSharedPower || isDemotingMyself) {
- const isConfirmed = await confirmDialog(
- 'Change power level',
- isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG,
- 'Change',
- 'caution'
- );
- if (!isConfirmed) return;
- roomActions.setPowerLevel(mx, roomId, userId, newPowerLevel);
- } else {
- roomActions.setPowerLevel(mx, roomId, userId, newPowerLevel);
- }
- };
-
- const handlePowerSelector = (e) => {
- openReusableContextMenu('bottom', getEventCords(e, '.btn-surface'), (closeMenu) => (
- {
- closeMenu();
- handleChangePowerLevel(pl);
- }}
- />
- ));
- };
-
- return (
-
-
-
-
-
- {username}
-
- {userId}
-
-
- Role
-
-
-
-
-
- {userId !== mx.getUserId() && (
-
- )}
-
- );
- };
-
- return (
- }
- >
- {roomId ? renderProfile() : }
-
- );
-}
-
-export default ProfileViewer;
diff --git a/src/app/organisms/profile-viewer/ProfileViewer.scss b/src/app/organisms/profile-viewer/ProfileViewer.scss
deleted file mode 100644
index 1401b777..00000000
--- a/src/app/organisms/profile-viewer/ProfileViewer.scss
+++ /dev/null
@@ -1,110 +0,0 @@
-@use '../../partials/flex';
-@use '../../partials/dir';
-
-.profile-viewer__dialog {
- & .dialog__content__wrapper {
- position: relative;
- }
- & .dialog__content-container {
- padding-top: var(--sp-normal);
- padding-bottom: 89px;
- @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight));
- }
-}
-
-.profile-viewer {
- &__user {
- display: flex;
- padding-bottom: var(--sp-normal);
-
- &__info {
- align-self: flex-end;
- flex: 1;
- min-width: 0;
-
- margin: 0 var(--sp-normal);
-
- & .text {
- white-space: pre-wrap;
- word-break: break-word;
- }
- }
- &__role {
- align-self: flex-end;
- & > .text {
- margin-bottom: var(--sp-ultra-tight);
- }
- }
- }
-
- & .session-info {
- margin-top: var(--sp-normal);
- }
-
- &__buttons {
- position: absolute;
- left: 0;
- bottom: 0;
-
- width: 100%;
- padding: var(--sp-normal);
- background-color: var(--bg-surface);
- border-top: 1px solid var(--bg-surface-border);
- display: flex;
-
- & > *:nth-child(2n) {
- margin: 0 var(--sp-normal)
- }
- & > *:last-child {
- @include dir.side(margin, auto, 0);
- }
- }
-}
-
-.profile-viewer__admin-tool {
- .setting-tile {
- margin-top: var(--sp-loose);
- }
-}
-
-.moderation-tools {
- & > form {
- margin: var(--sp-normal) 0;
- display: flex;
- align-items: flex-end;
- & .input-container {
- @extend .cp-fx__item-one;
- @include dir.side(margin, 0, var(--sp-tight));
- }
- & button {
- height: 46px;
- }
- }
-}
-
-.session-info {
- box-shadow: var(--bs-surface-border);
- border-radius: var(--bo-radius);
- overflow: hidden;
-
- & .context-menu__item button {
- padding: var(--sp-extra-tight);
- & .ic-raw {
- @include dir.side(margin, 0, var(--sp-extra-tight));
- }
- }
-
- &__chips {
- border-top: 1px solid var(--bg-surface-border);
- padding: var(--sp-tight);
- padding-top: var(--sp-ultra-tight);
-
- & > .text {
- margin-top: var(--sp-extra-tight);
- }
- & .chip {
- margin-top: var(--sp-extra-tight);
- @include dir.side(margin, 0, var(--sp-extra-tight));
- }
- }
-}
\ No newline at end of file
diff --git a/src/app/organisms/pw/Dialogs.jsx b/src/app/organisms/pw/Dialogs.jsx
deleted file mode 100644
index 56eb6794..00000000
--- a/src/app/organisms/pw/Dialogs.jsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-
-import ProfileViewer from '../profile-viewer/ProfileViewer';
-import SpaceAddExisting from '../../molecules/space-add-existing/SpaceAddExisting';
-import CreateRoom from '../create-room/CreateRoom';
-import JoinAlias from '../join-alias/JoinAlias';
-
-import ReusableDialog from '../../molecules/dialog/ReusableDialog';
-
-function Dialogs() {
- return (
- <>
-
-
-
-
-
-
- >
- );
-}
-
-export default Dialogs;
diff --git a/src/app/organisms/pw/Windows.jsx b/src/app/organisms/pw/Windows.jsx
deleted file mode 100644
index 77452d15..00000000
--- a/src/app/organisms/pw/Windows.jsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import React, { useState, useEffect } from 'react';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-
-import InviteUser from '../invite-user/InviteUser';
-
-function Windows() {
- const [inviteUser, changeInviteUser] = useState({
- isOpen: false,
- roomId: undefined,
- term: undefined,
- });
-
- function openInviteUser(roomId, searchTerm) {
- changeInviteUser({
- isOpen: true,
- roomId,
- searchTerm,
- });
- }
-
- useEffect(() => {
- navigation.on(cons.events.navigation.INVITE_USER_OPENED, openInviteUser);
- return () => {
- navigation.removeListener(cons.events.navigation.INVITE_USER_OPENED, openInviteUser);
- };
- }, []);
-
- return (
- changeInviteUser({ isOpen: false, roomId: undefined })}
- />
- );
-}
-
-export default Windows;
diff --git a/src/app/organisms/search/Search.jsx b/src/app/organisms/search/Search.jsx
deleted file mode 100644
index ebdac396..00000000
--- a/src/app/organisms/search/Search.jsx
+++ /dev/null
@@ -1,265 +0,0 @@
-import React, { useState, useEffect, useRef, useCallback } from 'react';
-import { useAtomValue } from 'jotai';
-import './Search.scss';
-
-import cons from '../../../client/state/cons';
-import navigation from '../../../client/state/navigation';
-import AsyncSearch from '../../../util/AsyncSearch';
-import { joinRuleToIconSrc } from '../../../util/matrixUtil';
-
-import Text from '../../atoms/text/Text';
-import RawIcon from '../../atoms/system-icons/RawIcon';
-import IconButton from '../../atoms/button/IconButton';
-import Input from '../../atoms/input/Input';
-import RawModal from '../../atoms/modal/RawModal';
-import ScrollView from '../../atoms/scroll/ScrollView';
-import RoomSelector from '../../molecules/room-selector/RoomSelector';
-
-import SearchIC from '../../../../public/res/ic/outlined/search.svg';
-import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
-import { useRoomNavigate } from '../../hooks/useRoomNavigate';
-import { useDirects, useRooms, useSpaces } from '../../state/hooks/roomList';
-import { roomToUnreadAtom } from '../../state/room/roomToUnread';
-import { roomToParentsAtom } from '../../state/room/roomToParents';
-import { allRoomsAtom } from '../../state/room-list/roomList';
-import { mDirectAtom } from '../../state/mDirectList';
-import { useKeyDown } from '../../hooks/useKeyDown';
-import { openSearch } from '../../../client/action/navigation';
-import { useMatrixClient } from '../../hooks/useMatrixClient';
-import { factoryRoomIdByActivity } from '../../utils/sort';
-
-function useVisiblityToggle(setResult) {
- const [isOpen, setIsOpen] = useState(false);
-
- useEffect(() => {
- const handleSearchOpen = (term) => {
- setResult({
- term,
- chunk: [],
- });
- setIsOpen(true);
- };
- navigation.on(cons.events.navigation.SEARCH_OPENED, handleSearchOpen);
- return () => {
- navigation.removeListener(cons.events.navigation.SEARCH_OPENED, handleSearchOpen);
- };
- }, []);
-
- useEffect(() => {
- if (isOpen === false) {
- setResult(undefined);
- }
- }, [isOpen]);
-
- useKeyDown(
- window,
- useCallback((event) => {
- // Ctrl/Cmd +
- if (event.ctrlKey || event.metaKey) {
- // open search modal
- if (event.key === 'k') {
- event.preventDefault();
- // means some menu or modal window is open
- if (
- document.body.lastChild.className !== 'ReactModalPortal' ||
- navigation.isRawModalVisible
- ) {
- return;
- }
- openSearch();
- }
- }
- }, [])
- );
-
- const requestClose = () => setIsOpen(false);
-
- return [isOpen, requestClose];
-}
-
-function mapRoomIds(mx, roomIds, directs, roomIdToParents) {
- return roomIds.map((roomId) => {
- const room = mx.getRoom(roomId);
- const parentSet = roomIdToParents.get(roomId);
- const parentNames = parentSet ? [] : undefined;
- parentSet?.forEach((parentId) => parentNames.push(mx.getRoom(parentId).name));
-
- const parents = parentNames ? parentNames.join(', ') : null;
-
- let type = 'room';
- if (room.isSpaceRoom()) type = 'space';
- else if (directs.includes(roomId)) type = 'direct';
-
- return {
- type,
- name: room.name,
- parents,
- roomId,
- room,
- };
- });
-}
-
-function Search() {
- const [result, setResult] = useState(null);
- const [asyncSearch] = useState(new AsyncSearch());
- const [isOpen, requestClose] = useVisiblityToggle(setResult);
- const searchRef = useRef(null);
- const mx = useMatrixClient();
- const { navigateRoom, navigateSpace } = useRoomNavigate();
- const mDirects = useAtomValue(mDirectAtom);
- const spaces = useSpaces(mx, allRoomsAtom);
- const rooms = useRooms(mx, allRoomsAtom, mDirects);
- const directs = useDirects(mx, allRoomsAtom, mDirects);
- const roomToUnread = useAtomValue(roomToUnreadAtom);
- const roomToParents = useAtomValue(roomToParentsAtom);
-
- const handleSearchResults = (chunk, term) => {
- setResult({
- term,
- chunk,
- });
- };
-
- const generateResults = (term) => {
- const prefix = term.match(/^[#@*]/)?.[0];
-
- if (term.length > 1) {
- asyncSearch.search(prefix ? term.slice(1) : term);
- return;
- }
-
- let ids = null;
-
- if (prefix) {
- if (prefix === '#') ids = [...rooms];
- else if (prefix === '@') ids = [...directs];
- else ids = [...spaces];
- } else {
- ids = [...rooms].concat([...directs], [...spaces]);
- }
-
- ids.sort(factoryRoomIdByActivity(mx));
- const mappedIds = mapRoomIds(mx, ids, directs, roomToParents);
- asyncSearch.setup(mappedIds, { keys: 'name', isContain: true, limit: 20 });
- if (prefix) handleSearchResults(mappedIds, prefix);
- else asyncSearch.search(term);
- };
-
- const loadRecentRooms = () => {
- const recentRooms = [];
- handleSearchResults(mapRoomIds(mx, recentRooms, directs, roomToParents).reverse());
- };
-
- const handleAfterOpen = () => {
- searchRef.current.focus();
- loadRecentRooms();
- asyncSearch.on(asyncSearch.RESULT_SENT, handleSearchResults);
-
- if (typeof result.term === 'string') {
- generateResults(result.term);
- searchRef.current.value = result.term;
- }
- };
-
- const handleAfterClose = () => {
- asyncSearch.removeListener(asyncSearch.RESULT_SENT, handleSearchResults);
- };
-
- const handleOnChange = () => {
- const { value } = searchRef.current;
- if (value.length === 0) {
- loadRecentRooms();
- return;
- }
- generateResults(value);
- };
-
- const handleCross = (e) => {
- e.preventDefault();
- const { value } = searchRef.current;
- if (value.length === 0) requestClose();
- else {
- searchRef.current.value = '';
- searchRef.current.focus();
- loadRecentRooms();
- }
- };
-
- const openItem = (roomId, type) => {
- if (type === 'space') navigateSpace(roomId);
- else navigateRoom(roomId);
- requestClose();
- };
-
- const openFirstResult = () => {
- const { chunk } = result;
- if (chunk?.length > 0) {
- const item = chunk[0];
- openItem(item.roomId, item.type);
- }
- };
-
- const renderRoomSelector = (item) => {
- let imageSrc = null;
- let iconSrc = null;
- if (item.type === 'direct') {
- imageSrc =
- item.room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
- } else {
- iconSrc = joinRuleToIconSrc(item.room.getJoinRule(), item.type === 'space');
- }
-
- return (
- 0}
- onClick={() => openItem(item.roomId, item.type)}
- />
- );
- };
-
- return (
-
-
-
-
-
-
- {Array.isArray(result?.chunk) && result.chunk.map(renderRoomSelector)}
-
-
-
-
- Type # for rooms, @ for DMs and * for spaces. Hotkey: Ctrl + k
-
-
-
- );
-}
-
-export default Search;
diff --git a/src/app/organisms/search/Search.scss b/src/app/organisms/search/Search.scss
deleted file mode 100644
index 3612d742..00000000
--- a/src/app/organisms/search/Search.scss
+++ /dev/null
@@ -1,80 +0,0 @@
-@use '../../partials/dir';
-
-.search-dialog__modal {
- --modal-height: 380px;
- height: 100%;
- background-color: var(--bg-surface);
-}
-
-.search-dialog {
- display: flex;
- flex-direction: column;
- height: 100%;
- width: 100%;
-
- &__input {
- padding: var(--sp-normal);
- display: flex;
- align-items: center;
- position: relative;
-
- & > .ic-raw {
- position: absolute;
- --away: calc(var(--sp-normal) + var(--sp-tight));
- @include dir.prop(left, var(--away), unset);
- @include dir.prop(right, unset, var(--away));
- }
- & > .ic-btn {
- border-radius: calc(var(--bo-radius) / 2);
- position: absolute;
- --away: calc(var(--sp-normal) + var(--sp-extra-tight));
- @include dir.prop(right, var(--away), unset);
- @include dir.prop(left, unset, var(--away));
- }
- & .input-container {
- min-width: 0;
- flex: 1;
- }
-
- & input {
- padding-left: 40px;
- padding-right: 40px;
- font-size: var(--fs-s1);
- letter-spacing: var(--ls-s1);
- line-height: var(--lh-s1);
- color: var(--tc-surface-high);
- }
- }
- &__content-wrapper {
- min-height: 0;
- flex: 1;
- position: relative;
- &::before,
- &::after {
- position: absolute;
- top: 0;
- z-index: 99;
- content: "";
- display: inline-block;
- width: 100%;
- height: 8px;
- background-image: linear-gradient(to bottom, var(--bg-surface), var(--bg-surface-transparent));
- }
- &::after {
- top: unset;
- bottom: 0;
- background-image: linear-gradient(to bottom, var(--bg-surface-transparent), var(--bg-surface));
- }
- }
-
- &__content {
- padding: var(--sp-extra-tight);
- @include dir.side(padding, var(--sp-normal), var(--sp-extra-tight));
- }
-
- &__footer {
- padding: var(--sp-tight) var(--sp-normal);
- text-align: center;
- }
-
-}
\ No newline at end of file
diff --git a/src/app/pages/App.tsx b/src/app/pages/App.tsx
index b16462df..935790ac 100644
--- a/src/app/pages/App.tsx
+++ b/src/app/pages/App.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import { Provider as JotaiProvider } from 'jotai';
import { RouterProvider } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -13,9 +13,19 @@ import { ScreenSizeProvider, useScreenSize } from '../hooks/useScreenSize';
const queryClient = new QueryClient();
+const useLastNodeToDetectReactPortalEntry = () => {
+ useEffect(() => {
+ const lastDiv = document.createElement('div');
+ lastDiv.setAttribute('data-last-node', 'true');
+ document.body.appendChild(lastDiv);
+ }, []);
+};
+
function App() {
const screenSize = useScreenSize();
+ useLastNodeToDetectReactPortalEntry();
+
return (
diff --git a/src/app/pages/Router.tsx b/src/app/pages/Router.tsx
index 4c0e2a4a..18591f29 100644
--- a/src/app/pages/Router.tsx
+++ b/src/app/pages/Router.tsx
@@ -30,7 +30,6 @@ import {
_SERVER_PATH,
CREATE_PATH,
} from './paths';
-import { isAuthenticated } from '../../client/state/auth';
import {
getAppPathFromHref,
getExploreFeaturedPath,
@@ -68,6 +67,7 @@ import { HomeCreateRoom } from './client/home/CreateRoom';
import { Create } from './client/create';
import { CreateSpaceModalRenderer } from '../features/create-space';
import { SearchModalRenderer } from '../features/search';
+import { getFallbackSession } from '../state/sessions';
export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize) => {
const { hashRouter } = clientConfig;
@@ -78,7 +78,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
{
- if (isAuthenticated()) return redirect(getHomePath());
+ if (getFallbackSession()) return redirect(getHomePath());
const afterLoginPath = getAppPathFromHref(getOriginBaseUrl(), window.location.href);
if (afterLoginPath) setAfterLoginRedirectPath(afterLoginPath);
return redirect(getLoginPath());
@@ -86,7 +86,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
/>
{
- if (isAuthenticated()) {
+ if (getFallbackSession()) {
return redirect(getHomePath());
}
@@ -106,7 +106,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
{
- if (!isAuthenticated()) {
+ if (!getFallbackSession()) {
const afterLoginPath = getAppPathFromHref(
getOriginBaseUrl(hashRouter),
window.location.href
diff --git a/src/app/pages/auth/login/loginUtil.ts b/src/app/pages/auth/login/loginUtil.ts
index 7e1c7153..ba5b0bc9 100644
--- a/src/app/pages/auth/login/loginUtil.ts
+++ b/src/app/pages/auth/login/loginUtil.ts
@@ -4,13 +4,13 @@ import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { ClientConfig, clientAllowedServer } from '../../../hooks/useClientConfig';
import { autoDiscovery, specVersions } from '../../../cs-api';
-import { updateLocalStore } from '../../../../client/action/auth';
import { ErrorCode } from '../../../cs-errorcode';
import {
deleteAfterLoginRedirectPath,
getAfterLoginRedirectPath,
} from '../../afterLoginRedirectPath';
import { getHomePath } from '../../pathUtils';
+import { setFallbackSession } from '../../../state/sessions';
export enum GetBaseUrlError {
NotAllow = 'NotAllow',
@@ -114,7 +114,7 @@ export const useLoginComplete = (data?: CustomLoginResponse) => {
useEffect(() => {
if (data) {
const { response: loginRes, baseUrl: loginBaseUrl } = data;
- updateLocalStore(loginRes.access_token, loginRes.device_id, loginRes.user_id, loginBaseUrl);
+ setFallbackSession(loginRes.access_token, loginRes.device_id, loginRes.user_id, loginBaseUrl);
const afterLoginRedirectUrl = getAfterLoginRedirectPath();
deleteAfterLoginRedirectPath();
navigate(afterLoginRedirectUrl ?? getHomePath(), { replace: true });
diff --git a/src/app/pages/auth/register/registerUtil.ts b/src/app/pages/auth/register/registerUtil.ts
index e8145780..86a38cb5 100644
--- a/src/app/pages/auth/register/registerUtil.ts
+++ b/src/app/pages/auth/register/registerUtil.ts
@@ -8,7 +8,6 @@ import {
} from 'matrix-js-sdk';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
-import { updateLocalStore } from '../../../../client/action/auth';
import { LoginPathSearchParams } from '../../paths';
import { ErrorCode } from '../../../cs-errorcode';
import {
@@ -17,6 +16,7 @@ import {
} from '../../afterLoginRedirectPath';
import { getHomePath, getLoginPath, withSearchParam } from '../../pathUtils';
import { getMxIdLocalPart, getMxIdServer } from '../../../utils/matrix';
+import { setFallbackSession } from '../../../state/sessions';
export enum RegisterError {
UserTaken = 'UserTaken',
@@ -119,7 +119,7 @@ export const useRegisterComplete = (data?: CustomRegisterResponse) => {
const deviceId = response.device_id;
if (accessToken && deviceId) {
- updateLocalStore(accessToken, deviceId, userId, baseUrl);
+ setFallbackSession(accessToken, deviceId, userId, baseUrl);
const afterLoginRedirectPath = getAfterLoginRedirectPath();
deleteAfterLoginRedirectPath();
navigate(afterLoginRedirectPath ?? getHomePath(), { replace: true });
diff --git a/src/app/pages/client/ClientRoot.tsx b/src/app/pages/client/ClientRoot.tsx
index c48dbf53..e1a5dc0c 100644
--- a/src/app/pages/client/ClientRoot.tsx
+++ b/src/app/pages/client/ClientRoot.tsx
@@ -23,21 +23,18 @@ import {
logoutClient,
startClient,
} from '../../../client/initMatrix';
-import { getSecret } from '../../../client/state/auth';
import { SplashScreen } from '../../components/splash-screen';
import { ServerConfigsLoader } from '../../components/ServerConfigsLoader';
import { CapabilitiesProvider } from '../../hooks/useCapabilities';
import { MediaConfigProvider } from '../../hooks/useMediaConfig';
import { MatrixClientProvider } from '../../hooks/useMatrixClient';
import { SpecVersions } from './SpecVersions';
-import Windows from '../../organisms/pw/Windows';
-import Dialogs from '../../organisms/pw/Dialogs';
-import ReusableContextMenu from '../../atoms/context-menu/ReusableContextMenu';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { useSyncState } from '../../hooks/useSyncState';
import { stopPropagation } from '../../utils/keyboard';
import { SyncStatus } from './SyncStatus';
import { AuthMetadataProvider } from '../../hooks/useAuthMetadata';
+import { getFallbackSession } from '../../state/sessions';
function ClientRootLoading() {
return (
@@ -146,10 +143,16 @@ type ClientRootProps = {
};
export function ClientRoot({ children }: ClientRootProps) {
const [loading, setLoading] = useState(true);
- const { baseUrl } = getSecret();
+ const { baseUrl } = getFallbackSession() ?? {};
const [loadState, loadMatrix] = useAsyncCallback(
- useCallback(() => initClient(getSecret() as any), [])
+ useCallback(() => {
+ const session = getFallbackSession();
+ if (!session) {
+ throw new Error('No session Found!');
+ }
+ return initClient(session);
+ }, [])
);
const mx = loadState.status === AsyncStatus.Success ? loadState.data : undefined;
const [startState, startMatrix] = useAsyncCallback(
@@ -214,9 +217,6 @@ export function ClientRoot({ children }: ClientRootProps) {
{children}
-
-
-
diff --git a/src/app/pages/client/direct/Direct.tsx b/src/app/pages/client/direct/Direct.tsx
index 51de3946..04edf109 100644
--- a/src/app/pages/client/direct/Direct.tsx
+++ b/src/app/pages/client/direct/Direct.tsx
@@ -42,7 +42,7 @@ import { useDirectRooms } from './useDirectRooms';
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
import { useRoomsUnread } from '../../../state/hooks/unread';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { stopPropagation } from '../../../utils/keyboard';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
diff --git a/src/app/pages/client/home/Home.tsx b/src/app/pages/client/home/Home.tsx
index 2597bb73..d87db963 100644
--- a/src/app/pages/client/home/Home.tsx
+++ b/src/app/pages/client/home/Home.tsx
@@ -53,7 +53,7 @@ import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page';
import { useRoomsUnread } from '../../../state/hooks/unread';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
import { stopPropagation } from '../../../utils/keyboard';
import { useSetting } from '../../../state/hooks/settings';
diff --git a/src/app/pages/client/inbox/Notifications.tsx b/src/app/pages/client/inbox/Notifications.tsx
index afdfec6d..4cc94d91 100644
--- a/src/app/pages/client/inbox/Notifications.tsx
+++ b/src/app/pages/client/inbox/Notifications.tsx
@@ -73,7 +73,7 @@ import * as customHtmlCss from '../../../styles/CustomHtml.css';
import { useRoomNavigate } from '../../../hooks/useRoomNavigate';
import { useRoomUnread } from '../../../state/hooks/unread';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { ContainerColor } from '../../../styles/ContainerColor.css';
import { VirtualTile } from '../../../components/virtualizer';
import { UserAvatar } from '../../../components/user-avatar';
diff --git a/src/app/pages/client/sidebar/DirectTab.tsx b/src/app/pages/client/sidebar/DirectTab.tsx
index bd8090d3..dcd44e2f 100644
--- a/src/app/pages/client/sidebar/DirectTab.tsx
+++ b/src/app/pages/client/sidebar/DirectTab.tsx
@@ -21,7 +21,7 @@ import { UnreadBadge } from '../../../components/unread-badge';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
import { useDirectRooms } from '../direct/useDirectRooms';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { stopPropagation } from '../../../utils/keyboard';
import { settingsAtom } from '../../../state/settings';
import { useSetting } from '../../../state/hooks/settings';
diff --git a/src/app/pages/client/sidebar/HomeTab.tsx b/src/app/pages/client/sidebar/HomeTab.tsx
index c8a80280..778b729e 100644
--- a/src/app/pages/client/sidebar/HomeTab.tsx
+++ b/src/app/pages/client/sidebar/HomeTab.tsx
@@ -22,7 +22,7 @@ import { UnreadBadge } from '../../../components/unread-badge';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath';
import { useHomeRooms } from '../home/useHomeRooms';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { stopPropagation } from '../../../utils/keyboard';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
diff --git a/src/app/pages/client/sidebar/SpaceTabs.tsx b/src/app/pages/client/sidebar/SpaceTabs.tsx
index c06fab92..6fc528a1 100644
--- a/src/app/pages/client/sidebar/SpaceTabs.tsx
+++ b/src/app/pages/client/sidebar/SpaceTabs.tsx
@@ -80,7 +80,7 @@ import { useOpenedSidebarFolderAtom } from '../../../state/hooks/openedSidebarFo
import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useRoomsUnread } from '../../../state/hooks/unread';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { copyToClipboard } from '../../../utils/dom';
import { stopPropagation } from '../../../utils/keyboard';
import { getMatrixToRoom } from '../../../plugins/matrix-to';
diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx
index fba3de50..fe1612fd 100644
--- a/src/app/pages/client/space/Space.tsx
+++ b/src/app/pages/client/space/Space.tsx
@@ -59,7 +59,7 @@ import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page
import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useRecursiveChildScopeFactory, useSpaceChildren } from '../../../state/hooks/roomList';
import { roomToParentsAtom } from '../../../state/room/roomToParents';
-import { markAsRead } from '../../../../client/action/notifications';
+import { markAsRead } from '../../../utils/notifications';
import { useRoomsUnread } from '../../../state/hooks/unread';
import { UseStateProvider } from '../../../components/UseStateProvider';
import { LeaveSpacePrompt } from '../../../components/leave-space-prompt';
diff --git a/src/app/partials/_dir.scss b/src/app/partials/_dir.scss
deleted file mode 100644
index b5d4a696..00000000
--- a/src/app/partials/_dir.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-* NOTICE: only use this
-* when sides are un-even
-* if they are even just use $property: 0 $spacing;
-*/
-
-@mixin side($property, $left, $right) {
- #{$property}: {
- left: $left;
- right: $right;
- };
-
- [dir=rtl] & {
- #{$property}: {
- left: $right;
- right: $left;
- };
- }
-}
-
-@mixin prop($property, $ltr, $rtl) {
- #{$property}: $ltr;
- [dir=rtl] & {
- #{$property}: $rtl;
- }
-}
\ No newline at end of file
diff --git a/src/app/partials/_flex.scss b/src/app/partials/_flex.scss
deleted file mode 100644
index 950cd1ed..00000000
--- a/src/app/partials/_flex.scss
+++ /dev/null
@@ -1,58 +0,0 @@
-._s-c {
- justify-content: flex-start;
- align-items: center;
-}
-._c-c {
- justify-content: center;
- align-items: center;
-}
-._e-c {
- justify-content: flex-end;
- align-items: center;
-}
-
-.cp-fx__row {
- display: flex;
- flex-direction: row;
-}
-
-.cp-fx__column {
- display: flex;
- flex-direction: column;
-}
-
-.cp-fx__row--s-c {
- @extend .cp-fx__row;
- @extend ._s-c;
-}
-
-.cp-fx__row--c-c {
- @extend .cp-fx__row;
- @extend ._c-c;
-}
-
-.cp-fx__row--e-c {
- @extend .cp-fx__row;
- @extend ._e-c;
-}
-
-.cp-fx__column--s-c {
- @extend .cp-fx__column;
- @extend ._s-c;
-}
-
-.cp-fx__column--c-c {
- @extend .cp-fx__column;
- @extend ._c-c;
-}
-
-.cp-fx__column--e-c {
- @extend .cp-fx__column;
- @extend ._e-c;
-}
-
-.cp-fx__item-one {
- flex: 1;
- min-width: 0;
- min-height: 0;
-}
diff --git a/src/app/partials/_screen.scss b/src/app/partials/_screen.scss
deleted file mode 100644
index 99a0907b..00000000
--- a/src/app/partials/_screen.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-
-$breakpoint-tablet: 1124px;
-$breakpoint-mobile: 750px;
-
-@mixin smallerThan($deviceBreakpoint) {
- @if $deviceBreakpoint==mobileBreakpoint {
- @media screen and (max-width: $breakpoint-mobile) {
- @content;
- }
- }
- @else if $deviceBreakpoint==tabletBreakpoint {
- @media screen and (max-width: $breakpoint-tablet) {
- @content;
- }
- }
-}
-
-@mixin biggerThan($deviceBreakpoint) {
- @if $deviceBreakpoint==mobileBreakpoint {
- @media screen and (min-width: $breakpoint-mobile) {
- @content;
- }
- } @else if $deviceBreakpoint==tabletBreakpoint {
- @media screen and (min-width: $breakpoint-tablet) {
- @content;
- }
- }
-}
diff --git a/src/app/partials/_text.scss b/src/app/partials/_text.scss
deleted file mode 100644
index ab5e2805..00000000
--- a/src/app/partials/_text.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.cp-txt__ellipsis {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
\ No newline at end of file
diff --git a/src/app/state/sessions.ts b/src/app/state/sessions.ts
index 85bcd10e..a9c34ce9 100644
--- a/src/app/state/sessions.ts
+++ b/src/app/state/sessions.ts
@@ -1,9 +1,9 @@
-import { atom } from 'jotai';
-import {
- atomWithLocalStorage,
- getLocalStorageItem,
- setLocalStorageItem,
-} from './utils/atomWithLocalStorage';
+// import { atom } from 'jotai';
+// import {
+// atomWithLocalStorage,
+// getLocalStorageItem,
+// setLocalStorageItem,
+// } from './utils/atomWithLocalStorage';
export type Session = {
baseUrl: string;
@@ -24,18 +24,29 @@ export type SessionStoreName = {
/**
* Migration code for old session
*/
-const FALLBACK_STORE_NAME: SessionStoreName = {
- sync: 'web-sync-store',
- crypto: 'crypto-store',
-} as const;
+// const FALLBACK_STORE_NAME: SessionStoreName = {
+// sync: 'web-sync-store',
+// crypto: 'crypto-store',
+// } as const;
-const removeFallbackSession = () => {
+export function setFallbackSession(
+ accessToken: string,
+ deviceId: string,
+ userId: string,
+ baseUrl: string
+) {
+ localStorage.setItem('cinny_access_token', accessToken);
+ localStorage.setItem('cinny_device_id', deviceId);
+ localStorage.setItem('cinny_user_id', userId);
+ localStorage.setItem('cinny_hs_base_url', baseUrl);
+}
+export const removeFallbackSession = () => {
localStorage.removeItem('cinny_hs_base_url');
localStorage.removeItem('cinny_user_id');
localStorage.removeItem('cinny_device_id');
localStorage.removeItem('cinny_access_token');
};
-const getFallbackSession = (): Session | undefined => {
+export const getFallbackSession = (): Session | undefined => {
const baseUrl = localStorage.getItem('cinny_hs_base_url');
const userId = localStorage.getItem('cinny_user_id');
const deviceId = localStorage.getItem('cinny_device_id');
@@ -59,71 +70,71 @@ const getFallbackSession = (): Session | undefined => {
* End of migration code for old session
*/
-export const getSessionStoreName = (session: Session): SessionStoreName => {
- if (session.fallbackSdkStores) {
- return FALLBACK_STORE_NAME;
- }
+// export const getSessionStoreName = (session: Session): SessionStoreName => {
+// if (session.fallbackSdkStores) {
+// return FALLBACK_STORE_NAME;
+// }
- return {
- sync: `sync${session.userId}`,
- crypto: `crypto${session.userId}`,
- };
-};
+// return {
+// sync: `sync${session.userId}`,
+// crypto: `crypto${session.userId}`,
+// };
+// };
-export const MATRIX_SESSIONS_KEY = 'matrixSessions';
-const baseSessionsAtom = atomWithLocalStorage(
- MATRIX_SESSIONS_KEY,
- (key) => {
- const defaultSessions: Sessions = [];
- const sessions = getLocalStorageItem(key, defaultSessions);
+// export const MATRIX_SESSIONS_KEY = 'matrixSessions';
+// const baseSessionsAtom = atomWithLocalStorage(
+// MATRIX_SESSIONS_KEY,
+// (key) => {
+// const defaultSessions: Sessions = [];
+// const sessions = getLocalStorageItem(key, defaultSessions);
- // Before multi account support session was stored
- // as multiple item in local storage.
- // So we need these migration code.
- const fallbackSession = getFallbackSession();
- if (fallbackSession) {
- removeFallbackSession();
- sessions.push(fallbackSession);
- setLocalStorageItem(key, sessions);
- }
- return sessions;
- },
- (key, value) => {
- setLocalStorageItem(key, value);
- }
-);
+// // Before multi account support session was stored
+// // as multiple item in local storage.
+// // So we need these migration code.
+// const fallbackSession = getFallbackSession();
+// if (fallbackSession) {
+// removeFallbackSession();
+// sessions.push(fallbackSession);
+// setLocalStorageItem(key, sessions);
+// }
+// return sessions;
+// },
+// (key, value) => {
+// setLocalStorageItem(key, value);
+// }
+// );
-export type SessionsAction =
- | {
- type: 'PUT';
- session: Session;
- }
- | {
- type: 'DELETE';
- session: Session;
- };
+// export type SessionsAction =
+// | {
+// type: 'PUT';
+// session: Session;
+// }
+// | {
+// type: 'DELETE';
+// session: Session;
+// };
-export const sessionsAtom = atom(
- (get) => get(baseSessionsAtom),
- (get, set, action) => {
- if (action.type === 'PUT') {
- const sessions = [...get(baseSessionsAtom)];
- const sessionIndex = sessions.findIndex(
- (session) => session.userId === action.session.userId
- );
- if (sessionIndex === -1) {
- sessions.push(action.session);
- } else {
- sessions.splice(sessionIndex, 1, action.session);
- }
- set(baseSessionsAtom, sessions);
- return;
- }
- if (action.type === 'DELETE') {
- const sessions = get(baseSessionsAtom).filter(
- (session) => session.userId !== action.session.userId
- );
- set(baseSessionsAtom, sessions);
- }
- }
-);
+// export const sessionsAtom = atom(
+// (get) => get(baseSessionsAtom),
+// (get, set, action) => {
+// if (action.type === 'PUT') {
+// const sessions = [...get(baseSessionsAtom)];
+// const sessionIndex = sessions.findIndex(
+// (session) => session.userId === action.session.userId
+// );
+// if (sessionIndex === -1) {
+// sessions.push(action.session);
+// } else {
+// sessions.splice(sessionIndex, 1, action.session);
+// }
+// set(baseSessionsAtom, sessions);
+// return;
+// }
+// if (action.type === 'DELETE') {
+// const sessions = get(baseSessionsAtom).filter(
+// (session) => session.userId !== action.session.userId
+// );
+// set(baseSessionsAtom, sessions);
+// }
+// }
+// );
diff --git a/src/client/action/notifications.ts b/src/app/utils/notifications.ts
similarity index 100%
rename from src/client/action/notifications.ts
rename to src/app/utils/notifications.ts
diff --git a/src/client/action/auth.ts b/src/client/action/auth.ts
deleted file mode 100644
index dbe9baac..00000000
--- a/src/client/action/auth.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import cons from '../state/cons';
-
-export function updateLocalStore(
- accessToken: string,
- deviceId: string,
- userId: string,
- baseUrl: string
-) {
- localStorage.setItem(cons.secretKey.ACCESS_TOKEN, accessToken);
- localStorage.setItem(cons.secretKey.DEVICE_ID, deviceId);
- localStorage.setItem(cons.secretKey.USER_ID, userId);
- localStorage.setItem(cons.secretKey.BASE_URL, baseUrl);
-}
diff --git a/src/client/action/navigation.js b/src/client/action/navigation.js
deleted file mode 100644
index b7c5ae38..00000000
--- a/src/client/action/navigation.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import appDispatcher from '../dispatcher';
-import cons from '../state/cons';
-
-
-export function openSpaceAddExisting(roomId, spaces = false) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_SPACE_ADDEXISTING,
- roomId,
- spaces,
- });
-}
-
-
-export function openCreateRoom(isSpace = false, parentId = null) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_CREATE_ROOM,
- isSpace,
- parentId,
- });
-}
-
-export function openJoinAlias(term) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_JOIN_ALIAS,
- term,
- });
-}
-
-export function openInviteUser(roomId, searchTerm) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_INVITE_USER,
- roomId,
- searchTerm,
- });
-}
-
-export function openProfileViewer(userId, roomId) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_PROFILE_VIEWER,
- userId,
- roomId,
- });
-}
-
-export function openSearch(term) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_SEARCH,
- term,
- });
-}
-
-export function openReusableContextMenu(placement, cords, render, afterClose) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_REUSABLE_CONTEXT_MENU,
- placement,
- cords,
- render,
- afterClose,
- });
-}
-
-export function openReusableDialog(title, render, afterClose) {
- appDispatcher.dispatch({
- type: cons.actions.navigation.OPEN_REUSABLE_DIALOG,
- title,
- render,
- afterClose,
- });
-}
-
diff --git a/src/client/action/room.js b/src/client/action/room.js
deleted file mode 100644
index e39aeed8..00000000
--- a/src/client/action/room.js
+++ /dev/null
@@ -1,279 +0,0 @@
-import { EventTimeline } from 'matrix-js-sdk';
-import { getIdServer } from '../../util/matrixUtil';
-
-/**
- * https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
- * @param {MatrixClient} mx Matrix client
- * @param {string} roomId Id of room to add
- * @param {string} userId User id to which dm || undefined to remove
- * @returns {Promise} A promise
- */
-function addRoomToMDirect(mx, roomId, userId) {
- const mDirectsEvent = mx.getAccountData('m.direct');
- let userIdToRoomIds = {};
-
- if (typeof mDirectsEvent !== 'undefined') userIdToRoomIds = structuredClone(mDirectsEvent.getContent());
-
- // remove it from the lists of any others users
- // (it can only be a DM room for one person)
- Object.keys(userIdToRoomIds).forEach((thisUserId) => {
- const roomIds = userIdToRoomIds[thisUserId];
-
- if (thisUserId !== userId) {
- const indexOfRoomId = roomIds.indexOf(roomId);
- if (indexOfRoomId > -1) {
- roomIds.splice(indexOfRoomId, 1);
- }
- }
- });
-
- // now add it, if it's not already there
- if (userId) {
- const roomIds = userIdToRoomIds[userId] || [];
- if (roomIds.indexOf(roomId) === -1) {
- roomIds.push(roomId);
- }
- userIdToRoomIds[userId] = roomIds;
- }
-
- return mx.setAccountData('m.direct', userIdToRoomIds);
-}
-
-/**
- * Given a room, estimate which of its members is likely to
- * be the target if the room were a DM room and return that user.
- * https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L117
- *
- * @param {Object} room Target room
- * @param {string} myUserId User ID of the current user
- * @returns {string} User ID of the user that the room is probably a DM with
- */
-function guessDMRoomTargetId(room, myUserId) {
- let oldestMemberTs;
- let oldestMember;
-
- // Pick the joined user who's been here longest (and isn't us),
- room.getJoinedMembers().forEach((member) => {
- if (member.userId === myUserId) return;
-
- if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
- oldestMember = member;
- oldestMemberTs = member.events.member.getTs();
- }
- });
- if (oldestMember) return oldestMember.userId;
-
- // if there are no joined members other than us, use the oldest member
- room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getMembers().forEach((member) => {
- if (member.userId === myUserId) return;
-
- if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
- oldestMember = member;
- oldestMemberTs = member.events.member.getTs();
- }
- });
-
- if (typeof oldestMember === 'undefined') return myUserId;
- return oldestMember.userId;
-}
-
-function convertToDm(mx, roomId) {
- const room = mx.getRoom(roomId);
- return addRoomToMDirect(mx, roomId, guessDMRoomTargetId(room, mx.getUserId()));
-}
-
-function convertToRoom(mx, roomId) {
- return addRoomToMDirect(mx, roomId, undefined);
-}
-
-/**
- * @param {MatrixClient} mx
- * @param {string} roomId
- * @param {boolean} isDM
- * @param {string[]} via
- */
-async function join(mx, roomIdOrAlias, isDM = false, via = undefined) {
- try {
- const resultRoom = await mx.joinRoom(roomIdOrAlias, { viaServers: via });
-
- if (isDM) {
- const targetUserId = guessDMRoomTargetId(mx.getRoom(resultRoom.roomId), mx.getUserId());
- await addRoomToMDirect(mx, resultRoom.roomId, targetUserId);
- }
- return resultRoom.roomId;
- } catch (e) {
- throw new Error(e);
- }
-}
-
-async function create(mx, options, isDM = false) {
- try {
- const result = await mx.createRoom(options);
- if (isDM && typeof options.invite?.[0] === 'string') {
- await addRoomToMDirect(mx, result.room_id, options.invite[0]);
- }
- return result;
- } catch (e) {
- const errcodes = ['M_UNKNOWN', 'M_BAD_JSON', 'M_ROOM_IN_USE', 'M_INVALID_ROOM_STATE', 'M_UNSUPPORTED_ROOM_VERSION'];
- if (errcodes.includes(e.errcode)) {
- throw new Error(e);
- }
- throw new Error('Something went wrong!');
- }
-}
-
-async function createDM(mx, userIdOrIds, isEncrypted = true) {
- const options = {
- is_direct: true,
- invite: Array.isArray(userIdOrIds) ? userIdOrIds : [userIdOrIds],
- visibility: 'private',
- preset: 'trusted_private_chat',
- initial_state: [],
- };
- if (isEncrypted) {
- options.initial_state.push({
- type: 'm.room.encryption',
- state_key: '',
- content: {
- algorithm: 'm.megolm.v1.aes-sha2',
- },
- });
- }
-
- const result = await create(mx, options, true);
- return result;
-}
-
-async function createRoom(mx, opts) {
- // joinRule: 'public' | 'invite' | 'restricted'
- const { name, topic, joinRule } = opts;
- const alias = opts.alias ?? undefined;
- const parentId = opts.parentId ?? undefined;
- const isSpace = opts.isSpace ?? false;
- const isEncrypted = opts.isEncrypted ?? false;
- const powerLevel = opts.powerLevel ?? undefined;
- const blockFederation = opts.blockFederation ?? false;
-
- const visibility = joinRule === 'public' ? 'public' : 'private';
- const options = {
- creation_content: undefined,
- name,
- topic,
- visibility,
- room_alias_name: alias,
- initial_state: [],
- power_level_content_override: undefined,
- };
- if (isSpace) {
- options.creation_content = { type: 'm.space' };
- }
- if (blockFederation) {
- options.creation_content = { 'm.federate': false };
- }
- if (isEncrypted) {
- options.initial_state.push({
- type: 'm.room.encryption',
- state_key: '',
- content: {
- algorithm: 'm.megolm.v1.aes-sha2',
- },
- });
- }
- if (powerLevel) {
- options.power_level_content_override = {
- users: {
- [mx.getUserId()]: powerLevel,
- },
- };
- }
- if (parentId) {
- options.initial_state.push({
- type: 'm.space.parent',
- state_key: parentId,
- content: {
- canonical: true,
- via: [getIdServer(mx.getUserId())],
- },
- });
- }
- if (parentId && joinRule === 'restricted') {
- const caps = await mx.getCapabilities();
- if (caps['m.room_versions'].available?.['9'] !== 'stable') {
- throw new Error("ERROR: The server doesn't support restricted rooms");
- }
- if (Number(caps['m.room_versions'].default) < 9) {
- options.room_version = '9';
- }
- options.initial_state.push({
- type: 'm.room.join_rules',
- content: {
- join_rule: 'restricted',
- allow: [{
- type: 'm.room_membership',
- room_id: parentId,
- }],
- },
- });
- }
-
- const result = await create(mx, options);
-
- if (parentId) {
- await mx.sendStateEvent(parentId, 'm.space.child', {
- auto_join: false,
- suggested: false,
- via: [getIdServer(mx.getUserId())],
- }, result.room_id);
- }
-
- return result;
-}
-
-async function ignore(mx, userIds) {
-
- let ignoredUsers = mx.getIgnoredUsers().concat(userIds);
- ignoredUsers = [...new Set(ignoredUsers)];
- await mx.setIgnoredUsers(ignoredUsers);
-}
-
-async function unignore(mx, userIds) {
- const ignoredUsers = mx.getIgnoredUsers();
- await mx.setIgnoredUsers(ignoredUsers.filter((id) => !userIds.includes(id)));
-}
-
-async function setPowerLevel(mx, roomId, userId, powerLevel) {
- const result = await mx.setPowerLevel(roomId, userId, powerLevel);
- return result;
-}
-
-async function setMyRoomNick(mx, roomId, nick) {
- const room = mx.getRoom(roomId);
- const mEvent = room.getLiveTimeline().getState(EventTimeline.FORWARDS).getStateEvents('m.room.member', mx.getUserId());
- const content = mEvent?.getContent();
- if (!content) return;
- await mx.sendStateEvent(roomId, 'm.room.member', {
- ...content,
- displayname: nick,
- }, mx.getUserId());
-}
-
-async function setMyRoomAvatar(mx, roomId, mxc) {
- const room = mx.getRoom(roomId);
- const mEvent = room.getLiveTimeline().getState(EventTimeline.FORWARDS).getStateEvents('m.room.member', mx.getUserId());
- const content = mEvent?.getContent();
- if (!content) return;
- await mx.sendStateEvent(roomId, 'm.room.member', {
- ...content,
- avatar_url: mxc,
- }, mx.getUserId());
-}
-
-export {
- convertToDm,
- convertToRoom,
- join,
- createDM, createRoom,
- ignore, unignore,
- setPowerLevel,
- setMyRoomNick, setMyRoomAvatar,
-};
diff --git a/src/client/dispatcher.js b/src/client/dispatcher.js
deleted file mode 100644
index 12a48721..00000000
--- a/src/client/dispatcher.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import { Dispatcher } from 'flux';
-
-const appDispatcher = new Dispatcher();
-export default appDispatcher;
diff --git a/src/client/initMatrix.ts b/src/client/initMatrix.ts
index b80a080f..487c3f13 100644
--- a/src/client/initMatrix.ts
+++ b/src/client/initMatrix.ts
@@ -1,6 +1,6 @@
import { createClient, MatrixClient, IndexedDBStore, IndexedDBCryptoStore } from 'matrix-js-sdk';
-import { cryptoCallbacks } from './state/secretStorageKeys';
+import { cryptoCallbacks } from './secretStorageKeys';
import { clearNavToActivePathStore } from '../app/state/navToActivePath';
type Session = {
diff --git a/src/client/state/secretStorageKeys.js b/src/client/secretStorageKeys.js
similarity index 83%
rename from src/client/state/secretStorageKeys.js
rename to src/client/secretStorageKeys.js
index 7439e7a5..78c0cb4c 100644
--- a/src/client/state/secretStorageKeys.js
+++ b/src/client/secretStorageKeys.js
@@ -7,18 +7,14 @@ export function storePrivateKey(keyId, privateKey) {
secretStorageKeys.set(keyId, privateKey);
}
-export function hasPrivateKey(keyId) {
+function hasPrivateKey(keyId) {
return secretStorageKeys.get(keyId) instanceof Uint8Array;
}
-export function getPrivateKey(keyId) {
+function getPrivateKey(keyId) {
return secretStorageKeys.get(keyId);
}
-export function deletePrivateKey(keyId) {
- delete secretStorageKeys.delete(keyId);
-}
-
export function clearSecretStorageKeys() {
secretStorageKeys.clear();
}
diff --git a/src/client/state/auth.ts b/src/client/state/auth.ts
deleted file mode 100644
index 9536a927..00000000
--- a/src/client/state/auth.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import cons from './cons';
-
-const isAuthenticated = () => localStorage.getItem(cons.secretKey.ACCESS_TOKEN) !== null;
-
-const getSecret = () => ({
- accessToken: localStorage.getItem(cons.secretKey.ACCESS_TOKEN),
- deviceId: localStorage.getItem(cons.secretKey.DEVICE_ID),
- userId: localStorage.getItem(cons.secretKey.USER_ID),
- baseUrl: localStorage.getItem(cons.secretKey.BASE_URL),
-});
-
-export { isAuthenticated, getSecret };
diff --git a/src/client/state/cons.js b/src/client/state/cons.js
deleted file mode 100644
index 6f4fabe4..00000000
--- a/src/client/state/cons.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const cons = {
- version: '4.9.1',
- secretKey: {
- ACCESS_TOKEN: 'cinny_access_token',
- DEVICE_ID: 'cinny_device_id',
- USER_ID: 'cinny_user_id',
- BASE_URL: 'cinny_hs_base_url',
- },
- status: {
- PRE_FLIGHT: 'pre-flight',
- IN_FLIGHT: 'in-flight',
- SUCCESS: 'success',
- ERROR: 'error',
- },
- actions: {
- navigation: {
- OPEN_SPACE_ADDEXISTING: 'OPEN_SPACE_ADDEXISTING',
- 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_SEARCH: 'OPEN_SEARCH',
- OPEN_REUSABLE_CONTEXT_MENU: 'OPEN_REUSABLE_CONTEXT_MENU',
- OPEN_REUSABLE_DIALOG: 'OPEN_REUSABLE_DIALOG',
- },
- },
- events: {
- navigation: {
- SPACE_ADDEXISTING_OPENED: 'SPACE_ADDEXISTING_OPENED',
- CREATE_ROOM_OPENED: 'CREATE_ROOM_OPENED',
- JOIN_ALIAS_OPENED: 'JOIN_ALIAS_OPENED',
- INVITE_USER_OPENED: 'INVITE_USER_OPENED',
- SEARCH_OPENED: 'SEARCH_OPENED',
- REUSABLE_CONTEXT_MENU_OPENED: 'REUSABLE_CONTEXT_MENU_OPENED',
- REUSABLE_DIALOG_OPENED: 'REUSABLE_DIALOG_OPENED',
- },
- },
-};
-
-Object.freeze(cons);
-
-export default cons;
diff --git a/src/client/state/navigation.js b/src/client/state/navigation.js
deleted file mode 100644
index 743d0199..00000000
--- a/src/client/state/navigation.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import EventEmitter from 'events';
-import appDispatcher from '../dispatcher';
-import cons from './cons';
-
-class Navigation extends EventEmitter {
- constructor() {
- super();
- this.rawModelStack = [];
- }
-
- get isRawModalVisible() {
- return this.rawModelStack.length > 0;
- }
-
- setIsRawModalVisible(visible) {
- if (visible) this.rawModelStack.push(true);
- else this.rawModelStack.pop();
- }
-
- navigate(action) {
- const actions = {
- [cons.actions.navigation.OPEN_SPACE_ADDEXISTING]: () => {
- this.emit(cons.events.navigation.SPACE_ADDEXISTING_OPENED, action.roomId, action.spaces);
- },
- [cons.actions.navigation.OPEN_CREATE_ROOM]: () => {
- this.emit(
- cons.events.navigation.CREATE_ROOM_OPENED,
- action.isSpace,
- action.parentId,
- );
- },
- [cons.actions.navigation.OPEN_JOIN_ALIAS]: () => {
- this.emit(
- cons.events.navigation.JOIN_ALIAS_OPENED,
- action.term,
- );
- },
- [cons.actions.navigation.OPEN_INVITE_USER]: () => {
- this.emit(cons.events.navigation.INVITE_USER_OPENED, action.roomId, action.searchTerm);
- },
- [cons.actions.navigation.OPEN_PROFILE_VIEWER]: () => {
- this.emit(cons.events.navigation.PROFILE_VIEWER_OPENED, action.userId, action.roomId);
- },
- [cons.actions.navigation.OPEN_SEARCH]: () => {
- this.emit(
- cons.events.navigation.SEARCH_OPENED,
- action.term,
- );
- },
- [cons.actions.navigation.OPEN_REUSABLE_CONTEXT_MENU]: () => {
- this.emit(
- cons.events.navigation.REUSABLE_CONTEXT_MENU_OPENED,
- action.placement,
- action.cords,
- action.render,
- action.afterClose,
- );
- },
- [cons.actions.navigation.OPEN_REUSABLE_DIALOG]: () => {
- this.emit(
- cons.events.navigation.REUSABLE_DIALOG_OPENED,
- action.title,
- action.render,
- action.afterClose,
- );
- },
- };
- actions[action.type]?.();
- }
-}
-
-const navigation = new Navigation();
-appDispatcher.register(navigation.navigate.bind(navigation));
-
-export default navigation;
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 00000000..ca28536d
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,131 @@
+@font-face {
+ font-family: Twemoji;
+ src: url('../public/font/Twemoji.Mozilla.v15.1.0.woff2'),
+ url('../public/font/Twemoji.Mozilla.v15.1.0.ttf');
+ font-display: swap;
+}
+
+:root {
+ --tc-link: hsl(213deg 100% 45%);
+
+ /* user mxid colors */
+ --mx-uc-1: hsl(208, 100%, 45%);
+ --mx-uc-2: hsl(302, 100%, 30%);
+ --mx-uc-3: hsl(163, 100%, 30%);
+ --mx-uc-4: hsl(343, 100%, 45%);
+ --mx-uc-5: hsl(24, 100%, 45%);
+ --mx-uc-6: hsl(181, 100%, 30%);
+ --mx-uc-7: hsl(242, 100%, 45%);
+ --mx-uc-8: hsl(94, 100%, 35%);
+
+ --font-emoji: 'Twemoji_DISABLED';
+ --font-secondary: 'InterVariable', var(--font-emoji), sans-serif;
+}
+
+.dark-theme,
+.butter-theme {
+ --tc-link: hsl(213deg 100% 80%);
+
+ --mx-uc-1: hsl(208, 100%, 75%);
+ --mx-uc-2: hsl(301, 100%, 80%);
+ --mx-uc-3: hsl(163, 100%, 70%);
+ --mx-uc-4: hsl(343, 100%, 75%);
+ --mx-uc-5: hsl(24, 100%, 70%);
+ --mx-uc-6: hsl(181, 100%, 60%);
+ --mx-uc-7: hsl(243, 100%, 80%);
+ --mx-uc-8: hsl(94, 100%, 80%);
+
+ --font-secondary: 'InterVariable', var(--font-emoji), sans-serif;
+}
+
+html {
+ height: 100%;
+ overflow: hidden;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ font-family: var(--font-secondary);
+ font-size: 16px;
+ font-weight: 400;
+
+ /*Why font-variant-ligatures => https://github.com/rsms/inter/issues/222 */
+ font-variant-ligatures: no-contextual;
+}
+#root {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+}
+a {
+ color: var(--tc-link);
+ text-decoration: none;
+}
+a:hover {
+ text-decoration: underline;
+}
+
+[data-mx-spoiler][aria-pressed='true'] a {
+ color: transparent;
+ pointer-events: none;
+}
+
+b {
+ font-weight: 500;
+}
+label {
+ margin: 0;
+ padding: 0;
+}
+button,
+textarea {
+ margin: 0;
+ padding: 0;
+ background-color: transparent;
+ color: inherit;
+ font-family: inherit;
+ font-size: inherit;
+ font-weight: inherit;
+ line-height: inherit;
+ letter-spacing: inherit;
+ border: none;
+}
+button {
+ max-width: 100%;
+ text-transform: none;
+ text-align: inherit;
+ overflow: visible;
+ -webkit-appearance: button;
+}
+textarea,
+input,
+input[type],
+input[type='text'],
+input[type='username'],
+input[type='password'],
+input[type='email'],
+input[type='checkbox'] {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+textarea {
+ color: inherit;
+ word-spacing: inherit;
+}
+
+audio:not([controls]) {
+ display: none !important;
+}
diff --git a/src/index.scss b/src/index.scss
deleted file mode 100644
index 14bf4749..00000000
--- a/src/index.scss
+++ /dev/null
@@ -1,522 +0,0 @@
-@use './app/partials/screen';
-
-@font-face {
- font-family: Twemoji;
- src: url('../public/font/Twemoji.Mozilla.v15.1.0.woff2'),
- url('../public/font/Twemoji.Mozilla.v15.1.0.ttf');
- font-display: swap;
-}
-
-:root {
- /* background color | --bg-[background type]: value */
- --bg-surface: #ffffff;
- --bg-surface-transparent: #ffffff00;
- --bg-surface-low: #f6f6f6;
- --bg-surface-low-transparent: #f6f6f600;
- --bg-surface-extra-low: #f6f6f6;
- --bg-surface-extra-low-transparent: #f6f6f600;
- --bg-surface-hover: rgba(0, 0, 0, 3%);
- --bg-surface-active: rgba(0, 0, 0, 5%);
- --bg-surface-border: rgba(0, 0, 0, 6%);
-
- --bg-primary: rgb(18, 69, 168);
- --bg-primary-hover: rgba(18, 69, 168, 80%);
- --bg-primary-active: rgba(18, 69, 168, 70%);
- --bg-primary-border: rgba(18, 69, 168, 38%);
-
- --bg-positive: rgb(1, 115, 67);
- --bg-positive-hover: rgba(1, 115, 67, 8%);
- --bg-positive-active: rgba(1, 115, 67, 15%);
- --bg-positive-border: rgba(1, 115, 67, 40%);
-
- --bg-caution: rgb(134, 67, 0);
- --bg-caution-hover: rgba(134, 67, 0, 8%);
- --bg-caution-active: rgba(134, 67, 0, 15%);
- --bg-caution-border: rgba(134, 67, 0, 40%);
-
- --bg-danger: rgb(157, 15, 15);
- --bg-danger-hover: rgba(157, 15, 15, 5%);
- --bg-danger-active: rgba(157, 15, 15, 10%);
- --bg-danger-border: rgba(157, 15, 15, 20%);
-
- --bg-tooltip: #353535;
- --bg-badge: #989898;
- --bg-ping: hsla(137deg, 100%, 68%, 40%);
- --bg-ping-hover: hsla(137deg, 100%, 68%, 50%);
- --bg-divider: hsla(0, 0%, 0%, 0.1);
-
- /* text color | --tc-[background type]-[priority]: value */
- --tc-surface-high: #000000;
- --tc-surface-normal: rgba(0, 0, 0, 78%);
- --tc-surface-normal-low: rgba(0, 0, 0, 60%);
- --tc-surface-low: rgba(0, 0, 0, 48%);
-
- --tc-primary-high: #ffffff;
- --tc-primary-normal: rgba(255, 255, 255, 68%);
- --tc-primary-low: rgba(255, 255, 255, 40%);
-
- --tc-positive-high: var(--bg-positive);
- --tc-positive-normal: rgb(1, 115, 67, 80%);
- --tc-positive-low: rgb(1, 115, 67, 60%);
-
- --tc-caution-high: var(--bg-caution);
- --tc-caution-normal: rgb(134, 67, 0, 80%);
- --tc-caution-low: rgb(134, 67, 0, 60%);
-
- --tc-danger-high: var(--bg-danger);
- --tc-danger-normal: rgba(157, 15, 15, 88%);
- --tc-danger-low: rgba(157, 15, 15, 60%);
-
- --tc-code: #e62498;
- --tc-link: hsl(213deg 100% 45%);
- --tc-tooltip: white;
- --tc-badge: white;
-
- /* system icons | --ic-[background type]-[priority]: value */
- --ic-surface-high: #272727;
- --ic-surface-normal: #626262;
- --ic-surface-low: #7c7c7c;
- --ic-primary-high: #ffffff;
- --ic-primary-normal: #ffffff;
- --ic-positive-high: rgba(1, 115, 67);
- --ic-positive-normal: rgba(1, 115, 67, 80%);
- --ic-caution-high: rgba(134, 67, 0);
- --ic-caution-normal: rgba(134, 67, 0, 80%);
- --ic-danger-high: rgba(157, 15, 15);
- --ic-danger-normal: rgba(157, 15, 15, 0.7);
-
- /* user mxid colors */
- --mx-uc-1: hsl(208, 100%, 45%);
- --mx-uc-2: hsl(302, 100%, 30%);
- --mx-uc-3: hsl(163, 100%, 30%);
- --mx-uc-4: hsl(343, 100%, 45%);
- --mx-uc-5: hsl(24, 100%, 45%);
- --mx-uc-6: hsl(181, 100%, 30%);
- --mx-uc-7: hsl(242, 100%, 45%);
- --mx-uc-8: hsl(94, 100%, 35%);
-
- /* system icon size | -ic-[size]: value */
- --ic-large: 38px;
- --ic-normal: 24px;
- --ic-small: 20px;
- --ic-extra-small: 18px;
-
- /* avatar size */
- --av-large: 80px;
- --av-normal: 42px;
- --av-small: 36px;
- --av-extra-small: 24px;
-
- /* shadow and overlay */
- --bg-overlay: rgba(0, 0, 0, 20%);
- --bg-overlay-low: rgba(0, 0, 0, 50%);
-
- --bs-popup: 0 0 16px rgba(0, 0, 0, 10%);
-
- --bs-surface-border: inset 0 0 0 1px var(--bg-surface-border);
- --bs-surface-outline: 0 0 0 2px var(--bg-surface-border);
-
- --bs-primary-border: inset 0 0 0 1px var(--bg-primary-border);
- --bs-primary-outline: 0 0 0 2px var(--bg-primary-border);
-
- --bs-positive-border: inset 0 0 0 1px var(--bg-positive-border);
- --bs-positive-outline: 0 0 0 2px var(--bg-positive-border);
-
- --bs-caution-border: inset 0 0 0 1px var(--bg-caution-border);
- --bs-caution-outline: 0 0 0 2px var(--bg-caution-border);
-
- --bs-danger-border: inset 0 0 0 1px var(--bg-danger-border);
- --bs-danger-outline: 0 0 0 2px var(--bg-danger-border);
-
- /* border */
- --bo-radius: 8px;
-
- /* font styles: font-size, letter-spacing, line-hight */
- --fs-h1: 36px;
- --ls-h1: -1.5px;
- --lh-h1: 38px;
-
- --fs-h2: 24px;
- --ls-h2: -0.5px;
- --lh-h2: 30px;
-
- --fs-s1: 18px;
- --ls-s1: -0.2px;
- --lh-s1: 24px;
-
- --fs-b1: 16px;
- --ls-b1: 0.1px;
- --lh-b1: 24px;
-
- --fs-b2: 14px;
- --ls-b2: 0.2px;
- --lh-b2: 20px;
-
- --fs-b3: 12px;
- --ls-b3: 0px;
- --lh-b3: 16px;
-
- /* font-weight */
- --fw-light: 300;
- --fw-normal: 420;
- --fw-medium: 500;
- --fw-bold: 700;
-
- /* spacing | --sp-[space]: value */
- --sp-none: 0px;
- --sp-ultra-tight: 4px;
- --sp-extra-tight: 8px;
- --sp-tight: 12px;
- --sp-normal: 16px;
- --sp-loose: 20px;
- --sp-extra-loose: 32px;
-
- /* other */
- --border-width: 1px;
- --header-height: 54px;
- --navigation-sidebar-width: calc(64px + var(--border-width));
- --navigation-drawer-width: calc(280px + var(--border-width));
- --navigation-width: calc(var(--navigation-sidebar-width) + var(--navigation-drawer-width));
- --people-drawer-width: calc(268px - var(--border-width));
-
- --popup-window-drawer-width: 280px;
-
- @include screen.smallerThan(tabletBreakpoint) {
- --navigation-drawer-width: calc(240px + var(--border-width));
- --people-drawer-width: calc(256px - var(--border-width));
- --popup-window-drawer-width: 240px;
- }
-
- /* transition curves */
- --fluid-push: cubic-bezier(0, 0.8, 0.67, 0.97);
- --fluid-slide-down: cubic-bezier(0.02, 0.82, 0.4, 0.96);
- --fluid-slide-up: cubic-bezier(0.13, 0.56, 0.25, 0.99);
-
- --font-emoji: 'Twemoji_DISABLED';
- --font-primary: 'InterVariable', var(--font-emoji), sans-serif;
- --font-secondary: 'InterVariable', var(--font-emoji), sans-serif;
-}
-
-.silver-theme {
- /* background color | --bg-[background type]: value */
- --bg-surface: hsl(0, 0%, 95%);
- --bg-surface-transparent: hsla(0, 0%, 95%, 0);
- --bg-surface-low: hsl(0, 0%, 91%);
- --bg-surface-low-transparent: hsla(0, 0%, 91%, 0);
- --bg-surface-extra-low: hsl(0, 0%, 91%);
- --bg-surface-extra-low-transparent: hsla(0, 0%, 91%, 0);
-}
-
-.dark-theme,
-.butter-theme {
- /* background color | --bg-[background type]: value */
- --bg-surface: #262626;
- --bg-surface-transparent: #26262600;
- --bg-surface-low: #1a1a1a;
- --bg-surface-low-transparent: #1a1a1a00;
- --bg-surface-extra-low: #1a1a1a;
- --bg-surface-extra-low-transparent: #1a1a1a00;
- --bg-surface-hover: #333333;
- --bg-surface-active: #404040;
- --bg-surface-border: #404040;
-
- --bg-primary: rgb(189, 182, 236);
- --bg-primary-hover: rgba(189, 182, 236, 0.8);
- --bg-primary-active: rgba(189, 182, 236, 70%);
- --bg-primary-border: rgba(189, 182, 236, 38%);
-
- --bg-positive: rgb(133, 224, 186);
- --bg-positive-hover: rgba(133, 224, 186, 8%);
- --bg-positive-active: rgba(133, 224, 186, 15%);
- --bg-positive-border: rgba(133, 224, 186, 40%);
-
- --bg-caution: rgb(227, 186, 145);
- --bg-caution-hover: rgba(227, 186, 145, 8%);
- --bg-caution-active: rgba(227, 186, 145, 15%);
- --bg-caution-border: rgba(227, 186, 145, 40%);
-
- --bg-danger: rgb(230, 157, 157);
- --bg-danger-hover: rgba(230, 157, 157, 5%);
- --bg-danger-active: rgba(230, 157, 157, 10%);
- --bg-danger-border: rgba(230, 157, 157, 20%);
-
- --bg-tooltip: #000;
- --bg-badge: hsl(0, 0%, 75%);
- --bg-ping: hsla(137deg, 100%, 38%, 40%);
- --bg-ping-hover: hsla(137deg, 100%, 38%, 50%);
- --bg-divider: hsla(0, 0%, 100%, 0.1);
-
- /* text color | --tc-[background type]-[priority]: value */
- --tc-surface-high: rgba(255, 255, 255, 98%);
- --tc-surface-normal: rgba(255, 255, 255, 94%);
- --tc-surface-normal-low: rgba(255, 255, 255, 60%);
- --tc-surface-low: rgba(255, 255, 255, 58%);
-
- --tc-primary-high: rgb(44, 40, 67);
- --tc-primary-normal: rgba(44, 40, 67, 0.68);
- --tc-primary-low: rgba(44, 40, 67, 0.4);
-
- --tc-positive-high: var(--bg-positive);
- --tc-positive-normal: rgb(133, 224, 186, 80%);
- --tc-positive-low: rgb(133, 224, 186, 60%);
-
- --tc-caution-high: var(--bg-caution);
- --tc-caution-normal: rgb(227, 186, 145, 80%);
- --tc-caution-low: rgb(227, 186, 145, 60%);
-
- --tc-danger-high: var(--bg-danger);
- --tc-danger-normal: rgba(230, 157, 157, 88%);
- --tc-danger-low: rgba(230, 157, 157, 60%);
-
- --tc-code: #e565b1;
- --tc-link: hsl(213deg 100% 80%);
- --tc-badge: black;
-
- /* system icons | --ic-[background type]-[priority]: value */
- --ic-surface-high: rgb(255, 255, 255);
- --ic-surface-normal: rgba(255, 255, 255, 84%);
- --ic-surface-low: rgba(255, 255, 255, 64%);
- --ic-primary-high: var(--tc-primary-high);
- --ic-primary-normal: var(--tc-primary-high);
- --ic-primary-low: var(--tc-primary-high);
- --ic-positive-high: rgba(133, 224, 186);
- --ic-positive-normal: rgba(133, 224, 186, 80%);
- --ic-caution-high: rgba(227, 186, 145);
- --ic-caution-normal: rgba(227, 186, 145, 80%);
- --ic-danger-high: rgba(230, 157, 157);
- --ic-danger-normal: rgba(230, 157, 157, 0.7);
-
- --mx-uc-1: hsl(208, 100%, 75%);
- --mx-uc-2: hsl(301, 100%, 80%);
- --mx-uc-3: hsl(163, 100%, 70%);
- --mx-uc-4: hsl(343, 100%, 75%);
- --mx-uc-5: hsl(24, 100%, 70%);
- --mx-uc-6: hsl(181, 100%, 60%);
- --mx-uc-7: hsl(243, 100%, 80%);
- --mx-uc-8: hsl(94, 100%, 80%);
-
- /* shadow and overlay */
- --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%);
-
- --bs-surface-border: inset 0 0 0 1px var(--bg-surface-border);
- --bs-surface-outline: 0 0 0 2px var(--bg-surface-border);
-
- --bs-primary-border: inset 0 0 0 1px var(--bg-primary-border);
- --bs-primary-outline: 0 0 0 2px var(--bg-primary-border);
-
- /* font styles: font-size, letter-spacing, line-hight */
- --fs-h1: 35.6px;
-
- --fs-h2: 23.6px;
-
- --fs-s1: 17.6px;
-
- --fs-b1: 14.6px;
- --ls-b1: 0.14px;
-
- --fs-b2: 13.2px;
-
- --fs-b3: 11.2px;
-
- /* override normal font weight for dark mode */
- --fw-normal: 350;
- --fw-medium: 450;
- --fw-bold: 550;
-
- --font-secondary: 'InterVariable', var(--font-emoji), sans-serif;
-}
-
-.butter-theme {
- /* background color | --bg-[background type]: value */
- --bg-surface: #262621;
- --bg-surface-transparent: #26262100;
- --bg-surface-low: #1a1916;
- --bg-surface-low-transparent: #1a191600;
- --bg-surface-extra-low: #1a1916;
- --bg-surface-extra-low-transparent: #1a1916;
- --bg-surface-hover: #33322c;
- --bg-surface-active: #403f38;
- --bg-surface-border: #403f38;
-
- --bg-badge: #c4c1ab;
-
- /* text color | --tc-[background type]-[priority]: value */
- --tc-surface-high: rgb(255, 251, 222);
- --tc-surface-normal: rgba(255, 251, 222, 94%);
- --tc-surface-normal-low: rgba(255, 251, 222, 60%);
- --tc-surface-low: rgba(255, 251, 222, 58%);
-
- /* system icons | --ic-[background type]-[priority]: value */
- --ic-surface-high: rgb(255, 251, 222);
- --ic-surface-normal: rgba(255, 251, 222, 84%);
- --ic-surface-low: rgba(255, 251, 222, 64%);
-}
-
-.font-primary {
- font-family: var(--font-primary);
-
- /* override font styles for primary font */
- --fs-h1: 36px;
- --ls-h1: -1.5px;
- --lh-h1: 38px;
-
- --fs-h2: 24px;
- --ls-h2: -0.5px;
- --lh-h2: 30px;
-
- --fs-s1: 18px;
- --ls-s1: -0.2px;
- --lh-s1: 24px;
-
- --fs-b1: 16px;
- --ls-b1: 0.1px;
- --lh-b1: 24px;
-
- --fs-b2: 14px;
- --ls-b2: 0.2px;
- --lh-b2: 20px;
-
- --fs-b3: 12px;
- --ls-b3: 0px;
- --lh-b3: 16px;
-
- --fw-light: 300;
- --fw-normal: 400;
- --fw-medium: 500;
- --fw-bold: 600;
-}
-
-html {
- height: 100%;
- overflow: hidden;
-}
-
-body {
- margin: 0;
- padding: 0;
- height: 100%;
- font-family: var(--font-secondary);
- font-size: 16px;
- font-weight: var(--fw-normal);
- background-color: var(--bg-surface-low);
-
- /*Why font-variant-ligatures => https://github.com/rsms/inter/issues/222 */
- font-variant-ligatures: no-contextual;
-}
-#root {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
-}
-
-*,
-*::before,
-*::after {
- box-sizing: border-box;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
- -webkit-tap-highlight-color: transparent;
-}
-a {
- color: var(--tc-link);
- text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
-}
-
-[data-mx-spoiler][aria-pressed='true'] a {
- color: transparent;
- pointer-events: none;
-}
-
-b {
- font-weight: var(--fw-medium);
-}
-label {
- margin: 0;
- padding: 0;
-}
-button,
-textarea {
- margin: 0;
- padding: 0;
- background-color: transparent;
- color: inherit;
- font-family: inherit;
- font-size: inherit;
- font-weight: inherit;
- line-height: inherit;
- letter-spacing: inherit;
- border: none;
-}
-button {
- max-width: 100%;
- text-transform: none;
- text-align: inherit;
- overflow: visible;
- -webkit-appearance: button;
-}
-textarea,
-input,
-input[type],
-input[type='text'],
-input[type='username'],
-input[type='password'],
-input[type='email'],
-input[type='checkbox'] {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-}
-input[type='checkbox'] {
- margin: 0;
- padding: 0;
- width: 20px;
- height: 20px;
- border-radius: calc(var(--bo-radius) / 2);
- box-shadow: var(--bs-primary-border);
- background-color: var(--bg-surface);
- cursor: pointer;
- @extend .flex--center;
-
- &:checked {
- background-color: var(--bg-primary);
- &::before {
- content: '';
- display: inline-block;
- width: 12px;
- height: 6px;
- border: 6px solid white;
- border-width: 0 0 3px 3px;
- transform: rotateZ(-45deg) translate(1px, -1px);
- }
- }
-}
-
-textarea {
- color: inherit;
- word-spacing: inherit;
-}
-.noselect {
- -webkit-touch-callout: none; /* iOS Safari */
- -webkit-user-select: none; /* Safari */
- -khtml-user-select: none; /* Konqueror HTML */
- -moz-user-select: none; /* Old versions of Firefox */
- -ms-user-select: none; /* Internet Explorer/Edge */
- user-select: none; /* Non-prefixed version, currently
- supported by Chrome, Edge, Opera and Firefox */
-}
-
-audio:not([controls]) {
- display: none !important;
-}
-
-.flex--center {
- display: flex;
- justify-content: center;
- align-items: center;
-}
diff --git a/src/index.tsx b/src/index.tsx
index 402a4c1b..ddfd30b4 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -8,7 +8,7 @@ import { configClass, varsClass } from 'folds';
enableMapSet();
-import './index.scss';
+import './index.css';
import { trimTrailingSlash } from './app/utils/common';
import App from './app/pages/App';
diff --git a/src/util/AsyncSearch.js b/src/util/AsyncSearch.js
deleted file mode 100644
index d0a2130e..00000000
--- a/src/util/AsyncSearch.js
+++ /dev/null
@@ -1,135 +0,0 @@
-import EventEmitter from 'events';
-
-class AsyncSearch extends EventEmitter {
- constructor() {
- super();
-
- this._reset();
-
- this.RESULT_SENT = 'RESULT_SENT';
- }
-
- _reset() {
- this.dataList = null;
- this.term = null;
- this.searchKeys = null;
- this.isContain = false;
- this.isCaseSensitive = false;
- this.normalizeUnicode = true;
- this.ignoreWhitespace = true;
- this.limit = null;
- this.findingList = [];
-
- this.searchUptoIndex = 0;
- this.sessionStartTimestamp = 0;
- }
-
- _softReset() {
- this.term = null;
- this.findingList = [];
- this.searchUptoIndex = 0;
- this.sessionStartTimestamp = 0;
- }
-
- /**
- * Setup the search.
- * opts.keys are required when dataList items are object.
- *
- * @param {[string | object]} dataList - A list to search in
- * @param {object} opts - Options
- * @param {string | [string]} [opts.keys=null]
- * @param {boolean} [opts.isContain=false] - Add finding to result if it contain search term
- * @param {boolean} [opts.isCaseSensitive=false]
- * @param {boolean} [opts.normalizeUnicode=true]
- * @param {boolean} [opts.ignoreWhitespace=true]
- * @param {number} [opts.limit=null] - Stop search after limit
- */
- setup(dataList, opts) {
- this._reset();
- this.dataList = dataList;
- this.searchKeys = opts?.keys || null;
- this.isContain = opts?.isContain || false;
- this.isCaseSensitive = opts?.isCaseSensitive || false;
- this.normalizeUnicode = opts?.normalizeUnicode || true;
- this.ignoreWhitespace = opts?.ignoreWhitespace || true;
- this.limit = opts?.limit || null;
- }
-
- search(term) {
- this._softReset();
-
- this.term = this._normalize(term);
- if (this.term === '') {
- this._sendFindings();
- return;
- }
-
- this._find(this.sessionStartTimestamp, 0);
- }
-
- _find(sessionTimestamp, lastFindingCount) {
- if (sessionTimestamp !== this.sessionStartTimestamp) return;
- this.sessionStartTimestamp = window.performance.now();
-
- for (
- let searchIndex = this.searchUptoIndex;
- searchIndex < this.dataList.length;
- searchIndex += 1
- ) {
- if (this._match(this.dataList[searchIndex])) {
- this.findingList.push(this.dataList[searchIndex]);
- if (typeof this.limit === 'number' && this.findingList.length >= this.limit) break;
- }
-
- const calcFinishTime = window.performance.now();
- if (calcFinishTime - this.sessionStartTimestamp > 8) {
- const thisFindingCount = this.findingList.length;
- const thisSessionTimestamp = this.sessionStartTimestamp;
- if (lastFindingCount !== thisFindingCount) this._sendFindings();
-
- this.searchUptoIndex = searchIndex + 1;
- setTimeout(() => this._find(thisSessionTimestamp, thisFindingCount));
- return;
- }
- }
-
- if (lastFindingCount !== this.findingList.length
- || lastFindingCount === 0) this._sendFindings();
- this._softReset();
- }
-
- _match(item) {
- if (typeof item === 'string') {
- return this._compare(item);
- }
- if (typeof item === 'object') {
- if (Array.isArray(this.searchKeys)) {
- return !!this.searchKeys.find((key) => this._compare(item[key]));
- }
- if (typeof this.searchKeys === 'string') {
- return this._compare(item[this.searchKeys]);
- }
- }
- return false;
- }
-
- _compare(item) {
- if (typeof item !== 'string') return false;
- const myItem = this._normalize(item);
- if (this.isContain) return myItem.indexOf(this.term) !== -1;
- return myItem.startsWith(this.term);
- }
-
- _normalize(item) {
- let myItem = item.normalize(this.normalizeUnicode ? 'NFKC' : 'NFC');
- if (!this.isCaseSensitive) myItem = myItem.toLocaleLowerCase();
- if (this.ignoreWhitespace) myItem = myItem.replace(/\s/g, '');
- return myItem;
- }
-
- _sendFindings() {
- this.emit(this.RESULT_SENT, this.findingList, this.term);
- }
-}
-
-export default AsyncSearch;
diff --git a/src/util/common.js b/src/util/common.js
deleted file mode 100644
index 2affe27d..00000000
--- a/src/util/common.js
+++ /dev/null
@@ -1,230 +0,0 @@
-/* eslint-disable max-classes-per-file */
-export function bytesToSize(bytes) {
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
- if (bytes === 0) return 'n/a';
- const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
- if (i === 0) return `${bytes} ${sizes[i]}`;
- return `${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}`;
-}
-
-export function diffMinutes(dt2, dt1) {
- let diff = (dt2.getTime() - dt1.getTime()) / 1000;
- diff /= 60;
- return Math.abs(Math.round(diff));
-}
-
-export function isInSameDay(dt2, dt1) {
- return (
- dt2.getFullYear() === dt1.getFullYear()
- && dt2.getMonth() === dt1.getMonth()
- && dt2.getDate() === dt1.getDate()
- );
-}
-
-/**
- * @param {Event} ev
- * @param {string} [targetSelector] element selector for Element.matches([selector])
- */
-export function getEventCords(ev, targetSelector) {
- let boxInfo;
-
- const path = ev.nativeEvent.composedPath();
- const target = targetSelector
- ? path.find((element) => element.matches?.(targetSelector))
- : null;
- if (target) {
- boxInfo = target.getBoundingClientRect();
- } else {
- boxInfo = ev.target.getBoundingClientRect();
- }
-
- return {
- x: boxInfo.x,
- y: boxInfo.y,
- width: boxInfo.width,
- height: boxInfo.height,
- detail: ev.detail,
- };
-}
-
-export function abbreviateNumber(number) {
- if (number > 99) return '99+';
- return number;
-}
-
-export class Debounce {
- constructor() {
- this.timeoutId = null;
- }
-
- /**
- * @param {function} func - callback function
- * @param {number} wait - wait in milliseconds to call func
- * @returns {func} debounceCallback - to pass arguments to func callback
- */
- _(func, wait) {
- const that = this;
- return function debounceCallback(...args) {
- clearTimeout(that.timeoutId);
- that.timeoutId = setTimeout(() => {
- func.apply(this, args);
- that.timeoutId = null;
- }, wait);
- };
- }
-}
-
-export class Throttle {
- constructor() {
- this.timeoutId = null;
- }
-
- /**
- * @param {function} func - callback function
- * @param {number} wait - wait in milliseconds to call func
- * @returns {function} throttleCallback - to pass arguments to func callback
- */
- _(func, wait) {
- const that = this;
- return function throttleCallback(...args) {
- if (that.timeoutId !== null) return;
- that.timeoutId = setTimeout(() => {
- func.apply(this, args);
- that.timeoutId = null;
- }, wait);
- };
- }
-}
-
-export function getUrlPrams(paramName) {
- const queryString = window.location.search;
- const urlParams = new URLSearchParams(queryString);
- return urlParams.get(paramName);
-}
-
-export function getScrollInfo(target) {
- const scroll = {};
- scroll.top = Math.round(target.scrollTop);
- scroll.height = Math.round(target.scrollHeight);
- scroll.viewHeight = Math.round(target.offsetHeight);
- scroll.isScrollable = scroll.height > scroll.viewHeight;
- return scroll;
-}
-
-export function avatarInitials(text) {
- return [...text][0];
-}
-
-export function cssVar(name) {
- return getComputedStyle(document.body).getPropertyValue(name);
-}
-
-export function setFavicon(url) {
- const favicon = document.querySelector('#favicon');
- if (!favicon) return;
- favicon.setAttribute('href', url);
-}
-
-export function copyToClipboard(text) {
- if (navigator.clipboard) {
- navigator.clipboard.writeText(text);
- } else {
- const host = document.body;
- const copyInput = document.createElement('input');
- copyInput.style.position = 'fixed';
- copyInput.style.opacity = '0';
- copyInput.value = text;
- host.append(copyInput);
-
- copyInput.select();
- copyInput.setSelectionRange(0, 99999);
- document.execCommand('Copy');
- copyInput.remove();
- }
-}
-
-export function suffixRename(name, validator) {
- let suffix = 2;
- let newName = name;
- do {
- newName = name + suffix;
- suffix += 1;
- } while (validator(newName));
-
- return newName;
-}
-
-export function getImageDimension(file) {
- return new Promise((resolve) => {
- const img = new Image();
- img.onload = async () => {
- resolve({
- w: img.width,
- h: img.height,
- });
- URL.revokeObjectURL(img.src);
- };
- img.src = URL.createObjectURL(file);
- });
-}
-
-export function scaleDownImage(imageFile, width, height) {
- return new Promise((resolve) => {
- const imgURL = URL.createObjectURL(imageFile);
- const img = new Image();
-
- img.onload = () => {
- let newWidth = img.width;
- let newHeight = img.height;
- if (newHeight <= height && newWidth <= width) {
- resolve(imageFile);
- }
-
- if (newHeight > height) {
- newWidth = Math.floor(newWidth * (height / newHeight));
- newHeight = height;
- }
- if (newWidth > width) {
- newHeight = Math.floor(newHeight * (width / newWidth));
- newWidth = width;
- }
-
- const canvas = document.createElement('canvas');
- canvas.width = newWidth;
- canvas.height = newHeight;
- const ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0, newWidth, newHeight);
-
- canvas.toBlob((thumbnail) => {
- URL.revokeObjectURL(imgURL);
- resolve(thumbnail);
- }, imageFile.type);
- };
-
- img.src = imgURL;
- });
-}
-
-/**
- * @param {sigil} string sigil to search for (for example '@', '#' or '$')
- * @param {flags} string regex flags
- * @param {prefix} string prefix appended at the beginning of the regex
- * @returns {RegExp}
- */
-export function idRegex(sigil, flags, prefix) {
- const servername = '(?:[a-zA-Z0-9-.]*[a-zA-Z0-9]+|\\[\\S+?\\])(?::\\d+)?';
- return new RegExp(`${prefix}(${sigil}\\S+:${servername})`, flags);
-}
-
-const matrixToRegex = /^https?:\/\/matrix.to\/#\/(\S+:\S+)/;
-/**
- * Parses a matrix.to URL into an matrix id.
- * This function can later be extended to support matrix: URIs
- * @param {string} uri The URI to parse
- * @returns {string|null} The id or null if the URI does not match
- */
-export function parseIdUri(uri) {
- const res = decodeURIComponent(uri).match(matrixToRegex);
- if (!res) return null;
- return res[1];
-}
diff --git a/src/util/matrixUtil.js b/src/util/matrixUtil.js
deleted file mode 100644
index 19688a2b..00000000
--- a/src/util/matrixUtil.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import HashIC from '../../public/res/ic/outlined/hash.svg';
-import HashGlobeIC from '../../public/res/ic/outlined/hash-globe.svg';
-import HashLockIC from '../../public/res/ic/outlined/hash-lock.svg';
-import SpaceIC from '../../public/res/ic/outlined/space.svg';
-import SpaceGlobeIC from '../../public/res/ic/outlined/space-globe.svg';
-import SpaceLockIC from '../../public/res/ic/outlined/space-lock.svg';
-
-const WELL_KNOWN_URI = '/.well-known/matrix/client';
-
-export async function getBaseUrl(servername) {
- let protocol = 'https://';
- if (servername.match(/^https?:\/\//) !== null) protocol = '';
- const serverDiscoveryUrl = `${protocol}${servername}${WELL_KNOWN_URI}`;
- try {
- const result = await (await fetch(serverDiscoveryUrl, { method: 'GET' })).json();
-
- const baseUrl = result?.['m.homeserver']?.base_url;
- if (baseUrl === undefined) throw new Error();
- return baseUrl;
- } catch (e) {
- return `${protocol}${servername}`;
- }
-}
-
-export function getUsername(mx, userId) {
- const user = mx.getUser(userId);
- if (user === null) return userId;
- let username = user.displayName;
- if (typeof username === 'undefined') {
- username = userId;
- }
- return username;
-}
-
-export function getUsernameOfRoomMember(roomMember) {
- return roomMember.name || roomMember.userId;
-}
-
-export async function isRoomAliasAvailable(mx, alias) {
- try {
- const result = await mx.getRoomIdForAlias(alias);
- if (result.room_id) return false;
- return false;
- } catch (e) {
- if (e.errcode === 'M_NOT_FOUND') return true;
- return false;
- }
-}
-
-export function getPowerLabel(powerLevel) {
- if (powerLevel > 9000) return 'Goku';
- if (powerLevel > 100) return 'Founder';
- if (powerLevel === 100) return 'Admin';
- if (powerLevel >= 50) return 'Mod';
- return null;
-}
-
-export function parseReply(rawBody) {
- if (rawBody?.indexOf('>') !== 0) return null;
- let body = rawBody.slice(rawBody.indexOf('<') + 1);
- const user = body.slice(0, body.indexOf('>'));
-
- body = body.slice(body.indexOf('>') + 2);
- const replyBody = body.slice(0, body.indexOf('\n\n'));
- body = body.slice(body.indexOf('\n\n') + 2);
-
- if (user === '') return null;
-
- const isUserId = user.match(/^@.+:.+/);
-
- return {
- userId: isUserId ? user : null,
- displayName: isUserId ? null : user,
- replyBody,
- body,
- };
-}
-
-export function trimHTMLReply(html) {
- if (!html) return html;
- const suffix = '';
- const i = html.indexOf(suffix);
- if (i < 0) {
- return html;
- }
- return html.slice(i + suffix.length);
-}
-
-export function joinRuleToIconSrc(joinRule, isSpace) {
- return ({
- restricted: () => (isSpace ? SpaceIC : HashIC),
- knock: () => (isSpace ? SpaceLockIC : HashLockIC),
- invite: () => (isSpace ? SpaceLockIC : HashLockIC),
- public: () => (isSpace ? SpaceGlobeIC : HashGlobeIC),
- }[joinRule]?.() || null);
-}
-
-export function getIdServer(userId) {
- const idParts = userId.split(':');
- return idParts[1];
-}
-
-export async function hasDevices(mx, userId) {
- try {
- const usersDeviceMap = await mx.getCrypto()?.getUserDeviceInfo([userId, mx.getUserId()], true);
-
- return Array.from(usersDeviceMap.values())
- .every((deviceIdToDevices) => deviceIdToDevices.size > 0);
- } catch (e) {
- console.error("Error determining if it's possible to encrypt to all users: ", e);
- return false;
- }
-}
diff --git a/src/util/sort.js b/src/util/sort.js
deleted file mode 100644
index cc73cf57..00000000
--- a/src/util/sort.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export function memberByAtoZ(m1, m2) {
- const aName = m1.name;
- const bName = m2.name;
-
- if (aName.toLowerCase() < bName.toLowerCase()) {
- return -1;
- }
- if (aName.toLowerCase() > bName.toLowerCase()) {
- return 1;
- }
- return 0;
-}
-export function memberByPowerLevel(m1, m2) {
- const pl1 = m1.powerLevel;
- const pl2 = m2.powerLevel;
-
- if (pl1 > pl2) return -1;
- if (pl1 < pl2) return 1;
- return 0;
-}
From c571c93f61a627643db8d376dbc7d8072d41d0d4 Mon Sep 17 00:00:00 2001
From: Ajay Bura <32841439+ajbura@users.noreply.github.com>
Date: Sat, 30 Aug 2025 01:00:01 +0530
Subject: [PATCH 13/15] Fix long space name shrinks three dot menu button
(#2471)
---
src/app/pages/client/space/Space.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx
index fe1612fd..3f60d2a9 100644
--- a/src/app/pages/client/space/Space.tsx
+++ b/src/app/pages/client/space/Space.tsx
@@ -271,7 +271,7 @@ function SpaceHeader() {
{joinRules?.join_rule !== JoinRule.Public && }
-
+
From 7f40605bfe777daa6fd3b8381f6b442d43346d6d Mon Sep 17 00:00:00 2001
From: Krishan <33421343+kfiven@users.noreply.github.com>
Date: Sun, 31 Aug 2025 21:05:38 +1000
Subject: [PATCH 14/15] Release v4.10.0 (#2472)
* Release v4.10.0
* update version number in about
---
package-lock.json | 4 ++--
package.json | 2 +-
src/app/features/settings/about/About.tsx | 2 +-
src/app/pages/auth/AuthFooter.tsx | 2 +-
src/app/pages/client/WelcomePage.tsx | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index daf135d4..34a390f7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "cinny",
- "version": "4.9.1",
+ "version": "4.10.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cinny",
- "version": "4.9.1",
+ "version": "4.10.0",
"license": "AGPL-3.0-only",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "1.1.6",
diff --git a/package.json b/package.json
index 58b220b5..0c06993e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cinny",
- "version": "4.9.1",
+ "version": "4.10.0",
"description": "Yet another matrix client",
"main": "index.js",
"type": "module",
diff --git a/src/app/features/settings/about/About.tsx b/src/app/features/settings/about/About.tsx
index 00f5cc3d..4fba564e 100644
--- a/src/app/features/settings/about/About.tsx
+++ b/src/app/features/settings/about/About.tsx
@@ -46,7 +46,7 @@ export function About({ requestClose }: AboutProps) {
Cinny
- v4.9.1
+ v4.10.0
Yet another matrix client.
diff --git a/src/app/pages/auth/AuthFooter.tsx b/src/app/pages/auth/AuthFooter.tsx
index 53191f33..25c70e6e 100644
--- a/src/app/pages/auth/AuthFooter.tsx
+++ b/src/app/pages/auth/AuthFooter.tsx
@@ -15,7 +15,7 @@ export function AuthFooter() {
target="_blank"
rel="noreferrer"
>
- v4.9.1
+ v4.10.0
Twitter
diff --git a/src/app/pages/client/WelcomePage.tsx b/src/app/pages/client/WelcomePage.tsx
index 55456855..37ca10fa 100644
--- a/src/app/pages/client/WelcomePage.tsx
+++ b/src/app/pages/client/WelcomePage.tsx
@@ -24,7 +24,7 @@ export function WelcomePage() {
target="_blank"
rel="noreferrer noopener"
>
- v4.9.1
+ v4.10.0
}
From 7a562af35bf4057973b0e9ae9040632a6c0e8697 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 31 Aug 2025 11:06:43 +0000
Subject: [PATCH 15/15] Update dependency linkifyjs to v4.3.2 [SECURITY]
---
package-lock.json | 9 +++++----
package.json | 2 +-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 34a390f7..9c4b76a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -42,7 +42,7 @@
"is-hotkey": "0.2.0",
"jotai": "2.6.0",
"linkify-react": "4.1.3",
- "linkifyjs": "4.1.3",
+ "linkifyjs": "4.3.2",
"matrix-js-sdk": "37.5.0",
"millify": "6.1.0",
"pdfjs-dist": "4.2.67",
@@ -8506,9 +8506,10 @@
}
},
"node_modules/linkifyjs": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz",
- "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg=="
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz",
+ "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==",
+ "license": "MIT"
},
"node_modules/locate-path": {
"version": "6.0.0",
diff --git a/package.json b/package.json
index 0c06993e..ddb2c2d3 100644
--- a/package.json
+++ b/package.json
@@ -53,7 +53,7 @@
"is-hotkey": "0.2.0",
"jotai": "2.6.0",
"linkify-react": "4.1.3",
- "linkifyjs": "4.1.3",
+ "linkifyjs": "4.3.2",
"matrix-js-sdk": "37.5.0",
"millify": "6.1.0",
"pdfjs-dist": "4.2.67",