Fix mobile layout broken after update

Fix PC settings scroll
Settings use TypeScript
This commit is contained in:
OfficialDakari 2025-02-11 21:39:00 +05:00
parent 4213fd941c
commit 32124d9b86
21 changed files with 168 additions and 228 deletions

View file

@ -1,17 +1,16 @@
import React, { ComponentProps, MutableRefObject, PropsWithChildren, ReactNode, RefObject } from 'react';
import { AsProp, Box, Header, Line, Scroll, Text, as } from 'folds';
import React, { ComponentProps, MutableRefObject, PropsWithChildren, ReactNode } from 'react';
import { Box, Header, Line, Scroll, Text, as } from 'folds';
import classNames from 'classnames';
import { ContainerColor } from '../../styles/ContainerColor.css';
import * as css from './style.css';
import { ScreenSize, useScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { useTheme } from '@mui/material';
import { AnimatePresence, HTMLMotionProps, Variants, motion } from 'framer-motion';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import * as css from './style.css';
import { ScreenSize, useScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import BottomNav from '../../pages/client/BottomNav';
import { MotionBox } from '../../atoms/motion/Animated';
import { ContainerColor } from '../../styles/ContainerColor.css';
type PageRootProps = {
nav: ReactNode;
nav?: ReactNode;
children: ReactNode;
};
@ -29,6 +28,78 @@ export function PageRoot({ nav, children }: PageRootProps) {
);
}
const PageAnimationVariants: Variants = {
initial: {
translateX: '20px',
opacity: 0.3,
transition: {
ease: 'linear'
},
},
final: {
translateX: 0,
opacity: 1,
transition: {
ease: 'linear'
},
},
exit: {
translateX: '-20px',
opacity: 0.3,
transition: {
ease: 'linear'
},
}
};
export function AnimatedLayout(props: PropsWithChildren & HTMLMotionProps<'div'>) {
return (
<motion.div
initial='initial'
animate='final'
exit='exit'
variants={PageAnimationVariants}
style={{
display: 'flex',
flexDirection: 'column',
}}
layoutScroll
{...props}
/>
);
}
export function AnimatedNode(props: PropsWithChildren & HTMLMotionProps<'div'>) {
return (
<motion.div
style={{
display: 'flex',
flexDirection: 'column',
}}
{...props}
/>
);
}
export function MobileAnimatedLayout({ children, ...props }: PropsWithChildren & HTMLMotionProps<'div'>) {
const screenSize = useScreenSize();
if (screenSize === ScreenSize.Mobile) {
return (
<MotionBox
initial='initial'
animate='final'
exit='exit'
variants={PageAnimationVariants}
direction='Column'
{...props}
>
{children}
</MotionBox>
);
}
return children;
}
type ClientDrawerLayoutProps = {
children: ReactNode;
header?: ReactNode;
@ -94,77 +165,7 @@ export function PageNavContent({
);
}
const PageAnimationVariants: Variants = {
initial: {
translateX: '20px',
opacity: 0.3,
transition: {
ease: 'linear'
},
},
final: {
translateX: 0,
opacity: 1,
transition: {
ease: 'linear'
},
},
exit: {
translateX: '-20px',
opacity: 0.3,
transition: {
ease: 'linear'
},
}
};
export function AnimatedLayout(props: PropsWithChildren & HTMLMotionProps<'div'>) {
return (
<motion.div
initial='initial'
animate='final'
exit='exit'
variants={PageAnimationVariants}
style={{
display: 'flex',
flexDirection: 'column',
}}
layoutScroll
{...props}
/>
);
}
export function AnimatedNode(props: PropsWithChildren & HTMLMotionProps<'div'>) {
return (
<motion.div
style={{
display: 'flex',
flexDirection: 'column',
}}
{...props}
/>
);
}
export function MobileAnimatedLayout(props: PropsWithChildren & HTMLMotionProps<'div'>) {
const screenSize = useScreenSize();
if (screenSize === ScreenSize.Mobile) {
return (
<MotionBox
initial='initial'
animate='final'
exit='exit'
variants={PageAnimationVariants}
direction='Column'
{...props}
/>
);
}
return props.children;
}
export const Page = as<'div'>(({ className, children, ...props }, ref) => {
export const Page = as<'div'>(({ children, ...props }, ref) => {
const theme = useTheme();
return (
<Box

View file

@ -89,6 +89,9 @@
border-bottom: none;
}
}
max-height: 100%;
overflow-y: scroll;
}
.settings-notifications {

View file

@ -1,7 +1,11 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import './Settings.scss';
import '../profile-viewer/Banner.scss';
import { Switch, Button, ToggleButtonGroup, ToggleButton, DialogTitle, DialogContent, TextField, DialogActions, Dialog, AppBar, IconButton, ListItemButton, ListItemIcon, ListItemText, Toolbar, Typography, List, ListSubheader, Divider } from '@mui/material';
import { mdiBell, mdiCog, mdiEmoticon, mdiEye, mdiInformationSlabCircle, mdiLock, mdiStar } from '@mdi/js';
import Icon from '@mdi/react';
import { ArrowBack, Logout } from '@mui/icons-material';
import { SetPresence } from 'matrix-js-sdk';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
@ -15,7 +19,6 @@ import { usePermission } from '../../hooks/usePermission';
import Text from '../../atoms/text/Text';
import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import SegmentedControls from '../../atoms/segmented-controls/SegmentedControls';
import SettingTile from '../../molecules/setting-tile/SettingTile';
import ImportE2ERoomKeys from '../../molecules/import-export-e2e-room-keys/ImportE2ERoomKeys';
@ -25,45 +28,31 @@ import GlobalNotification from '../../molecules/global-notification/GlobalNotifi
import KeywordNotification from '../../molecules/global-notification/KeywordNotification';
import IgnoreUserList, { IgnorePolicyList } from '../../molecules/global-notification/IgnoreUserList';
import ProfileEditor from '../profile-editor/ProfileEditor';
import ProfileEditor from '../../organisms/profile-editor/ProfileEditor';
import CrossSigning from './CrossSigning';
import KeyBackup from './KeyBackup';
import DeviceManage from './DeviceManage';
import { Switch, Button, ToggleButtonGroup, ToggleButton, DialogTitle, DialogContent, DialogContentText, TextField, DialogActions, Dialog, AppBar, IconButton, Tab, Tabs, useTheme, ListItemButton, ListItemIcon, ListItemText, Toolbar, Typography, List, ListSubheader, Divider } from '@mui/material';
import CinnySVG from '../../../../public/res/svg/cinny.svg';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import { useSetting } from '../../state/hooks/settings';
import { SettingSetter, useSetting } from '../../state/hooks/settings';
import { settingsAtom } from '../../state/settings';
import { isMacOS } from '../../utils/user-agent';
import { KeySymbol } from '../../utils/key-symbol';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { Box, config, Header } from 'folds';
import Banner from '../profile-editor/Banner';
import { getText } from '../../../lang';
import { BackButtonHandler, useBackButton } from '../../hooks/useBackButton';
import { BackButtonHandler } from '../../hooks/useBackButton';
import { disablePush, enablePush } from '../../../push';
import { mdiAccount, mdiArrowLeft, mdiBell, mdiClose, mdiCog, mdiEmoticon, mdiEye, mdiInformationSlabCircle, mdiInformationSlabCircleOutline, mdiLock, mdiStar } from '@mdi/js';
import { authRequest } from './AuthRequest';
import Icon from '@mdi/react';
import FocusTrap from 'focus-trap-react';
import getCachedURL from '../../utils/cache';
import wallpaperDB from '../../utils/wallpaper';
import { useAccountData } from '../../hooks/useAccountData';
import ProminientToolbar from '../../components/prominient-toolbar/ProminientToolbar';
import { ArrowBack, Close, HideImage, Image, Logout } from '@mui/icons-material';
import { ScreenSize, useScreenSize } from '../../hooks/useScreenSize';
import { AnimatePresence } from 'framer-motion';
import { AnimatedLayout } from '../../components/page';
import useCordova from '../../hooks/cordova';
import Themes from './Themes';
import { PageRoot } from '../../components/page';
function AppearanceSection() {
const [, updateState] = useState({});
const mx = useMatrixClient();
const [enterForNewline, setEnterForNewline] = useSetting(settingsAtom, 'enterForNewline');
const [messageLayout, setMessageLayout] = useSetting(settingsAtom, 'messageLayout');
const [messageSpacing, setMessageSpacing] = useSetting(settingsAtom, 'messageSpacing');
@ -75,12 +64,12 @@ function AppearanceSection() {
const [urlPreview, setUrlPreview] = useSetting(settingsAtom, 'urlPreview');
const [encUrlPreview, setEncUrlPreview] = useSetting(settingsAtom, 'encUrlPreview');
const [showHiddenEvents, setShowHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
const [wallpaperURL, setWallpaperURL] = useState();
const [wallpaperURL, setWallpaperURL] = useState<string | null>(null);
const [newDesignInput, setNewDesignInput] = useSetting(settingsAtom, 'newDesignInput');
const [voiceMessages, setVoiceMessages] = useSetting(settingsAtom, 'voiceMessages');
const spacings = ['0', '100', '200', '300', '400', '500'];
const wallpaperInputRef = useRef(null);
const wallpaperInputRef = useRef<HTMLInputElement>(null);
const handleDeleteWallpaper = async () => {
if (
@ -88,27 +77,23 @@ function AppearanceSection() {
getText('settings.remove_wallpaper.title'),
getText('settings.remove_wallpaper.desc'),
getText('btn.remove_wallpaper'),
'danger'
'error'
)
) {
wallpaperDB.removeWallpaper();
}
};
/**
* @type {React.ChangeEventHandler<HTMLInputElement>}
* @returns
*/
async function uploadImage(e) {
const file = e.target.files.item(0);
if (file === null) return;
async function uploadImage(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.item(0);
if (!file) return;
try {
wallpaperDB.setWallpaper(file);
} catch (err) {
console.error(err);
alert('Failed to set wallpaper');
}
wallpaperInputRef.current.value = null;
if (wallpaperInputRef.current) wallpaperInputRef.current.value = '';
}
const handleSetWallpaper = async () => {
@ -116,8 +101,10 @@ function AppearanceSection() {
};
useEffect(() => {
wallpaperDB.getWallpaper().then(setWallpaperURL);
}, [wallpaperDB]);
wallpaperDB.getWallpaper().then((x) => {
setWallpaperURL(x);
});
}, []);
return (
<div className="settings-appearance">
@ -200,7 +187,7 @@ function AppearanceSection() {
<ToggleButtonGroup
exclusive
value={spacings.findIndex((s) => s === messageSpacing)}
onChange={(evt, value) => setMessageSpacing(spacings[value])}
onChange={(evt, value: number) => setMessageSpacing(spacings[value] as SettingSetter<'messageSpacing'>)}
>
<ToggleButton value={0}>No</ToggleButton>
<ToggleButton value={1}>XXS</ToggleButton>
@ -319,20 +306,23 @@ function AppearanceSection() {
function PresenceSection() {
const mx = useMatrixClient();
const [status, setStatus] = useSetting(settingsAtom, 'extera_status');
const statusMsg = mx.getUser(mx.getUserId()).presenceStatusMsg || '';
const statusMsgRef = useRef();
const statusMsg = mx.getUser(mx.getUserId()!)?.presenceStatusMsg || '';
const statusMsgRef = useRef<HTMLInputElement>();
const [ghostMode, setGhostMode] = useSetting(settingsAtom, 'extera_ghostMode');
const statuses = [
'online', 'offline', 'unavailable'
];
const updateStatusMessage = (evt) => {
const updateStatusMessage = (evt: React.FormEvent<HTMLFormElement>) => {
evt.preventDefault();
const { statusInput } = evt.currentTarget.elements;
if (!('statusInput' in evt.currentTarget.elements)) return;
const statusInput = evt.currentTarget.elements.statusInput as HTMLInputElement;
const value = statusInput.value.trim();
if (value === '') return;
const presence = statuses[status];
if (presence !== 'online' && presence !== 'offline' && presence !== 'unavailable') return;
mx.setPresence({
presence: statuses[status],
presence,
status_msg: value
});
};
@ -347,10 +337,10 @@ function PresenceSection() {
exclusive
value={status}
onChange={(evt, index) => {
mx.setSyncPresence(statuses[index]);
mx.setSyncPresence(statuses[index] as SetPresence);
mx.setPresence({
presence: statuses[index],
status_msg: index != 1 ? statusMsgRef.current?.value?.trim() : undefined
presence: statuses[index] as SetPresence,
status_msg: index !== 1 ? statusMsgRef.current?.value?.trim() : undefined
}).then(() => {
console.log('Presence updated');
}).catch(err => {
@ -404,12 +394,10 @@ function PresenceSection() {
}
function ExteraSection() {
const mx = useMatrixClient();
const [hideTgAds, setHideTgAds] = useSetting(settingsAtom, 'extera_hideTgAds');
const [enableCaptions, setEnableCaptions] = useSetting(settingsAtom, 'extera_enableCaptions');
const [renameTgBot, setRenameTgBot] = useSetting(settingsAtom, 'extera_renameTgBot');
const [smoothScroll, setSmoothScroll] = useSetting(settingsAtom, 'extera_smoothScroll');
const [ignorePolicies, setIgnorePolicies] = useSetting(settingsAtom, 'ignorePolicies');
const [replyFallbacks, setReplyFallbacks] = useSetting(settingsAtom, 'replyFallbacks');
return (
@ -500,19 +488,19 @@ function NotificationsSection() {
const requestPermissions = () => {
if (typeof window.Notification !== 'undefined') window.Notification?.requestPermission().then(setPermission);
else if (window.cordova?.plugins?.notification?.local) {
cordova.plugins.notification.local.requestPermission((granted) => {
else if (cordova?.plugins?.notification?.local) {
cordova.plugins.notification.local.requestPermission((granted: boolean) => {
setPermission(granted);
});
}
};
const renderOptions = () => {
if (window.Notification === undefined && !window.cordova?.plugins?.notification?.local) {
if (window.Notification === undefined && !cordova?.plugins?.notification?.local) {
return <Text className="settings-notifications__not-supported">{getText('settings.notifications.unsupported')}</Text>;
}
window.cordova?.plugins?.notification?.local?.hasPermission(setPermission);
cordova?.plugins?.notification?.local?.hasPermission(setPermission);
if (permission) {
return (
@ -540,11 +528,9 @@ function NotificationsSection() {
const togglePushes = () => {
if (!pushes) {
enablePush();
console.log('enabled push');
setPushes(true);
} else {
disablePush();
console.log('disabled push');
setPushes(false);
}
};
@ -618,12 +604,14 @@ function SecuritySection() {
const [open, setOpen] = useState(false);
const [disableBtn, setDisableBtn] = useState(false);
const changePassword = useCallback(async (evt) => {
const changePassword = useCallback((evt: React.FormEvent<HTMLFormElement>) => {
if (!('passwordInput' in evt.target)) return;
const passwordInput = evt.target.passwordInput as HTMLInputElement;
evt.preventDefault();
setOpen(false);
setDisableBtn(true);
await authRequest(getText('change_password.old'), async (auth) => {
await mx.setPassword(auth, evt.target.passwordInput.value, false);
authRequest(getText('change_password.old'), async (auth) => {
await mx.setPassword(auth, passwordInput.value, false);
setDisableBtn(false);
});
}, [mx]);
@ -663,7 +651,7 @@ function SecuritySection() {
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color='primary'>{getText('btn.cancel')}</Button>
<Button type='submit' color='error'>{getText('change_password.btn')}</Button>
<Button type='submit' color='error' disabled={disableBtn}>{getText('change_password.btn')}</Button>
</DialogActions>
</Dialog>
<div className="settings-security">
@ -677,7 +665,7 @@ function SecuritySection() {
<SettingTile
title={getText('settings.change_password.title')}
options={(
<Button onClick={openChangePassword} variant='contained' color='error'>{getText('change_password.btn')}</Button>
<Button onClick={openChangePassword} variant='contained' color='error' disabled={disableBtn}>{getText('change_password.btn')}</Button>
)}
/>
</div>
@ -708,42 +696,6 @@ function SecuritySection() {
);
}
function ProfileSection() {
return (
<div className='settings-profile'>
<div className='settings-profile__card'>
<ProfileEditor userId={initMatrix.matrixClient.getUserId()} />
{/* <Box grow='Yes'>
</Box>
<IconButton
size='large'
edge='end'
onClick={() => uploadImageRef.current?.click()}
>
<Image />
</IconButton>
<IconButton
size='large'
edge='end'
color='error'
onClick={handleBannerRemove}
>
<HideImage />
</IconButton>
<IconButton
size='large'
edge='end'
color='error'
onClick={handleLogout}
>
<Logout />
</IconButton> */}
</div>
</div>
);
}
function AboutSection() {
return (
<div className="settings-about">
@ -839,11 +791,11 @@ const tabItems = [{
render: () => <AboutSection />,
}];
function useWindowToggle(setSelectedTab) {
function useWindowToggle(setSelectedTab: (a: any) => void): [boolean, () => void] {
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
const openSettings = (tab) => {
const openSettings = (tab: any) => {
const tabItem = tabItems.find((item) => item.text === tab);
if (tabItem) setSelectedTab(tabItem);
setIsOpen(true);
@ -852,7 +804,7 @@ function useWindowToggle(setSelectedTab) {
return () => {
navigation.removeListener(cons.events.navigation.SETTINGS_OPENED, openSettings);
};
}, []);
}, [setSelectedTab]);
const requestClose = () => setIsOpen(false);
@ -860,11 +812,11 @@ function useWindowToggle(setSelectedTab) {
}
function Settings() {
const mx = useMatrixClient();
const [selectedTab, setSelectedTab] = useState(-1);
const [isOpen, requestClose] = useWindowToggle(setSelectedTab);
const screenSize = useScreenSize();
const mx = useMatrixClient();
const handleLogout = async () => {
if (await confirmDialog(getText('logout.title'), getText('logout.confirm'), getText('btn.logout.confirm'), 'error')) {
initMatrix.logout();
@ -878,7 +830,7 @@ function Settings() {
const renderSidebar = () => (
<>
<ProfileEditor userId={initMatrix.matrixClient.getUserId()} />
<ProfileEditor userId={mx.getUserId()} />
<Divider />
<List>
<ListSubheader sx={{ bgcolor: 'transparent' }} disableSticky>{getText('settings.header')}</ListSubheader>
@ -912,10 +864,13 @@ function Settings() {
<Dialog
open={isOpen}
onClose={requestClose}
fullScreen={screenSize !== ScreenSize.Desktop}
scroll='body'
fullWidth
maxWidth='xl'
fullScreen
PaperProps={{
style: {
maxHeight: '100%',
margin: 0
}
}}
>
{isOpen && <BackButtonHandler callback={handleBack} id='settings' />}
{isOpen && (
@ -936,12 +891,15 @@ function Settings() {
</AppBar>
)}
{isOpen && (
<div className="settings-window__content">
{(screenSize !== ScreenSize.Mobile) && (
<div className='settings-window__sidebar'>
{renderSidebar()}
</div>
)}
<PageRoot
nav={
(screenSize !== ScreenSize.Mobile) && (
<div className='settings-window__sidebar'>
{renderSidebar()}
</div>
)
}
>
{(screenSize === ScreenSize.Mobile && selectedTab === -1) && (
<div className='settings-window__sidebar-mobile'>
{renderSidebar()}
@ -950,20 +908,14 @@ function Settings() {
{screenSize !== ScreenSize.Mobile && <Divider sx={{ height: 'auto' }} orientation='vertical' />}
{selectedTab !== -1 && (
<div className='settings-window__cards-wrapper'>
{isOpen && <BackButtonHandler callback={handleBack} id='settings' />}
{tabItems[selectedTab].render()}
</div>
)}
</div>
</PageRoot>
)}
</Dialog>
);
}
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
export default Settings;

View file

@ -1,6 +1,8 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './RoomEncryption.scss';
import { Switch } from '@mui/material';
import { EventTimeline } from 'matrix-js-sdk';
import initMatrix from '../../../client/initMatrix';
@ -9,14 +11,14 @@ import SettingTile from '../setting-tile/SettingTile';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import { getText } from '../../../lang';
import { Switch } from '@mui/material';
import { getStateEvents } from '../../utils/room';
function RoomEncryption({ roomId }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const encryptionEvents = room.currentState.getStateEvents('m.room.encryption');
const encryptionEvents = getStateEvents(room, 'm.room.encryption');
const [isEncrypted, setIsEncrypted] = useState(encryptionEvents.length > 0);
const canEnableEncryption = room.currentState.maySendStateEvent('m.room.encryption', mx.getUserId());
const canEnableEncryption = room.getLiveTimeline().getState(EventTimeline.FORWARDS).maySendStateEvent('m.room.encryption', mx.getUserId());
const handleEncryptionEnable = async () => {
const joinRule = room.getJoinRule();
@ -31,7 +33,7 @@ function RoomEncryption({ roomId }) {
if (await confirmDialog(title, confirmMsg2, getText('btn.dialog.room_encryption.2'), 'caution')) {
setIsEncrypted(true);
mx.sendStateEvent(roomId, 'm.room.encryption', {
algorithm: 'm.megolm.v1.aes-sha2',
algorithm: 'm.megolm.v1.aes-sha2'
});
}
};

View file

@ -16,7 +16,7 @@ import Text from '../../atoms/text/Text';
// import Dialog from '../../molecules/dialog/Dialog';
import { useStore } from '../../hooks/useStore';
import { accessSecretStorage } from '../settings/SecretStorageAccess';
import { accessSecretStorage } from '../../features/settings/SecretStorageAccess';
import { getText } from '../../../lang';
import { mdiClose } from '@mdi/js';
import { AppBar, Button, CircularProgress, Dialog, IconButton, LinearProgress, Toolbar, Typography } from '@mui/material';

View file

@ -4,7 +4,7 @@ import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import InviteUser from '../invite-user/InviteUser';
import Settings from '../settings/Settings';
import Settings from '../../features/settings/Settings';
import SpaceSettings from '../space-settings/SpaceSettings';
import RoomSettings from '../room/RoomSettings';

View file

@ -1,62 +1,46 @@
import React, { MouseEventHandler, forwardRef, useMemo, useRef, useState } from 'react';
import { useAtom, useAtomValue } from 'jotai';
import { useAtomValue } from 'jotai';
import Icon from '@mdi/react';
import { mdiAt } from '@mdi/js';
import { DoneAll, MenuOpen, MoreVert, PersonAdd, Menu as MenuIcon } from '@mui/icons-material';
import {
AppBar,
Avatar,
Box,
Button,
IconButton,
ListItem,
ListItemIcon,
ListItemText,
Menu,
MenuItem,
MenuList,
Paper,
Popper,
Toolbar,
Typography,
} from '@mui/material';
import { useVirtualizer } from '@tanstack/react-virtual';
import FocusTrap from 'focus-trap-react';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { factoryRoomIdByActivity, factoryRoomIdByUnreadCount } from '../../../utils/sort';
import { factoryRoomIdByActivity } from '../../../utils/sort';
import {
NavButton,
NavCategory,
NavCategoryHeader,
NavEmptyCenter,
NavEmptyLayout,
NavItem,
NavItemContent,
} from '../../../components/nav';
import { getDirectRoomPath } from '../../pathUtils';
import { getCanonicalAliasOrRoomId, getRoomTags } from '../../../utils/matrix';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { VirtualTile } from '../../../components/virtualizer';
import { RoomNavCategoryButton, RoomNavItem } from '../../../features/room-nav';
import { RoomNavItem } from '../../../features/room-nav';
import { muteChangesAtom } from '../../../state/room-list/mutedRoomList';
import { makeNavCategoryId } from '../../../state/closedNavCategories';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
import { useDirectRooms } from './useDirectRooms';
import { openInviteUser } from '../../../../client/action/navigation';
import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page';
import { useClosedNavCategoriesAtom } from '../../../state/hooks/closedNavCategories';
import { PageNav, PageNavContent } from '../../../components/page';
import { useRoomsUnread } from '../../../state/hooks/unread';
import { markAsRead } from '../../../../client/action/notifications';
import { getText } from '../../../../lang';
import { isHidden } from '../../../state/hooks/roomList';
import Icon from '@mdi/react';
import { mdiAt } from '@mdi/js';
import { ScreenSize, useScreenSize } from '../../../hooks/useScreenSize';
import { Add, DoneAll, MenuOpen, MoreVert, PersonAdd, Menu as MenuIcon } from '@mui/icons-material';
import FAB from '../../../components/fab/FAB';
import { useNavHidden } from '../../../hooks/useHideableNav';
import SearchBar from '../SearchBar';
import SyncStateAlert from '../SyncStateAlert';
import BottomNav from '../BottomNav';
type DirectMenuProps = {
requestClose: () => void;
@ -167,11 +151,11 @@ export function Direct() {
const directs = useDirectRooms();
const muteChanges = useAtomValue(muteChangesAtom);
const mutedRooms = muteChanges.added;
const roomToUnread = useAtomValue(roomToUnreadAtom);
const screenSize = useScreenSize();
const selectedRoomId = useSelectedRoom();
const noRoomToDisplay = directs.length === 0;
// eslint-disable-next-line no-restricted-globals
const prev = history.state?.usr?.prev || '';
const sortedDirects = useMemo(() => {
@ -179,7 +163,7 @@ export function Direct() {
factoryRoomIdByActivity(mx)
);
return items;
}, [mx, directs, roomToUnread, selectedRoomId]);
}, [mx, directs]);
const virtualizer = useVirtualizer({
count: sortedDirects.length,
@ -193,7 +177,7 @@ export function Direct() {
header={<DirectHeader />}
variants={{
exit: {
translateX: prev == 'rooms' ? '20px' : '-20px',
translateX: prev === 'rooms' ? '20px' : '-20px',
opacity: 0.3,
transition: {
ease: 'linear'

View file

@ -5,7 +5,6 @@ import { logger } from 'matrix-js-sdk/lib/logger';
import { getSecret } from './state/auth';
import { cryptoCallbacks } from './state/secretStorageKeys';
import { SlidingSync } from 'matrix-js-sdk/lib/sliding-sync';
import indexedDBFactory from './workers/IndexedDBFactory';
global.Olm = Olm;
@ -65,7 +64,7 @@ class InitMatrix extends EventEmitter {
await indexedDBStore.startup();
await this.matrixClient.initCrypto();
await this.matrixClient.initRustCrypto();
if (spec.unstable_features['org.matrix.simplified_msc3575']) {
await this.matrixClient.startClient({

View file

@ -6,9 +6,8 @@ import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill';
import inject from '@rollup/plugin-inject';
import topLevelAwait from 'vite-plugin-top-level-await';
import buildConfig from './build.config';
import { readFileSync } from 'fs';
import { VitePWA } from 'vite-plugin-pwa';
import buildConfig from './build.config';
const copyFiles = {
targets: [