mirror of
https://github.com/officialdakari/Extera.git
synced 2025-04-11 23:08:46 +02:00
Fix mobile layout broken after update
Fix PC settings scroll Settings use TypeScript
This commit is contained in:
parent
4213fd941c
commit
32124d9b86
21 changed files with 168 additions and 228 deletions
|
@ -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
|
||||
|
|
|
@ -89,6 +89,9 @@
|
|||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
max-height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.settings-notifications {
|
|
@ -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;
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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: [
|
||||
|
|
Loading…
Add table
Reference in a new issue