initial changes
This commit is contained in:
parent
2a1bf4a42a
commit
a2b8b127f4
19 changed files with 2177 additions and 3102 deletions
4724
package-lock.json
generated
4724
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -52,7 +52,6 @@
|
|||
"linkifyjs": "4.0.2",
|
||||
"matrix-js-sdk": "24.1.0",
|
||||
"millify": "6.1.0",
|
||||
"pdfjs-dist": "3.10.111",
|
||||
"prismjs": "1.29.0",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "17.0.2",
|
||||
|
@ -63,7 +62,6 @@
|
|||
"react-dnd-html5-backend": "15.1.3",
|
||||
"react-dom": "17.0.2",
|
||||
"react-error-boundary": "4.0.10",
|
||||
"react-google-recaptcha": "2.1.0",
|
||||
"react-modal": "3.16.1",
|
||||
"react-range": "1.8.14",
|
||||
"sanitize-html": "2.8.0",
|
||||
|
@ -71,8 +69,7 @@
|
|||
"slate-history": "0.93.0",
|
||||
"slate-react": "0.98.4",
|
||||
"tippy.js": "6.3.7",
|
||||
"twemoji": "14.0.2",
|
||||
"ua-parser-js": "1.0.35"
|
||||
"twemoji": "14.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
|
||||
|
@ -103,4 +100,4 @@
|
|||
"vite": "4.3.9",
|
||||
"vite-plugin-static-copy": "0.13.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
display: inline-flex;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: var(--bo-radius);
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
|
||||
&__large {
|
||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import './ContextMenu.scss';
|
||||
|
||||
import Tippy from '@tippyjs/react';
|
||||
import 'tippy.js/animations/scale-extreme.css';
|
||||
import 'tippy.js/animations/scale-subtle.css';
|
||||
|
||||
import Text from '../text/Text';
|
||||
import Button from '../button/Button';
|
||||
|
@ -22,7 +22,7 @@ function ContextMenu({
|
|||
|
||||
return (
|
||||
<Tippy
|
||||
animation="scale-extreme"
|
||||
animation="scale-subtle"
|
||||
className="context-menu"
|
||||
visible={isVisible}
|
||||
onClickOutside={hideMenu}
|
||||
|
@ -31,7 +31,7 @@ function ContextMenu({
|
|||
interactive
|
||||
arrow={false}
|
||||
maxWidth={maxWidth}
|
||||
duration={200}
|
||||
duration={125}
|
||||
>
|
||||
{render(isVisible ? hideMenu : showMenu)}
|
||||
</Tippy>
|
||||
|
|
|
@ -39,20 +39,23 @@
|
|||
}
|
||||
|
||||
.ReactModal__Overlay {
|
||||
animation: raw-modal--overlay 150ms;
|
||||
animation: raw-modal--overlay 190ms;
|
||||
animation-timing-function: cubic-bezier(0.77,0,0.18,1);
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.ReactModal__Content {
|
||||
animation: raw-modal--content 150ms;
|
||||
animation: raw-modal--content 190ms;
|
||||
animation-timing-function: cubic-bezier(0.77,0,0.18,1);
|
||||
}
|
||||
|
||||
@keyframes raw-modal--content {
|
||||
0% {
|
||||
transform: translateY(100px);
|
||||
opacity: .5;
|
||||
transform: scale(0.90);
|
||||
opacity: .4;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,13 @@ import { isInSameDay } from '../../../util/common';
|
|||
function Time({ timestamp, fullTime }) {
|
||||
const date = new Date(timestamp);
|
||||
|
||||
const formattedFullTime = dateFormat(date, 'dd mmmm yyyy, hh:MM TT');
|
||||
let formattedDate = formattedFullTime;
|
||||
let formattedFullTime;
|
||||
let formattedDate;
|
||||
|
||||
if (!fullTime) {
|
||||
if (fullTime) {
|
||||
formattedFullTime = dateFormat(date, 'dd mmmm yyyy, hh:MM TT');
|
||||
formattedDate = formattedFullTime;
|
||||
} else {
|
||||
const compareDate = new Date();
|
||||
const isToday = isInSameDay(date, compareDate);
|
||||
compareDate.setDate(compareDate.getDate() - 1);
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import { style } from '@vanilla-extract/css';
|
||||
import { DefaultReset, color, config } from 'folds';
|
||||
|
||||
export const PdfViewer = style([
|
||||
DefaultReset,
|
||||
{
|
||||
height: '100%',
|
||||
},
|
||||
]);
|
||||
|
||||
export const PdfViewerHeader = style([
|
||||
DefaultReset,
|
||||
{
|
||||
paddingLeft: config.space.S200,
|
||||
paddingRight: config.space.S200,
|
||||
borderBottomWidth: config.borderWidth.B300,
|
||||
flexShrink: 0,
|
||||
gap: config.space.S200,
|
||||
},
|
||||
]);
|
||||
export const PdfViewerFooter = style([
|
||||
PdfViewerHeader,
|
||||
{
|
||||
borderTopWidth: config.borderWidth.B300,
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
]);
|
||||
|
||||
export const PdfViewerContent = style([
|
||||
DefaultReset,
|
||||
{
|
||||
margin: 'auto',
|
||||
display: 'inline-block',
|
||||
backgroundColor: color.Surface.Container,
|
||||
color: color.Surface.OnContainer,
|
||||
},
|
||||
]);
|
|
@ -1,257 +0,0 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
||||
import React, { FormEventHandler, useEffect, useRef, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
Header,
|
||||
Icon,
|
||||
IconButton,
|
||||
Icons,
|
||||
Input,
|
||||
Menu,
|
||||
PopOut,
|
||||
Scroll,
|
||||
Spinner,
|
||||
Text,
|
||||
as,
|
||||
config,
|
||||
} from 'folds';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import FileSaver from 'file-saver';
|
||||
import * as css from './PdfViewer.css';
|
||||
import { AsyncStatus } from '../../hooks/useAsyncCallback';
|
||||
import { useZoom } from '../../hooks/useZoom';
|
||||
import { createPage, usePdfDocumentLoader, usePdfJSLoader } from '../../plugins/pdfjs-dist';
|
||||
|
||||
export type PdfViewerProps = {
|
||||
name: string;
|
||||
src: string;
|
||||
requestClose: () => void;
|
||||
};
|
||||
|
||||
export const PdfViewer = as<'div', PdfViewerProps>(
|
||||
({ className, name, src, requestClose, ...props }, ref) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const { zoom, zoomIn, zoomOut, setZoom } = useZoom(0.2);
|
||||
|
||||
const [pdfJSState, loadPdfJS] = usePdfJSLoader();
|
||||
const [docState, loadPdfDocument] = usePdfDocumentLoader(
|
||||
pdfJSState.status === AsyncStatus.Success ? pdfJSState.data : undefined,
|
||||
src
|
||||
);
|
||||
const isLoading =
|
||||
pdfJSState.status === AsyncStatus.Loading || docState.status === AsyncStatus.Loading;
|
||||
const isError =
|
||||
pdfJSState.status === AsyncStatus.Error || docState.status === AsyncStatus.Error;
|
||||
const [pageNo, setPageNo] = useState(1);
|
||||
const [openJump, setOpenJump] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadPdfJS();
|
||||
}, [loadPdfJS]);
|
||||
useEffect(() => {
|
||||
if (pdfJSState.status === AsyncStatus.Success) {
|
||||
loadPdfDocument();
|
||||
}
|
||||
}, [pdfJSState, loadPdfDocument]);
|
||||
|
||||
useEffect(() => {
|
||||
if (docState.status === AsyncStatus.Success) {
|
||||
const doc = docState.data;
|
||||
if (pageNo < 0 || pageNo > doc.numPages) return;
|
||||
createPage(doc, pageNo, { scale: zoom }).then((canvas) => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
container.textContent = '';
|
||||
container.append(canvas);
|
||||
scrollRef.current?.scrollTo({
|
||||
top: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
}, [docState, pageNo, zoom]);
|
||||
|
||||
const handleDownload = () => {
|
||||
FileSaver.saveAs(src, name);
|
||||
};
|
||||
|
||||
const handleJumpSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
|
||||
evt.preventDefault();
|
||||
if (docState.status !== AsyncStatus.Success) return;
|
||||
const jumpInput = evt.currentTarget.jumpInput as HTMLInputElement;
|
||||
if (!jumpInput) return;
|
||||
const jumpTo = parseInt(jumpInput.value, 10);
|
||||
setPageNo(Math.max(1, Math.min(docState.data.numPages, jumpTo)));
|
||||
setOpenJump(false);
|
||||
};
|
||||
|
||||
const handlePrevPage = () => {
|
||||
setPageNo((n) => Math.max(n - 1, 1));
|
||||
};
|
||||
|
||||
const handleNextPage = () => {
|
||||
if (docState.status !== AsyncStatus.Success) return;
|
||||
setPageNo((n) => Math.min(n + 1, docState.data.numPages));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className={classNames(css.PdfViewer, className)} direction="Column" {...props} ref={ref}>
|
||||
<Header className={css.PdfViewerHeader} size="400">
|
||||
<Box grow="Yes" alignItems="Center" gap="200">
|
||||
<IconButton size="300" radii="300" onClick={requestClose}>
|
||||
<Icon size="50" src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
<Text size="T300" truncate>
|
||||
{name}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box shrink="No" alignItems="Center" gap="200">
|
||||
<IconButton
|
||||
variant={zoom < 1 ? 'Success' : 'SurfaceVariant'}
|
||||
outlined={zoom < 1}
|
||||
size="300"
|
||||
radii="Pill"
|
||||
onClick={zoomOut}
|
||||
aria-label="Zoom Out"
|
||||
>
|
||||
<Icon size="50" src={Icons.Minus} />
|
||||
</IconButton>
|
||||
<Chip variant="SurfaceVariant" radii="Pill" onClick={() => setZoom(zoom === 1 ? 2 : 1)}>
|
||||
<Text size="B300">{Math.round(zoom * 100)}%</Text>
|
||||
</Chip>
|
||||
<IconButton
|
||||
variant={zoom > 1 ? 'Success' : 'SurfaceVariant'}
|
||||
outlined={zoom > 1}
|
||||
size="300"
|
||||
radii="Pill"
|
||||
onClick={zoomIn}
|
||||
aria-label="Zoom In"
|
||||
>
|
||||
<Icon size="50" src={Icons.Plus} />
|
||||
</IconButton>
|
||||
<Chip
|
||||
variant="Primary"
|
||||
onClick={handleDownload}
|
||||
radii="300"
|
||||
before={<Icon size="50" src={Icons.Download} />}
|
||||
>
|
||||
<Text size="B300">Download</Text>
|
||||
</Chip>
|
||||
</Box>
|
||||
</Header>
|
||||
<Box direction="Column" grow="Yes" alignItems="Center" justifyContent="Center" gap="200">
|
||||
{isLoading && <Spinner variant="Secondary" size="600" />}
|
||||
{isError && (
|
||||
<>
|
||||
<Text>Failed to load PDF</Text>
|
||||
<Button
|
||||
variant="Critical"
|
||||
fill="Soft"
|
||||
size="300"
|
||||
radii="300"
|
||||
before={<Icon src={Icons.Warning} size="50" />}
|
||||
onClick={loadPdfJS}
|
||||
>
|
||||
<Text size="B300">Retry</Text>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{docState.status === AsyncStatus.Success && (
|
||||
<Scroll
|
||||
ref={scrollRef}
|
||||
size="300"
|
||||
direction="Both"
|
||||
variant="Surface"
|
||||
visibility="Hover"
|
||||
>
|
||||
<Box>
|
||||
<div className={css.PdfViewerContent} ref={containerRef} />
|
||||
</Box>
|
||||
</Scroll>
|
||||
)}
|
||||
</Box>
|
||||
{docState.status === AsyncStatus.Success && (
|
||||
<Header as="footer" className={css.PdfViewerFooter} size="400">
|
||||
<Chip
|
||||
variant="Secondary"
|
||||
radii="300"
|
||||
before={<Icon size="50" src={Icons.ChevronLeft} />}
|
||||
onClick={handlePrevPage}
|
||||
aria-disabled={pageNo <= 1}
|
||||
>
|
||||
<Text size="B300">Previous</Text>
|
||||
</Chip>
|
||||
<Box grow="Yes" justifyContent="Center" alignItems="Center" gap="200">
|
||||
<PopOut
|
||||
open={openJump}
|
||||
align="Center"
|
||||
position="Top"
|
||||
content={
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
initialFocus: false,
|
||||
onDeactivate: () => setOpenJump(false),
|
||||
clickOutsideDeactivates: true,
|
||||
}}
|
||||
>
|
||||
<Menu variant="Surface">
|
||||
<Box
|
||||
as="form"
|
||||
onSubmit={handleJumpSubmit}
|
||||
style={{ padding: config.space.S200 }}
|
||||
direction="Column"
|
||||
gap="200"
|
||||
>
|
||||
<Input
|
||||
name="jumpInput"
|
||||
size="300"
|
||||
variant="Background"
|
||||
defaultValue={pageNo}
|
||||
min={1}
|
||||
max={docState.data.numPages}
|
||||
step={1}
|
||||
outlined
|
||||
type="number"
|
||||
radii="300"
|
||||
aria-label="Page Number"
|
||||
/>
|
||||
<Button type="submit" size="300" variant="Primary" radii="300">
|
||||
<Text size="B300">Jump To Page</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
</Menu>
|
||||
</FocusTrap>
|
||||
}
|
||||
>
|
||||
{(anchorRef) => (
|
||||
<Chip
|
||||
onClick={() => setOpenJump(!openJump)}
|
||||
ref={anchorRef}
|
||||
variant="SurfaceVariant"
|
||||
radii="300"
|
||||
aria-pressed={openJump}
|
||||
>
|
||||
<Text size="B300">{`${pageNo}/${docState.data.numPages}`}</Text>
|
||||
</Chip>
|
||||
)}
|
||||
</PopOut>
|
||||
</Box>
|
||||
<Chip
|
||||
variant="Primary"
|
||||
radii="300"
|
||||
after={<Icon size="50" src={Icons.ChevronRight} />}
|
||||
onClick={handleNextPage}
|
||||
aria-disabled={pageNo >= docState.data.numPages}
|
||||
>
|
||||
<Text size="B300">Next</Text>
|
||||
</Chip>
|
||||
</Header>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1 +0,0 @@
|
|||
export * from './PdfViewer';
|
|
@ -30,7 +30,6 @@ import {
|
|||
import * as css from './Editor.css';
|
||||
import { BlockType, MarkType } from './types';
|
||||
import { HeadingLevel } from './slate';
|
||||
import { isMacOS } from '../../utils/user-agent';
|
||||
import { KeySymbol } from '../../utils/key-symbol';
|
||||
import { useSetting } from '../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../state/settings';
|
||||
|
@ -121,7 +120,7 @@ export function HeadingBlockButton() {
|
|||
const level = headingLevel(editor);
|
||||
const [open, setOpen] = useState(false);
|
||||
const isActive = isBlockActive(editor, BlockType.Heading);
|
||||
const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl';
|
||||
const modKey = 'Ctrl';
|
||||
|
||||
const handleMenuSelect = (selectedLevel: HeadingLevel) => {
|
||||
setOpen(false);
|
||||
|
@ -247,7 +246,7 @@ export function ExitFormatting({ tooltip }: ExitFormattingProps) {
|
|||
|
||||
export function Toolbar() {
|
||||
const editor = useSlate();
|
||||
const modKey = isMacOS() ? KeySymbol.Command : 'Ctrl';
|
||||
const modKey = 'Ctrl';
|
||||
const disableInline = isBlockActive(editor, BlockType.CodeBlock);
|
||||
|
||||
const canEscape = isAnyMarkActive(editor) || !isBlockActive(editor, BlockType.Paragraph);
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
@use '../../partials/text';
|
||||
|
||||
.people-selector {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
padding: var(--sp-extra-tight) var(--sp-normal);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
||||
&__container {
|
||||
margin: var(--sp-extra-tight);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
}
|
||||
|
||||
& .room-selector {
|
||||
width: calc(100% - var(--sp-extra-tight));
|
||||
@include dir.side(margin, auto, 0);
|
||||
margin: var(--sp-ultra-tight);
|
||||
}
|
||||
}
|
|
@ -29,7 +29,6 @@ import {
|
|||
getFileNameExt,
|
||||
mimeTypeToExt,
|
||||
} from '../../../utils/mimeTypes';
|
||||
import { PdfViewer } from '../../../components/Pdf-viewer';
|
||||
import * as css from './styles.css';
|
||||
|
||||
export type FileContentProps = {
|
||||
|
@ -149,72 +148,6 @@ function ReadTextFile({ body, mimeType, url, encInfo }: Omit<FileContentProps, '
|
|||
);
|
||||
}
|
||||
|
||||
function ReadPdfFile({ body, mimeType, url, encInfo }: Omit<FileContentProps, 'info'>) {
|
||||
const mx = useMatrixClient();
|
||||
const [pdfViewer, setPdfViewer] = useState(false);
|
||||
|
||||
const [pdfState, loadPdf] = useAsyncCallback(
|
||||
useCallback(async () => {
|
||||
const httpUrl = await getFileSrcUrl(mx.mxcUrlToHttp(url) ?? '', mimeType, encInfo);
|
||||
setPdfViewer(true);
|
||||
return httpUrl;
|
||||
}, [mx, url, mimeType, encInfo])
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{pdfState.status === AsyncStatus.Success && (
|
||||
<Overlay open={pdfViewer} backdrop={<OverlayBackdrop />}>
|
||||
<OverlayCenter>
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
initialFocus: false,
|
||||
onDeactivate: () => setPdfViewer(false),
|
||||
clickOutsideDeactivates: true,
|
||||
}}
|
||||
>
|
||||
<Modal
|
||||
className={css.ModalWide}
|
||||
size="500"
|
||||
onContextMenu={(evt: any) => evt.stopPropagation()}
|
||||
>
|
||||
<PdfViewer
|
||||
name={body}
|
||||
src={pdfState.data}
|
||||
requestClose={() => setPdfViewer(false)}
|
||||
/>
|
||||
</Modal>
|
||||
</FocusTrap>
|
||||
</OverlayCenter>
|
||||
</Overlay>
|
||||
)}
|
||||
{pdfState.status === AsyncStatus.Error ? (
|
||||
renderErrorButton(loadPdf, 'Open PDF')
|
||||
) : (
|
||||
<Button
|
||||
variant="Secondary"
|
||||
fill="Solid"
|
||||
radii="300"
|
||||
size="400"
|
||||
onClick={() => (pdfState.status === AsyncStatus.Success ? setPdfViewer(true) : loadPdf())}
|
||||
disabled={pdfState.status === AsyncStatus.Loading}
|
||||
before={
|
||||
pdfState.status === AsyncStatus.Loading ? (
|
||||
<Spinner fill="Solid" size="100" variant="Secondary" />
|
||||
) : (
|
||||
<Icon size="100" src={Icons.ArrowRight} filled />
|
||||
)
|
||||
}
|
||||
>
|
||||
<Text size="B400" truncate>
|
||||
Open PDF
|
||||
</Text>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DownloadFile({ body, mimeType, url, info, encInfo }: FileContentProps) {
|
||||
const mx = useMatrixClient();
|
||||
|
||||
|
@ -260,9 +193,6 @@ export const FileContent = as<'div', FileContentProps>(
|
|||
READABLE_EXT_TO_MIME_TYPE[getFileNameExt(body)]) && (
|
||||
<ReadTextFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} />
|
||||
)}
|
||||
{mimeType === 'application/pdf' && (
|
||||
<ReadPdfFile body={body} mimeType={mimeType} url={url} encInfo={encInfo} />
|
||||
)}
|
||||
<DownloadFile body={body} mimeType={mimeType} url={url} info={info} encInfo={encInfo} />
|
||||
</Box>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, Suspense, lazy } from 'react';
|
||||
import './Settings.scss';
|
||||
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
|
@ -29,7 +29,6 @@ import KeywordNotification from '../../molecules/global-notification/KeywordNoti
|
|||
import IgnoreUserList from '../../molecules/global-notification/IgnoreUserList';
|
||||
|
||||
import ProfileEditor from '../profile-editor/ProfileEditor';
|
||||
import CrossSigning from './CrossSigning';
|
||||
import KeyBackup from './KeyBackup';
|
||||
import DeviceManage from './DeviceManage';
|
||||
|
||||
|
@ -45,8 +44,10 @@ import CinnySVG from '../../../../public/res/svg/cinny.svg';
|
|||
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
|
||||
import { useSetting } from '../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../state/settings';
|
||||
import { isMacOS } from '../../utils/user-agent';
|
||||
import { KeySymbol } from '../../utils/key-symbol';
|
||||
|
||||
|
||||
const CrossSigning = lazy(() => import("./CrossSigning"));
|
||||
|
||||
|
||||
function AppearanceSection() {
|
||||
const [, updateState] = useState({});
|
||||
|
@ -151,7 +152,7 @@ function AppearanceSection() {
|
|||
onToggle={() => setEnterForNewline(!enterForNewline) }
|
||||
/>
|
||||
)}
|
||||
content={<Text variant="b3">{`Use ${isMacOS() ? KeySymbol.Command : 'Ctrl'} + ENTER to send message and ENTER for newline.`}</Text>}
|
||||
content={<Text variant="b3">{'Use Ctrl + ENTER to send message and ENTER for newline.'}</Text>}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Markdown formatting"
|
||||
|
@ -302,7 +303,9 @@ function SecuritySection() {
|
|||
<div className="settings-security">
|
||||
<div className="settings-security__card">
|
||||
<MenuHeader>Cross signing and backup</MenuHeader>
|
||||
<CrossSigning />
|
||||
<Suspense fallback={ <Text>Loading...</Text> }>
|
||||
<CrossSigning />
|
||||
</Suspense>
|
||||
<KeyBackup />
|
||||
</div>
|
||||
<DeviceManage />
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
import React, { StrictMode } from 'react';
|
||||
import React, { StrictMode, Suspense, lazy } from 'react';
|
||||
import { Provider } from 'jotai';
|
||||
|
||||
import { isAuthenticated } from '../../client/state/auth';
|
||||
|
||||
import Auth from '../templates/auth/Auth';
|
||||
import Client from '../templates/client/Client';
|
||||
import Text from '../atoms/text/Text';
|
||||
|
||||
const Auth = lazy(() => import("../templates/auth/Auth"));
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<StrictMode>
|
||||
<Provider>{isAuthenticated() ? <Client /> : <Auth />}</Provider>
|
||||
<Provider>{
|
||||
isAuthenticated() ?
|
||||
<Client /> :
|
||||
<Suspense fallback={ <Text>Loading...</Text> }><Auth /></Suspense>}
|
||||
</Provider>
|
||||
</StrictMode>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import { useCallback } from 'react';
|
||||
import type * as PdfJsDist from 'pdfjs-dist';
|
||||
import type { GetViewportParameters } from 'pdfjs-dist/types/src/display/api';
|
||||
import { useAsyncCallback } from '../hooks/useAsyncCallback';
|
||||
|
||||
export const usePdfJSLoader = () =>
|
||||
useAsyncCallback(
|
||||
useCallback(async () => {
|
||||
const pdf = await import('pdfjs-dist');
|
||||
pdf.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.js';
|
||||
return pdf;
|
||||
}, [])
|
||||
);
|
||||
|
||||
export const usePdfDocumentLoader = (pdfJS: typeof PdfJsDist | undefined, src: string) =>
|
||||
useAsyncCallback(
|
||||
useCallback(async () => {
|
||||
if (!pdfJS) {
|
||||
throw new Error('PdfJS is not loaded');
|
||||
}
|
||||
const doc = await pdfJS.getDocument(src).promise;
|
||||
return doc;
|
||||
}, [pdfJS, src])
|
||||
);
|
||||
|
||||
export const createPage = async (
|
||||
doc: PdfJsDist.PDFDocumentProxy,
|
||||
pNo: number,
|
||||
opts: GetViewportParameters
|
||||
): Promise<HTMLCanvasElement> => {
|
||||
const page = await doc.getPage(pNo);
|
||||
const pageViewport = page.getViewport(opts);
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
if (!context) throw new Error('failed to render page.');
|
||||
|
||||
canvas.width = pageViewport.width;
|
||||
canvas.height = pageViewport.height;
|
||||
|
||||
page.render({
|
||||
canvasContext: context,
|
||||
viewport: pageViewport,
|
||||
});
|
||||
|
||||
return canvas;
|
||||
};
|
|
@ -2,7 +2,6 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './Auth.scss';
|
||||
import ReCAPTCHA from 'react-google-recaptcha';
|
||||
import { Formik } from 'formik';
|
||||
|
||||
import * as auth from '../../../client/action/auth';
|
||||
|
@ -400,17 +399,6 @@ function Register({ registerInfo, loginFlow, baseUrl }) {
|
|||
asyncProcess();
|
||||
}, [process]);
|
||||
|
||||
const handleRecaptcha = async (value) => {
|
||||
if (typeof value !== 'string') return;
|
||||
const [username, password] = getInputs();
|
||||
const d = await auth.completeRegisterStage(baseUrl, username, password, {
|
||||
type: 'm.login.recaptcha',
|
||||
response: value,
|
||||
session,
|
||||
});
|
||||
if (d.done) refreshWindow();
|
||||
else setProcess({ type: 'processing', message: 'Registration in progress...' });
|
||||
};
|
||||
const handleTerms = async () => {
|
||||
const [username, password] = getInputs();
|
||||
const d = await auth.completeRegisterStage(baseUrl, username, password, {
|
||||
|
@ -435,7 +423,7 @@ function Register({ registerInfo, loginFlow, baseUrl }) {
|
|||
return (
|
||||
<>
|
||||
{process.type === 'processing' && <LoadingScreen message={process.message} />}
|
||||
{process.type === 'm.login.recaptcha' && <Recaptcha message="Please check the box below to proceed." sitekey={process.sitekey} onChange={handleRecaptcha} />}
|
||||
{process.type === 'm.login.recaptcha' && <Text weight="medium">CAPTCHA is not supported</Text>}
|
||||
{process.type === 'm.login.terms' && <Terms url={process.url} onSubmit={handleTerms} />}
|
||||
{process.type === 'm.login.email.identity' && <EmailVerify email={process.email} onContinue={handleEmailVerify} />}
|
||||
<div className="auth-form__heading">
|
||||
|
@ -607,22 +595,6 @@ LoadingScreen.propTypes = {
|
|||
message: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
function Recaptcha({ message, sitekey, onChange }) {
|
||||
return (
|
||||
<ProcessWrapper>
|
||||
<div style={{ marginBottom: 'var(--sp-normal)' }}>
|
||||
<Text variant="s1" weight="medium">{message}</Text>
|
||||
</div>
|
||||
<ReCAPTCHA sitekey={sitekey} onChange={onChange} />
|
||||
</ProcessWrapper>
|
||||
);
|
||||
}
|
||||
Recaptcha.propTypes = {
|
||||
message: PropTypes.string.isRequired,
|
||||
sitekey: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
function Terms({ url, onSubmit }) {
|
||||
return (
|
||||
<ProcessWrapper>
|
||||
|
|
|
@ -1,13 +1,3 @@
|
|||
import { UAParser } from 'ua-parser-js';
|
||||
|
||||
export const ua = () => UAParser(window.navigator.userAgent);
|
||||
|
||||
export const isMacOS = () => ua().os.name === 'Mac OS';
|
||||
|
||||
export const mobileOrTablet = (): boolean => {
|
||||
const userAgent = ua();
|
||||
const { os, device } = userAgent;
|
||||
if (device.type === 'mobile' || device.type === 'tablet') return true;
|
||||
if (os.name === 'Android' || os.name === 'iOS') return true;
|
||||
return false;
|
||||
return window.navigator && window.navigator.maxTouchPoints > 0;
|
||||
};
|
||||
|
|
|
@ -13,10 +13,6 @@ const copyFiles = {
|
|||
src: 'node_modules/@matrix-org/olm/olm.wasm',
|
||||
dest: '',
|
||||
},
|
||||
{
|
||||
src: 'node_modules/pdfjs-dist/build/pdf.worker.min.js',
|
||||
dest: '',
|
||||
},
|
||||
{
|
||||
src: '_redirects',
|
||||
dest: '',
|
||||
|
@ -41,7 +37,7 @@ export default defineConfig({
|
|||
publicDir: false,
|
||||
base: "",
|
||||
server: {
|
||||
port: 8080,
|
||||
port: 8000,
|
||||
host: true,
|
||||
},
|
||||
plugins: [
|
||||
|
@ -53,16 +49,16 @@ export default defineConfig({
|
|||
],
|
||||
optimizeDeps: {
|
||||
esbuildOptions: {
|
||||
define: {
|
||||
global: 'globalThis'
|
||||
},
|
||||
plugins: [
|
||||
// Enable esbuild polyfill plugins
|
||||
NodeGlobalsPolyfillPlugin({
|
||||
process: false,
|
||||
buffer: true,
|
||||
}),
|
||||
]
|
||||
define: {
|
||||
global: 'globalThis'
|
||||
},
|
||||
plugins: [
|
||||
// Enable esbuild polyfill plugins
|
||||
NodeGlobalsPolyfillPlugin({
|
||||
process: false,
|
||||
buffer: true,
|
||||
}),
|
||||
]
|
||||
}
|
||||
},
|
||||
build: {
|
||||
|
|
Loading…
Reference in a new issue