Fix code style

This commit is contained in:
Dylan 2022-07-14 20:02:55 +09:30
parent 3668342ca7
commit 41ff8db2f5
59 changed files with 689 additions and 704 deletions

View file

@ -2,25 +2,24 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './ConfirmDialog.scss'; import './ConfirmDialog.scss';
import { useTranslation } from 'react-i18next';
import { openReusableDialog } from '../../../client/action/navigation'; import { openReusableDialog } from '../../../client/action/navigation';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Button from '../../atoms/button/Button'; import Button from '../../atoms/button/Button';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ConfirmDialog({ function ConfirmDialog({
desc, actionTitle, actionType, onComplete, desc, actionTitle, actionType, onComplete,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className="confirm-dialog"> <div className="confirm-dialog">
<Text>{desc}</Text> <Text>{desc}</Text>
<div className="confirm-dialog__btn"> <div className="confirm-dialog__btn">
<Button variant={actionType} onClick={() => onComplete(true)}>{actionTitle}</Button> <Button variant={actionType} onClick={() => onComplete(true)}>{actionTitle}</Button>
<Button onClick={() => onComplete(false)}>{t("Molecules.ConfirmDialog.cancel")}</Button> <Button onClick={() => onComplete(false)}>{t('Molecules.ConfirmDialog.cancel')}</Button>
</div> </div>
</div> </div>
); );

View file

@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
@ -8,8 +9,7 @@ import Dialog from './Dialog';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ReusableDialog() { function ReusableDialog() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -43,7 +43,7 @@ function ReusableDialog() {
title={data?.title || ''} title={data?.title || ''}
onAfterClose={handleAfterClose} onAfterClose={handleAfterClose}
onRequestClose={handleRequestClose} onRequestClose={handleRequestClose}
contentOptions={<IconButton src={CrossIC} onClick={handleRequestClose} tooltip={t("Molecules.ReusableDialog.close_tooltip")} />} contentOptions={<IconButton src={CrossIC} onClick={handleRequestClose} tooltip={t('Molecules.ReusableDialog.close_tooltip')} />}
invisibleScroll invisibleScroll
> >
{data?.render(handleRequestClose) || <div />} {data?.render(handleRequestClose) || <div />}

View file

@ -3,6 +3,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './FollowingMembers.scss'; import './FollowingMembers.scss';
import { Trans } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import { openReadReceipts } from '../../../client/action/navigation'; import { openReadReceipts } from '../../../client/action/navigation';
@ -11,15 +12,10 @@ import Text from '../../atoms/text/Text';
import RawIcon from '../../atoms/system-icons/RawIcon'; import RawIcon from '../../atoms/system-icons/RawIcon';
import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg'; import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
import { getUsersActionJsx } from '../../organisms/room/common';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import '../../i18n';
import '../../i18n.jsx'
import { useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
import { getUserDisplayName } from '../../../util/matrixUtil'; import { getUserDisplayName } from '../../../util/matrixUtil';
function FollowingMembers({ roomTimeline }) { function FollowingMembers({ roomTimeline }) {
@ -65,9 +61,9 @@ function FollowingMembers({ roomTimeline }) {
user_one: twemojify(getUserDisplayName(room, filteredM?.[0])), user_one: twemojify(getUserDisplayName(room, filteredM?.[0])),
user_two: twemojify(getUserDisplayName(room, filteredM?.[1])), user_two: twemojify(getUserDisplayName(room, filteredM?.[1])),
user_three: twemojify(getUserDisplayName(room, filteredM?.[2])), user_three: twemojify(getUserDisplayName(room, filteredM?.[2])),
other_count: filteredM.length - 3 other_count: filteredM.length - 3,
}} }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</Text> </Text>
</button> </button>

View file

@ -2,14 +2,14 @@ import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './ImageUpload.scss'; import './ImageUpload.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Avatar from '../../atoms/avatar/Avatar'; import Avatar from '../../atoms/avatar/Avatar';
import Spinner from '../../atoms/spinner/Spinner'; import Spinner from '../../atoms/spinner/Spinner';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ImageUpload({ function ImageUpload({
text, bgColor, imageSrc, onUpload, onRequestRemove, text, bgColor, imageSrc, onUpload, onRequestRemove,
@ -58,7 +58,7 @@ function ImageUpload({
size="large" size="large"
/> />
<div className={`img-upload__process ${uploadPromise === null ? ' img-upload__process--stopped' : ''}`}> <div className={`img-upload__process ${uploadPromise === null ? ' img-upload__process--stopped' : ''}`}>
{uploadPromise === null && <Text variant="b3" weight="bold">{t("Molecules.ImageUpload.prompt")}</Text>} {uploadPromise === null && <Text variant="b3" weight="bold">{t('Molecules.ImageUpload.prompt')}</Text>}
{uploadPromise !== null && <Spinner size="small" />} {uploadPromise !== null && <Spinner size="small" />}
</div> </div>
</button> </button>
@ -68,7 +68,7 @@ function ImageUpload({
type="button" type="button"
onClick={uploadPromise === null ? onRequestRemove : cancelUpload} onClick={uploadPromise === null ? onRequestRemove : cancelUpload}
> >
<Text variant="b3">{uploadPromise ? t("Molecules.ImageUpload.cancel") : t("Molecules.ImageUpload.remove")}</Text> <Text variant="b3">{uploadPromise ? t('Molecules.ImageUpload.cancel') : t('Molecules.ImageUpload.remove')}</Text>
</button> </button>
)} )}
<input onChange={uploadImage} style={{ display: 'none' }} ref={uploadImageRef} type="file" /> <input onChange={uploadImage} style={{ display: 'none' }} ref={uploadImageRef} type="file" />

View file

@ -3,6 +3,7 @@ import './ExportE2ERoomKeys.scss';
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import { encryptMegolmKeyFile } from '../../../util/cryptE2ERoomKeys'; import { encryptMegolmKeyFile } from '../../../util/cryptE2ERoomKeys';
@ -14,8 +15,7 @@ import Spinner from '../../atoms/spinner/Spinner';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ExportE2ERoomKeys() { function ExportE2ERoomKeys() {
const isMountStore = useStore(); const isMountStore = useStore();
@ -34,14 +34,14 @@ function ExportE2ERoomKeys() {
if (password !== confirmPasswordRef.current.value) { if (password !== confirmPasswordRef.current.value) {
setStatus({ setStatus({
isOngoing: false, isOngoing: false,
msg: t("Molecules.ExportE2ERoomKeys.password_does_not_match"), msg: t('Molecules.ExportE2ERoomKeys.password_does_not_match'),
type: cons.status.ERROR, type: cons.status.ERROR,
}); });
return; return;
} }
setStatus({ setStatus({
isOngoing: true, isOngoing: true,
msg: t("Molecules.ExportE2ERoomKeys.getting_keys"), msg: t('Molecules.ExportE2ERoomKeys.getting_keys'),
type: cons.status.IN_FLIGHT, type: cons.status.IN_FLIGHT,
}); });
try { try {
@ -49,7 +49,7 @@ function ExportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: true, isOngoing: true,
msg: t("Molecules.ExportE2ERoomKeys.encrypting_keys"), msg: t('Molecules.ExportE2ERoomKeys.encrypting_keys'),
type: cons.status.IN_FLIGHT, type: cons.status.IN_FLIGHT,
}); });
} }
@ -61,7 +61,7 @@ function ExportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: false, isOngoing: false,
msg: t("Molecules.ExportE2ERoomKeys.export_success"), msg: t('Molecules.ExportE2ERoomKeys.export_success'),
type: cons.status.SUCCESS, type: cons.status.SUCCESS,
}); });
} }
@ -69,7 +69,7 @@ function ExportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: false, isOngoing: false,
msg: e.friendlyText || t("Molecules.ExportE2ERoomKeys.export_failed"), msg: e.friendlyText || t('Molecules.ExportE2ERoomKeys.export_failed'),
type: cons.status.ERROR, type: cons.status.ERROR,
}); });
} }
@ -88,7 +88,7 @@ function ExportE2ERoomKeys() {
<form className="export-e2e-room-keys__form" onSubmit={(e) => { e.preventDefault(); exportE2ERoomKeys(); }}> <form className="export-e2e-room-keys__form" onSubmit={(e) => { e.preventDefault(); exportE2ERoomKeys(); }}>
<Input forwardRef={passwordRef} type="password" placeholder="Password" required /> <Input forwardRef={passwordRef} type="password" placeholder="Password" required />
<Input forwardRef={confirmPasswordRef} type="password" placeholder="Confirm password" required /> <Input forwardRef={confirmPasswordRef} type="password" placeholder="Confirm password" required />
<Button disabled={status.isOngoing} variant="primary" type="submit">{t("Molecules.ExportE2ERoomKeys.button_text")}</Button> <Button disabled={status.isOngoing} variant="primary" type="submit">{t('Molecules.ExportE2ERoomKeys.button_text')}</Button>
</form> </form>
{ status.type === cons.status.IN_FLIGHT && ( { status.type === cons.status.IN_FLIGHT && (
<div className="import-e2e-room-keys__process"> <div className="import-e2e-room-keys__process">

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import './ImportE2ERoomKeys.scss'; import './ImportE2ERoomKeys.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import { decryptMegolmKeyFile } from '../../../util/cryptE2ERoomKeys'; import { decryptMegolmKeyFile } from '../../../util/cryptE2ERoomKeys';
@ -15,8 +16,7 @@ import CirclePlusIC from '../../../../public/res/ic/outlined/circle-plus.svg';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ImportE2ERoomKeys() { function ImportE2ERoomKeys() {
const isMountStore = useStore(); const isMountStore = useStore();
@ -27,7 +27,7 @@ function ImportE2ERoomKeys() {
type: cons.status.PRE_FLIGHT, type: cons.status.PRE_FLIGHT,
}); });
const inputRef = useRef(null); const inputRef = useRef(null);
const passwordRef = useRef(null); const passwordRef = useRef(null);
const { t } = useTranslation(); const { t } = useTranslation();
@ -37,7 +37,7 @@ function ImportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: true, isOngoing: true,
msg: t("Molecules.ImportE2ERoomKeys.decrypting_file"), msg: t('Molecules.ImportE2ERoomKeys.decrypting_file'),
type: cons.status.IN_FLIGHT, type: cons.status.IN_FLIGHT,
}); });
} }
@ -46,7 +46,7 @@ function ImportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: true, isOngoing: true,
msg: t("Molecules.ImportE2ERoomKeys.decrypting_messages"), msg: t('Molecules.ImportE2ERoomKeys.decrypting_messages'),
type: cons.status.IN_FLIGHT, type: cons.status.IN_FLIGHT,
}); });
} }
@ -54,7 +54,7 @@ function ImportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: false, isOngoing: false,
msg: t("Molecules.ImportE2ERoomKeys.import_success"), msg: t('Molecules.ImportE2ERoomKeys.import_success'),
type: cons.status.SUCCESS, type: cons.status.SUCCESS,
}); });
inputRef.current.value = null; inputRef.current.value = null;
@ -64,7 +64,7 @@ function ImportE2ERoomKeys() {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
isOngoing: false, isOngoing: false,
msg: e.friendlyText || t("Molecules.ImportE2ERoomKeys.import_failed"), msg: e.friendlyText || t('Molecules.ImportE2ERoomKeys.import_failed'),
type: cons.status.ERROR, type: cons.status.ERROR,
}); });
} }
@ -119,9 +119,9 @@ function ImportE2ERoomKeys() {
<Text>{keyFile.name}</Text> <Text>{keyFile.name}</Text>
</div> </div>
)} )}
{keyFile === null && <Button onClick={() => inputRef.current.click()}>{t("Molecules.ImportE2ERoomKeys.import_keys_button")}</Button>} {keyFile === null && <Button onClick={() => inputRef.current.click()}>{t('Molecules.ImportE2ERoomKeys.import_keys_button')}</Button>}
<Input forwardRef={passwordRef} type="password" placeholder="Password" required /> <Input forwardRef={passwordRef} type="password" placeholder="Password" required />
<Button disabled={status.isOngoing} variant="primary" type="submit">{t("Molecules.ImportE2ERoomKeys.decrypt_button")}</Button> <Button disabled={status.isOngoing} variant="primary" type="submit">{t('Molecules.ImportE2ERoomKeys.decrypt_button')}</Button>
</form> </form>
{ status.type === cons.status.IN_FLIGHT && ( { status.type === cons.status.IN_FLIGHT && (
<div className="import-e2e-room-keys__process"> <div className="import-e2e-room-keys__process">

View file

@ -4,6 +4,7 @@ import './Media.scss';
import encrypt from 'browser-encrypt-attachment'; import encrypt from 'browser-encrypt-attachment';
import { useTranslation } from 'react-i18next';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import IconButton from '../../atoms/button/IconButton'; import IconButton from '../../atoms/button/IconButton';
import Spinner from '../../atoms/spinner/Spinner'; import Spinner from '../../atoms/spinner/Spinner';
@ -12,8 +13,7 @@ import DownloadSVG from '../../../../public/res/ic/outlined/download.svg';
import ExternalSVG from '../../../../public/res/ic/outlined/external.svg'; import ExternalSVG from '../../../../public/res/ic/outlined/external.svg';
import PlaySVG from '../../../../public/res/ic/outlined/play.svg'; import PlaySVG from '../../../../public/res/ic/outlined/play.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
// https://github.com/matrix-org/matrix-react-sdk/blob/a9e28db33058d1893d964ec96cd247ecc3d92fc3/src/utils/blobs.ts#L73 // https://github.com/matrix-org/matrix-react-sdk/blob/a9e28db33058d1893d964ec96cd247ecc3d92fc3/src/utils/blobs.ts#L73
const ALLOWED_BLOB_MIMETYPES = [ const ALLOWED_BLOB_MIMETYPES = [
@ -99,7 +99,7 @@ function FileHeader({
external && ( external && (
<IconButton <IconButton
size="extra-small" size="extra-small"
tooltip={t("Molecules.Media.open_new_tab")} tooltip={t('Molecules.Media.open_new_tab')}
src={ExternalSVG} src={ExternalSVG}
onClick={() => window.open(url || link)} onClick={() => window.open(url || link)}
/> />
@ -108,7 +108,7 @@ function FileHeader({
<a href={url || link} download={name} target="_blank" rel="noreferrer"> <a href={url || link} download={name} target="_blank" rel="noreferrer">
<IconButton <IconButton
size="extra-small" size="extra-small"
tooltip= {t("Molecules.Media.download")} tooltip={t('Molecules.Media.download')}
src={DownloadSVG} src={DownloadSVG}
onClick={handleDownload} onClick={handleDownload}
/> />
@ -155,7 +155,6 @@ function Image({
name, width, height, link, file, type, name, width, height, link, file, type,
}) { }) {
const [url, setUrl] = useState(null); const [url, setUrl] = useState(null);
useEffect(() => { useEffect(() => {
let unmounted = false; let unmounted = false;
@ -217,7 +216,7 @@ function Audio({
<FileHeader name={name} link={file !== null ? url : url || link} type={type} external /> <FileHeader name={name} link={file !== null ? url : url || link} type={type} external />
<div className="audio-container"> <div className="audio-container">
{ url === null && isLoading && <Spinner size="small" /> } { url === null && isLoading && <Spinner size="small" /> }
{ url === null && !isLoading && <IconButton onClick={handlePlayAudio} tooltip={t("Molecules.Media.play_audio")} src={PlaySVG} />} { url === null && !isLoading && <IconButton onClick={handlePlayAudio} tooltip={t('Molecules.Media.play_audio')} src={PlaySVG} />}
{ url !== null && ( { url !== null && (
/* eslint-disable-next-line jsx-a11y/media-has-caption */ /* eslint-disable-next-line jsx-a11y/media-has-caption */
<audio autoPlay controls> <audio autoPlay controls>
@ -284,7 +283,7 @@ function Video({
className="video-container" className="video-container"
> >
{ url === null && isLoading && <Spinner size="small" /> } { url === null && isLoading && <Spinner size="small" /> }
{ url === null && !isLoading && <IconButton onClick={handlePlayVideo} tooltip={t("Molecules.Media.play_video")} src={PlaySVG} />} { url === null && !isLoading && <IconButton onClick={handlePlayVideo} tooltip={t('Molecules.Media.play_video')} src={PlaySVG} />}
{ url !== null && ( { url !== null && (
/* eslint-disable-next-line jsx-a11y/media-has-caption */ /* eslint-disable-next-line jsx-a11y/media-has-caption */
<video autoPlay controls poster={thumbUrl}> <video autoPlay controls poster={thumbUrl}>

View file

@ -5,6 +5,7 @@ import React, {
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './Message.scss'; import './Message.scss';
import { useTranslation, Trans } from 'react-i18next';
import { getShortcodeToCustomEmoji } from '../../organisms/emoji-board/custom-emoji'; import { getShortcodeToCustomEmoji } from '../../organisms/emoji-board/custom-emoji';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
@ -38,10 +39,7 @@ import BinIC from '../../../../public/res/ic/outlined/bin.svg';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
import { t } from 'i18next';
function PlaceholderMessage() { function PlaceholderMessage() {
return ( return (
@ -123,7 +121,7 @@ const MessageReplyWrapper = React.memo(({ roomTimeline, eventId }) => {
useEffect(() => { useEffect(() => {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const timelineSet = roomTimeline.getUnfilteredTimelineSet(); const timelineSet = roomTimeline.getUnfilteredTimelineSet();
const loadReply = async () => { const loadReply = async () => {
try { try {
const eTimeline = await mx.getEventTimeline(timelineSet, eventId); const eTimeline = await mx.getEventTimeline(timelineSet, eventId);
@ -256,7 +254,7 @@ const MessageBody = React.memo(({
)} )}
{ content } { content }
</div> </div>
{ isEdited && <Text className="message__body-edited" variant="b3"><Trans i18nKey={"Molecules.Message.edited"}/></Text>} { isEdited && <Text className="message__body-edited" variant="b3"><Trans i18nKey="Molecules.Message.edited" /></Text>}
</div> </div>
); );
}); });
@ -297,14 +295,14 @@ function MessageEdit({ body, onSave, onCancel }) {
forwardRef={editInputRef} forwardRef={editInputRef}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
value={body} value={body}
placeholder={t("Molecules.Message.edit_placeholder")} placeholder={t('Molecules.Message.edit_placeholder')}
required required
resizable resizable
autoFocus autoFocus
/> />
<div className="message__edit-btns"> <div className="message__edit-btns">
<Button type="submit" variant="primary">{t("common.save")}</Button> <Button type="submit" variant="primary">{t('common.save')}</Button>
<Button onClick={onCancel}>{t("common.cancel")}</Button> <Button onClick={onCancel}>{t('common.cancel')}</Button>
</div> </div>
</form> </form>
); );
@ -351,20 +349,17 @@ function pickEmoji(e, roomId, eventId, roomTimeline) {
function genReactionMsg(userIds, reaction) { function genReactionMsg(userIds, reaction) {
console.log(reaction); console.log(reaction);
return ( return (
<> <Trans
<Trans i18nKey="Molecules.Message.user_reacted"
i18nKey="Molecules.Message.user_reacted" values={{
values={{ count: userIds.length,
count: userIds.length, user_one: getUsername(userIds?.[0]),
user_one: getUsername(userIds?.[0]), user_two: getUsername(userIds?.[1]),
user_two: getUsername(userIds?.[1]), user_three: getUsername(userIds?.[2]),
user_three: getUsername(userIds?.[2]), other_count: userIds.length - 3,
other_count: userIds.length - 3, }}
}} components={{ bold: <b />, emoji: reaction }}
components={{bold: <b/>, emoji: reaction}} />
/>
</>
); );
} }
@ -520,6 +515,8 @@ const MessageOptions = React.memo(({
const canIRedact = room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel); const canIRedact = room.currentState.hasSufficientPowerLevelFor('redact', myPowerlevel);
const canSendReaction = room.currentState.maySendEvent('m.reaction', mx.getUserId()); const canSendReaction = room.currentState.maySendEvent('m.reaction', mx.getUserId());
const { t } = useTranslation();
return ( return (
<div className="message__options"> <div className="message__options">
{canSendReaction && ( {canSendReaction && (
@ -527,38 +524,38 @@ const MessageOptions = React.memo(({
onClick={(e) => pickEmoji(e, roomId, mEvent.getId(), roomTimeline)} onClick={(e) => pickEmoji(e, roomId, mEvent.getId(), roomTimeline)}
src={EmojiAddIC} src={EmojiAddIC}
size="extra-small" size="extra-small"
tooltip={t("Molecules.Message.add_reaction_tooltip")} tooltip={t('Molecules.Message.add_reaction_tooltip')}
/> />
)} )}
<IconButton <IconButton
onClick={() => reply()} onClick={() => reply()}
src={ReplyArrowIC} src={ReplyArrowIC}
size="extra-small" size="extra-small"
tooltip={t("Molecules.Message.reply_tooltip")} tooltip={t('Molecules.Message.reply_tooltip')}
/> />
{(senderId === mx.getUserId() && !isMedia(mEvent)) && ( {(senderId === mx.getUserId() && !isMedia(mEvent)) && (
<IconButton <IconButton
onClick={() => edit(true)} onClick={() => edit(true)}
src={PencilIC} src={PencilIC}
size="extra-small" size="extra-small"
tooltip={t("Molecules.Message.edit_tooltip")} tooltip={t('Molecules.Message.edit_tooltip')}
/> />
)} )}
<ContextMenu <ContextMenu
content={() => ( content={() => (
<> <>
<MenuHeader>{t("Molecules.Message.options_header")}</MenuHeader> <MenuHeader>{t('Molecules.Message.options_header')}</MenuHeader>
<MenuItem <MenuItem
iconSrc={TickMarkIC} iconSrc={TickMarkIC}
onClick={() => openReadReceipts(roomId, roomTimeline.getEventReaders(mEvent))} onClick={() => openReadReceipts(roomId, roomTimeline.getEventReaders(mEvent))}
> >
{t("Molecules.Message.read_receipts")} {t('Molecules.Message.read_receipts')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
iconSrc={CmdIC} iconSrc={CmdIC}
onClick={() => handleOpenViewSource(mEvent, roomTimeline)} onClick={() => handleOpenViewSource(mEvent, roomTimeline)}
> >
{t("Molecules.Message.view_source")} {t('Molecules.Message.view_source')}
</MenuItem> </MenuItem>
{(canIRedact || senderId === mx.getUserId()) && ( {(canIRedact || senderId === mx.getUserId()) && (
<> <>
@ -568,9 +565,9 @@ const MessageOptions = React.memo(({
iconSrc={BinIC} iconSrc={BinIC}
onClick={async () => { onClick={async () => {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Molecules.Message.delete_message_prompt"), t('Molecules.Message.delete_message_prompt'),
t("Molecules.Message.delete_message_confirmation"), t('Molecules.Message.delete_message_confirmation'),
t("Molecules.Message.delete_message_button"), t('Molecules.Message.delete_message_button'),
'danger', 'danger',
); );
if (!isConfirmed) return; if (!isConfirmed) return;
@ -588,7 +585,7 @@ const MessageOptions = React.memo(({
onClick={toggleMenu} onClick={toggleMenu}
src={VerticalMenuIC} src={VerticalMenuIC}
size="extra-small" size="extra-small"
tooltip={t("Molecules.Message.options_tooltip")} tooltip={t('Molecules.Message.options_tooltip')}
/> />
)} )}
/> />

View file

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './PopupWindow.scss'; import './PopupWindow.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
@ -13,8 +14,7 @@ import RawModal from '../../atoms/modal/RawModal';
import ChevronLeftIC from '../../../../public/res/ic/outlined/chevron-left.svg'; import ChevronLeftIC from '../../../../public/res/ic/outlined/chevron-left.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function PWContentSelector({ function PWContentSelector({
selected, variant, iconSrc, selected, variant, iconSrc,
@ -74,7 +74,7 @@ function PopupWindow({
{haveDrawer && ( {haveDrawer && (
<div className="pw__drawer"> <div className="pw__drawer">
<Header> <Header>
<IconButton size="small" src={ChevronLeftIC} onClick={onRequestClose} tooltip={t("Molecules.PopupWindow.close_tooltip")}/> <IconButton size="small" src={ChevronLeftIC} onClick={onRequestClose} tooltip={t('Molecules.PopupWindow.close_tooltip')} />
<TitleWrapper> <TitleWrapper>
{ {
typeof title === 'string' typeof title === 'string'

View file

@ -2,13 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './PowerLevelSelector.scss'; import './PowerLevelSelector.scss';
import { useTranslation } from 'react-i18next';
import IconButton from '../../atoms/button/IconButton'; import IconButton from '../../atoms/button/IconButton';
import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu'; import { MenuHeader, MenuItem } from '../../atoms/context-menu/ContextMenu';
import CheckIC from '../../../../public/res/ic/outlined/check.svg'; import CheckIC from '../../../../public/res/ic/outlined/check.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function PowerLevelSelector({ function PowerLevelSelector({
value, max, onSelect, value, max, onSelect,
@ -30,7 +30,7 @@ function PowerLevelSelector({
defaultValue={value} defaultValue={value}
type="number" type="number"
name="power-level" name="power-level"
placeholder={t("Molecules.PowerLevelIndicator.placeholder")} placeholder={t('Molecules.PowerLevelIndicator.placeholder')}
max={max} max={max}
autoComplete="off" autoComplete="off"
required required

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomAliases.scss'; import './RoomAliases.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import { Debounce } from '../../../util/common'; import { Debounce } from '../../../util/common';
@ -17,14 +18,12 @@ import SettingTile from '../setting-tile/SettingTile';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function useValidate(hsString) { function useValidate(hsString) {
const [debounce] = useState(new Debounce()); const [debounce] = useState(new Debounce());
const [validate, setValidate] = useState({ alias: null, status: cons.status.PRE_FLIGHT }); const [validate, setValidate] = useState({ alias: null, status: cons.status.PRE_FLIGHT });
const { t } = useTranslation(); const { t } = useTranslation();
const setValidateToDefault = () => { const setValidateToDefault = () => {
@ -43,7 +42,7 @@ function useValidate(hsString) {
setValidate({ setValidate({
alias: null, alias: null,
status: cons.status.ERROR, status: cons.status.ERROR,
msg: t("Molecules.RoomAliases.invalid_characters") msg: t('Molecules.RoomAliases.invalid_characters'),
}); });
return false; return false;
} }
@ -65,7 +64,7 @@ function useValidate(hsString) {
setValidate({ setValidate({
alias, alias,
status: cons.status.IN_FLIGHT, status: cons.status.IN_FLIGHT,
msg: t("Molecules.RoomAliases.validating_alias", {alias: alias}), msg: t('Molecules.RoomAliases.validating_alias', { alias }),
}); });
const isValid = await isRoomAliasAvailable(alias); const isValid = await isRoomAliasAvailable(alias);
@ -76,7 +75,7 @@ function useValidate(hsString) {
return { return {
alias, alias,
status: isValid ? cons.status.SUCCESS : cons.status.ERROR, status: isValid ? cons.status.SUCCESS : cons.status.ERROR,
msg: t( isValid ? "Molecules.RoomAliases.alias_available": "Molecules.RoomAliases.alias_unavailable", {alias: alias}), msg: t(isValid ? 'Molecules.RoomAliases.alias_available' : 'Molecules.RoomAliases.alias_unavailable', { alias }),
}; };
}); });
}, 600)(); }, 600)();
@ -233,7 +232,7 @@ function RoomAliases({ roomId }) {
const handleDeleteAlias = async (alias) => { const handleDeleteAlias = async (alias) => {
try { try {
setDeleteAlias({ alias, status: cons.status.IN_FLIGHT, msg: t("Molecules.RoomAliases.deleting_alias")}); setDeleteAlias({ alias, status: cons.status.IN_FLIGHT, msg: t('Molecules.RoomAliases.deleting_alias') });
await mx.deleteAlias(alias); await mx.deleteAlias(alias);
let { main, published, local } = aliases; let { main, published, local } = aliases;
if (published.includes(alias)) { if (published.includes(alias)) {
@ -267,10 +266,10 @@ function RoomAliases({ roomId }) {
return ( return (
<div className="room-aliases__item-btns"> <div className="room-aliases__item-btns">
{canPublishAlias && !isMain && <Button onClick={() => handleSetMainAlias(alias)} variant="primary">{t("Molecules.RoomAliases.set_main_alias")}</Button>} {canPublishAlias && !isMain && <Button onClick={() => handleSetMainAlias(alias)} variant="primary">{t('Molecules.RoomAliases.set_main_alias')}</Button>}
{!isPublished && canPublishAlias && <Button onClick={() => handlePublishAlias(alias)} variant="positive">{t("Molecules.RoomAliases.publish_alias")}</Button>} {!isPublished && canPublishAlias && <Button onClick={() => handlePublishAlias(alias)} variant="positive">{t('Molecules.RoomAliases.publish_alias')}</Button>}
{isPublished && canPublishAlias && <Button onClick={() => handleUnPublishAlias(alias)} variant="caution">{t("Molecules.RoomAliases.unpublish_alias")}</Button>} {isPublished && canPublishAlias && <Button onClick={() => handleUnPublishAlias(alias)} variant="caution">{t('Molecules.RoomAliases.unpublish_alias')}</Button>}
<Button onClick={() => handleDeleteAlias(alias)} variant="danger">{t("Molecules.RoomAliases.delete_alias")}</Button> <Button onClick={() => handleDeleteAlias(alias)} variant="danger">{t('Molecules.RoomAliases.delete_alias')}</Button>
</div> </div>
); );
}; };
@ -286,7 +285,7 @@ function RoomAliases({ roomId }) {
<Checkbox variant="positive" disabled={disabled} isActive={isActive} onToggle={() => handleAliasSelect(alias)} /> <Checkbox variant="positive" disabled={disabled} isActive={isActive} onToggle={() => handleAliasSelect(alias)} />
<Text> <Text>
{alias} {alias}
{isMain && <span>{t("Molecules.RoomAliases.main_alias")}</span>} {isMain && <span>{t('Molecules.RoomAliases.main_alias')}</span>}
</Text> </Text>
</div> </div>
{isActive && renderAliasBtns(alias)} {isActive && renderAliasBtns(alias)}
@ -300,8 +299,8 @@ function RoomAliases({ roomId }) {
return ( return (
<div className="room-aliases"> <div className="room-aliases">
<SettingTile <SettingTile
title={t("Molecules.RoomAliases.publish_to_room_directory.title")} title={t('Molecules.RoomAliases.publish_to_room_directory.title')}
content={<Text variant="b3">{t(room.isSpaceRoom() ? "Molecules.RoomAliases.publish_to_room_directory.publish_space_message" : "Molecules.RoomAliases.publish_to_room_directory.publish_room_message", {homeserver: hsString})}</Text>} content={<Text variant="b3">{t(room.isSpaceRoom() ? 'Molecules.RoomAliases.publish_to_room_directory.publish_space_message' : 'Molecules.RoomAliases.publish_to_room_directory.publish_room_message', { homeserver: hsString })}</Text>}
options={( options={(
<Toggle <Toggle
isActive={isPublic} isActive={isPublic}
@ -312,35 +311,35 @@ function RoomAliases({ roomId }) {
/> />
<div className="room-aliases__content"> <div className="room-aliases__content">
<MenuHeader>{t("Molecules.RoomAliases.published_addresses.title")}</MenuHeader> <MenuHeader>{t('Molecules.RoomAliases.published_addresses.title')}</MenuHeader>
{(aliases.published.length === 0) && <Text className="room-aliases__message">{t("Molecules.RoomAliases.published_addresses.none")}</Text>} {(aliases.published.length === 0) && <Text className="room-aliases__message">{t('Molecules.RoomAliases.published_addresses.none')}</Text>}
{(aliases.published.length > 0 && !aliases.main) && <Text className="room-aliases__message">{t("Molecules.RoomAliases.published_addresses.no_main_address")}</Text>} {(aliases.published.length > 0 && !aliases.main) && <Text className="room-aliases__message">{t('Molecules.RoomAliases.published_addresses.no_main_address')}</Text>}
{aliases.published.map(renderAlias)} {aliases.published.map(renderAlias)}
<Text className="room-aliases__message" variant="b3"> <Text className="room-aliases__message" variant="b3">
{t(room.isSpaceRoom() ? "Molecules.RoomAliases.published_addresses.message_space" : "Molecules.RoomAliases.published_addresses.message_room")} {t(room.isSpaceRoom() ? 'Molecules.RoomAliases.published_addresses.message_space' : 'Molecules.RoomAliases.published_addresses.message_room')}
</Text> </Text>
</div> </div>
{ isLocalVisible && ( { isLocalVisible && (
<div className="room-aliases__content"> <div className="room-aliases__content">
<MenuHeader>{t("Molecules.RoomAliases.local_addresses.title")}</MenuHeader> <MenuHeader>{t('Molecules.RoomAliases.local_addresses.title')}</MenuHeader>
{(aliases.local.length === 0) && <Text className="room-aliases__message">{t("Molecules.RoomAliases.local_addresses.none")}</Text>} {(aliases.local.length === 0) && <Text className="room-aliases__message">{t('Molecules.RoomAliases.local_addresses.none')}</Text>}
{aliases.local.map(renderAlias)} {aliases.local.map(renderAlias)}
<Text className="room-aliases__message" variant="b3"> <Text className="room-aliases__message" variant="b3">
{t(room.isSpaceRoom() ? "Molecules.RoomAliases.local_addresses.message_space" : "Molecules.RoomAliases.local_addresses.message_room" )} {t(room.isSpaceRoom() ? 'Molecules.RoomAliases.local_addresses.message_space' : 'Molecules.RoomAliases.local_addresses.message_room')}
</Text> </Text>
<Text className="room-aliases__form-label" variant="b2">{t("Molecules.RoomAliases.local_addresses.add")}</Text> <Text className="room-aliases__form-label" variant="b2">{t('Molecules.RoomAliases.local_addresses.add')}</Text>
<form className="room-aliases__form" onSubmit={handleAliasSubmit}> <form className="room-aliases__form" onSubmit={handleAliasSubmit}>
<div className="room-aliases__input-wrapper"> <div className="room-aliases__input-wrapper">
<Input <Input
name="alias-input" name="alias-input"
state={inputState} state={inputState}
onChange={handleAliasChange} onChange={handleAliasChange}
placeholder={t(room.isSpaceRoom() ? "Molecules.RoomAliases.local_addresses.placeholder_space" : "Molecules.RoomAliases.local_addresses.placeholder_room")} placeholder={t(room.isSpaceRoom() ? 'Molecules.RoomAliases.local_addresses.placeholder_space' : 'Molecules.RoomAliases.local_addresses.placeholder_room')}
required required
/> />
</div> </div>
<Button variant="primary" type="submit">{t("Molecules.RoomAliases.local_addresses.add_button")}</Button> <Button variant="primary" type="submit">{t('Molecules.RoomAliases.local_addresses.add_button')}</Button>
</form> </form>
<div className="room-aliases__input-status"> <div className="room-aliases__input-status">
{validate.status === cons.status.SUCCESS && <Text className="room-aliases__valid" variant="b2">{validate.msg}</Text>} {validate.status === cons.status.SUCCESS && <Text className="room-aliases__valid" variant="b2">{validate.msg}</Text>}
@ -350,7 +349,7 @@ function RoomAliases({ roomId }) {
)} )}
<div className="room-aliases__content"> <div className="room-aliases__content">
<Button onClick={() => setIsLocalVisible(!isLocalVisible)}> <Button onClick={() => setIsLocalVisible(!isLocalVisible)}>
{t(isLocalVisible ? "Molecules.RoomAliases.local_addresses.hide" : "Molecules.RoomAliases.local_addresses.view" )} {t(isLocalVisible ? 'Molecules.RoomAliases.local_addresses.hide' : 'Molecules.RoomAliases.local_addresses.view')}
</Button> </Button>
</div> </div>
</div> </div>

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomEncryption.scss'; import './RoomEncryption.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
@ -10,8 +11,8 @@ import SettingTile from '../setting-tile/SettingTile';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function RoomEncryption({ roomId }) { function RoomEncryption({ roomId }) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -24,14 +25,14 @@ function RoomEncryption({ roomId }) {
const handleEncryptionEnable = async () => { const handleEncryptionEnable = async () => {
const joinRule = room.getJoinRule(); const joinRule = room.getJoinRule();
const confirmMsg1 = t("Molecules.RoomEncryption.encryption_public_room_message"); const confirmMsg1 = t('Molecules.RoomEncryption.encryption_public_room_message');
const confirmMsg2 = t("Molecules.RoomEncryption.encryption_message"); const confirmMsg2 = t('Molecules.RoomEncryption.encryption_message');
const isConfirmed1 = (joinRule === 'public') const isConfirmed1 = (joinRule === 'public')
? await confirmDialog(t("Molecules.RoomEncryption.enable_encryption_prompt"), confirmMsg1, t("Molecules.RoomEncryption.continue_button"), 'caution') ? await confirmDialog(t('Molecules.RoomEncryption.enable_encryption_prompt'), confirmMsg1, t('Molecules.RoomEncryption.continue_button'), 'caution')
: true; : true;
if (!isConfirmed1) return; if (!isConfirmed1) return;
if (await confirmDialog(t("Molecules.RoomEncryption.enable_encryption_prompt"), confirmMsg2, t("Molecules.RoomEncryption.enable_encryption_button"), 'caution')) { if (await confirmDialog(t('Molecules.RoomEncryption.enable_encryption_prompt'), confirmMsg2, t('Molecules.RoomEncryption.enable_encryption_button'), 'caution')) {
setIsEncrypted(true); setIsEncrypted(true);
mx.sendStateEvent(roomId, 'm.room.encryption', { mx.sendStateEvent(roomId, 'm.room.encryption', {
algorithm: 'm.megolm.v1.aes-sha2', algorithm: 'm.megolm.v1.aes-sha2',
@ -42,9 +43,9 @@ function RoomEncryption({ roomId }) {
return ( return (
<div className="room-encryption"> <div className="room-encryption">
<SettingTile <SettingTile
title={t("Molecules.RoomEncryption.enable_room_encryption")} title={t('Molecules.RoomEncryption.enable_room_encryption')}
content={( content={(
<Text variant="b3">{t("Molecules.RoomEncryption.encryption_cannot_be_disabled")}</Text> <Text variant="b3">{t('Molecules.RoomEncryption.encryption_cannot_be_disabled')}</Text>
)} )}
options={( options={(
<Toggle <Toggle

View file

@ -2,14 +2,14 @@ import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomHistoryVisibility.scss'; import './RoomHistoryVisibility.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import RadioButton from '../../atoms/button/RadioButton'; import RadioButton from '../../atoms/button/RadioButton';
import { MenuItem } from '../../atoms/context-menu/ContextMenu'; import { MenuItem } from '../../atoms/context-menu/ContextMenu';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const visibility = { const visibility = {
WORLD_READABLE: 'world_readable', WORLD_READABLE: 'world_readable',
@ -40,7 +40,8 @@ function setHistoryVisibility(roomId, type) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
return mx.sendStateEvent( return mx.sendStateEvent(
roomId, 'm.room.history_visibility', roomId,
'm.room.history_visibility',
{ {
history_visibility: type, history_visibility: type,
}, },
@ -92,7 +93,7 @@ function RoomHistoryVisibility({ roomId }) {
</MenuItem> </MenuItem>
)) ))
} }
<Text variant="b3">{t("Molecules.RoomHistoryVisibility.changes_only_affect_future")}</Text> <Text variant="b3">{t('Molecules.RoomHistoryVisibility.changes_only_affect_future')}</Text>
</div> </div>
); );
} }

View file

@ -4,6 +4,7 @@ import React, {
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomMembers.scss'; import './RoomMembers.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
import { openProfileViewer } from '../../../client/action/navigation'; import { openProfileViewer } from '../../../client/action/navigation';
@ -18,8 +19,7 @@ import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
import SegmentedControls from '../../atoms/segmented-controls/SegmentedControls'; import SegmentedControls from '../../atoms/segmented-controls/SegmentedControls';
import PeopleSelector from '../people-selector/PeopleSelector'; import PeopleSelector from '../people-selector/PeopleSelector';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const PER_PAGE_MEMBER = 50; const PER_PAGE_MEMBER = 50;
@ -129,14 +129,14 @@ function RoomMembers({ roomId }) {
const mList = searchMembers ? searchMembers.data : members.slice(0, itemCount); const mList = searchMembers ? searchMembers.data : members.slice(0, itemCount);
return ( return (
<div className="room-members"> <div className="room-members">
<MenuHeader>{t("Molecules.RoomMembers.search_title")}</MenuHeader> <MenuHeader>{t('Molecules.RoomMembers.search_title')}</MenuHeader>
<Input <Input
onChange={handleSearch} onChange={handleSearch}
placeholder={t("Molecules.RoomMembers.search_placeholder")} placeholder={t('Molecules.RoomMembers.search_placeholder')}
autoFocus autoFocus
/> />
<div className="room-members__header"> <div className="room-members__header">
<MenuHeader>{t("Molecules.RoomMembers.found_members", {count: mList.length})}</MenuHeader> <MenuHeader>{t('Molecules.RoomMembers.found_members', { count: mList.length })}</MenuHeader>
<SegmentedControls <SegmentedControls
selected={ selected={
(() => { (() => {
@ -144,7 +144,7 @@ function RoomMembers({ roomId }) {
return getSegmentIndex[membership]; return getSegmentIndex[membership];
})() })()
} }
segments={[{ text: t("Molecules.RoomMembers.joined") }, { text: t("Molecules.RoomMembers.invited") }, { text: t("Molecules.RoomMembers.banned") }]} segments={[{ text: t('Molecules.RoomMembers.joined') }, { text: t('Molecules.RoomMembers.invited') }, { text: t('Molecules.RoomMembers.banned') }]}
onSelect={(index) => { onSelect={(index) => {
const memberships = ['join', 'invite', 'ban']; const memberships = ['join', 'invite', 'ban'];
setMembership(memberships[index]); setMembership(memberships[index]);
@ -167,7 +167,7 @@ function RoomMembers({ roomId }) {
&& ( && (
<div className="room-members__status"> <div className="room-members__status">
<Text variant="b2"> <Text variant="b2">
{searchMembers ? t("Molecules.RoomMembers.invited", {term: searchMembers.term}) : t("Molecules.RoomMembers.no_members")} {searchMembers ? t('Molecules.RoomMembers.invited', { term: searchMembers.term }) : t('Molecules.RoomMembers.no_members')}
</Text> </Text>
</div> </div>
) )
@ -176,7 +176,7 @@ function RoomMembers({ roomId }) {
mList.length !== 0 mList.length !== 0
&& members.length > itemCount && members.length > itemCount
&& searchMembers === null && searchMembers === null
&& <Button onClick={loadMorePeople}>{t("Molecules.RoomMembers.view_more")}</Button> && <Button onClick={loadMorePeople}>{t('Molecules.RoomMembers.view_more')}</Button>
} }
</div> </div>
</div> </div>

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomNotification.scss'; import './RoomNotification.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
@ -14,8 +15,7 @@ import BellRingIC from '../../../../public/res/ic/outlined/bell-ring.svg';
import BellPingIC from '../../../../public/res/ic/outlined/bell-ping.svg'; import BellPingIC from '../../../../public/res/ic/outlined/bell-ping.svg';
import BellOffIC from '../../../../public/res/ic/outlined/bell-off.svg'; import BellOffIC from '../../../../public/res/ic/outlined/bell-off.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const items = [{ const items = [{
iconSrc: BellIC, iconSrc: BellIC,
@ -117,7 +117,6 @@ function useNotifications(roomId) {
} }
function RoomNotification({ roomId }) { function RoomNotification({ roomId }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [activeType, setNotification] = useNotifications(roomId); const [activeType, setNotification] = useNotifications(roomId);

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -17,8 +18,7 @@ import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function RoomOptions({ roomId, afterOptionSelect }) { function RoomOptions({ roomId, afterOptionSelect }) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -39,9 +39,9 @@ function RoomOptions({ roomId, afterOptionSelect }) {
const handleLeaveClick = async () => { const handleLeaveClick = async () => {
afterOptionSelect(); afterOptionSelect();
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Molecules.RoomOptions.leave.title"), t('Molecules.RoomOptions.leave.title'),
t("Molecules.RoomOptions.leave.subtitle", {room_name: room.name}), t('Molecules.RoomOptions.leave.subtitle', { room_name: room.name }),
t("Molecules.RoomOptions.leave.button_text"), t('Molecules.RoomOptions.leave.button_text'),
'danger', 'danger',
); );
if (!isConfirmed) return; if (!isConfirmed) return;
@ -50,17 +50,17 @@ function RoomOptions({ roomId, afterOptionSelect }) {
return ( return (
<div style={{ maxWidth: '256px' }}> <div style={{ maxWidth: '256px' }}>
<MenuHeader>{twemojify(t("Molecules.RoomOptions.title", {room_name: initMatrix.matrixClient.getRoom(roomId)?.name}))}</MenuHeader> <MenuHeader>{twemojify(t('Molecules.RoomOptions.title', { room_name: initMatrix.matrixClient.getRoom(roomId)?.name }))}</MenuHeader>
<MenuItem iconSrc={TickMarkIC} onClick={handleMarkAsRead}>{t("Molecules.RoomOptions.mark_as_read")}</MenuItem> <MenuItem iconSrc={TickMarkIC} onClick={handleMarkAsRead}>{t('Molecules.RoomOptions.mark_as_read')}</MenuItem>
<MenuItem <MenuItem
iconSrc={AddUserIC} iconSrc={AddUserIC}
onClick={handleInviteClick} onClick={handleInviteClick}
disabled={!canInvite} disabled={!canInvite}
> >
{t("Molecules.RoomOptions.invite")} {t('Molecules.RoomOptions.invite')}
</MenuItem> </MenuItem>
<MenuItem iconSrc={LeaveArrowIC} variant="danger" onClick={handleLeaveClick}>{t("Molecules.RoomOptions.leave.button_text")}</MenuItem> <MenuItem iconSrc={LeaveArrowIC} variant="danger" onClick={handleLeaveClick}>{t('Molecules.RoomOptions.leave.button_text')}</MenuItem>
<MenuHeader>{t("Molecules.RoomOptions.notifications_heading")}</MenuHeader> <MenuHeader>{t('Molecules.RoomOptions.notifications_heading')}</MenuHeader>
<RoomNotification roomId={roomId} /> <RoomNotification roomId={roomId} />
</div> </div>
); );

View file

@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomPermissions.scss'; import './RoomPermissions.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { getPowerLabel } from '../../../util/matrixUtil'; import { getPowerLabel } from '../../../util/matrixUtil';
import { openReusableContextMenu } from '../../../client/action/navigation'; import { openReusableContextMenu } from '../../../client/action/navigation';
@ -17,8 +18,7 @@ import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.s
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const permissionsInfo = { const permissionsInfo = {
users_default: { users_default: {

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomProfile.scss'; import './RoomProfile.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -21,8 +22,7 @@ import { useStore } from '../../hooks/useStore';
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function RoomProfile({ roomId }) { function RoomProfile({ roomId }) {
const isMountStore = useStore(); const isMountStore = useStore();
@ -81,7 +81,7 @@ function RoomProfile({ roomId }) {
const newName = roomNameInput.value; const newName = roomNameInput.value;
if (newName !== roomName && roomName.trim() !== '') { if (newName !== roomName && roomName.trim() !== '') {
setStatus({ setStatus({
msg: t("Molecules.RoomProfile.saving_room_name"), msg: t('Molecules.RoomProfile.saving_room_name'),
type: cons.status.IN_FLIGHT, type: cons.status.IN_FLIGHT,
}); });
await mx.setRoomName(roomId, newName); await mx.setRoomName(roomId, newName);
@ -92,7 +92,7 @@ function RoomProfile({ roomId }) {
if (newTopic !== roomTopic) { if (newTopic !== roomTopic) {
if (isMountStore.getItem()) { if (isMountStore.getItem()) {
setStatus({ setStatus({
msg: t("Molecules.RoomProfile.saving_room_topic"), msg: t('Molecules.RoomProfile.saving_room_topic'),
type: cons.status.IN_FLIGHT, type: cons.status.IN_FLIGHT,
}); });
} }
@ -101,13 +101,13 @@ function RoomProfile({ roomId }) {
} }
if (!isMountStore.getItem()) return; if (!isMountStore.getItem()) return;
setStatus({ setStatus({
msg: t("Molecules.RoomProfile.save_success"), msg: t('Molecules.RoomProfile.save_success'),
type: cons.status.SUCCESS, type: cons.status.SUCCESS,
}); });
} catch (err) { } catch (err) {
if (!isMountStore.getItem()) return; if (!isMountStore.getItem()) return;
setStatus({ setStatus({
msg: err.message || t("Molecules.RoomProfile.save_failed"), msg: err.message || t('Molecules.RoomProfile.save_failed'),
type: cons.status.ERROR, type: cons.status.ERROR,
}); });
} }
@ -124,9 +124,9 @@ function RoomProfile({ roomId }) {
const handleAvatarUpload = async (url) => { const handleAvatarUpload = async (url) => {
if (url === null) { if (url === null) {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Molecules.RoomProfile.remove_avatar_title"), t('Molecules.RoomProfile.remove_avatar_title'),
t("Molecules.RoomProfile.remove_avatar_subtitle"), t('Molecules.RoomProfile.remove_avatar_subtitle'),
t("Molecules.RoomProfile.remove_avatar_button"), t('Molecules.RoomProfile.remove_avatar_button'),
'caution', 'caution',
); );
if (isConfirmed) { if (isConfirmed) {
@ -137,20 +137,25 @@ function RoomProfile({ roomId }) {
const renderEditNameAndTopic = () => ( const renderEditNameAndTopic = () => (
<form className="room-profile__edit-form" onSubmit={handleOnSubmit}> <form className="room-profile__edit-form" onSubmit={handleOnSubmit}>
{canChangeName && <Input value={roomName} name="room-name" disabled={status.type === cons.status.IN_FLIGHT} label={t("Molecules.RoomProfile.name_label")} required />} {canChangeName && <Input value={roomName} name="room-name" disabled={status.type === cons.status.IN_FLIGHT} label={t('Molecules.RoomProfile.name_label')} required />}
{canChangeTopic && <Input value={roomTopic} name="room-topic" disabled={status.type === cons.status.IN_FLIGHT} minHeight={100} resizable label={t("Molecules.RoomProfile.topic_label")} />} {canChangeTopic && <Input value={roomTopic} name="room-topic" disabled={status.type === cons.status.IN_FLIGHT} minHeight={100} resizable label={t('Molecules.RoomProfile.topic_label')} />}
{(!canChangeName || !canChangeTopic) && <Text variant="b3">{ {(!canChangeName || !canChangeTopic) && (
room.isSpaceRoom() ? <Text variant="b3">
canChangeName ? "Molecules.RoomProfile.permission_change_space_name" : "Molecules.RoomProfile.permission_change_space_topic" : {
canChangeName ? "Molecules.RoomProfile.permission_change_room_name": "Molecules.RoomProfile.permission_change_room_topic" // eslint-disable-next-line no-nested-ternary
}</Text>} room.isSpaceRoom()
? canChangeName ? 'Molecules.RoomProfile.permission_change_space_name' : 'Molecules.RoomProfile.permission_change_space_topic'
: canChangeName ? 'Molecules.RoomProfile.permission_change_room_name' : 'Molecules.RoomProfile.permission_change_room_topic'
}
</Text>
)}
{ status.type === cons.status.IN_FLIGHT && <Text variant="b2">{status.msg}</Text>} { status.type === cons.status.IN_FLIGHT && <Text variant="b2">{status.msg}</Text>}
{ status.type === cons.status.SUCCESS && <Text style={{ color: 'var(--tc-positive-high)' }} variant="b2">{status.msg}</Text>} { status.type === cons.status.SUCCESS && <Text style={{ color: 'var(--tc-positive-high)' }} variant="b2">{status.msg}</Text>}
{ status.type === cons.status.ERROR && <Text style={{ color: 'var(--tc-danger-high)' }} variant="b2">{status.msg}</Text>} { status.type === cons.status.ERROR && <Text style={{ color: 'var(--tc-danger-high)' }} variant="b2">{status.msg}</Text>}
{ status.type !== cons.status.IN_FLIGHT && ( { status.type !== cons.status.IN_FLIGHT && (
<div> <div>
<Button type="submit" variant="primary">{t("common.save")}</Button> <Button type="submit" variant="primary">{t('common.save')}</Button>
<Button onClick={handleCancelEditing}>{t("common.cancel")}</Button> <Button onClick={handleCancelEditing}>{t('common.cancel')}</Button>
</div> </div>
)} )}
</form> </form>
@ -164,7 +169,7 @@ function RoomProfile({ roomId }) {
<IconButton <IconButton
src={PencilIC} src={PencilIC}
size="extra-small" size="extra-small"
tooltip={t("common.edit")} tooltip={t('common.edit')}
onClick={() => setIsEditing(true)} onClick={() => setIsEditing(true)}
/> />
)} )}

View file

@ -4,6 +4,7 @@ import './RoomSearch.scss';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import { selectRoom } from '../../../client/action/navigation'; import { selectRoom } from '../../../client/action/navigation';
@ -20,8 +21,7 @@ import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const roomIdToBackup = new Map(); const roomIdToBackup = new Map();
@ -144,37 +144,37 @@ function RoomSearch({ roomId }) {
return ( return (
<div className="room-search"> <div className="room-search">
<form className="room-search__form" onSubmit={handleSearch}> <form className="room-search__form" onSubmit={handleSearch}>
<MenuHeader>{t("Molecules.RoomSearch.title")}</MenuHeader> <MenuHeader>{t('Molecules.RoomSearch.title')}</MenuHeader>
<div> <div>
<Input <Input
placeholder={t("Molecules.RoomSearch.placeholder")} placeholder={t('Molecules.RoomSearch.placeholder')}
name="room-search-input" name="room-search-input"
disabled={isRoomEncrypted} disabled={isRoomEncrypted}
autoFocus autoFocus
/> />
<Button iconSrc={SearchIC} variant="primary" type="submit">{t("Molecules.RoomSearch.search_button")}</Button> <Button iconSrc={SearchIC} variant="primary" type="submit">{t('Molecules.RoomSearch.search_button')}</Button>
</div> </div>
{searchData?.results.length > 0 && ( {searchData?.results.length > 0 && (
<Text>{t("Molecules.RoomSearch.results", {count: searchData.count, term: searchTerm})}</Text> <Text>{t('Molecules.RoomSearch.results', { count: searchData.count, term: searchTerm })}</Text>
)} )}
{!isRoomEncrypted && searchData === null && ( {!isRoomEncrypted && searchData === null && (
<div className="room-search__help"> <div className="room-search__help">
{status.type === cons.status.IN_FLIGHT && <Spinner />} {status.type === cons.status.IN_FLIGHT && <Spinner />}
{status.type === cons.status.IN_FLIGHT && <Text>{t("Molecules.RoomSearch.searching")}</Text>} {status.type === cons.status.IN_FLIGHT && <Text>{t('Molecules.RoomSearch.searching')}</Text>}
{status.type === cons.status.PRE_FLIGHT && <RawIcon src={SearchIC} size="large" />} {status.type === cons.status.PRE_FLIGHT && <RawIcon src={SearchIC} size="large" />}
{status.type === cons.status.PRE_FLIGHT && <Text>{t("Molecules.RoomSearch.subtitle")}</Text>} {status.type === cons.status.PRE_FLIGHT && <Text>{t('Molecules.RoomSearch.subtitle')}</Text>}
{status.type === cons.status.ERROR && <Text>{t("Molecules.RoomSearch.failed")}</Text>} {status.type === cons.status.ERROR && <Text>{t('Molecules.RoomSearch.failed')}</Text>}
</div> </div>
)} )}
{!isRoomEncrypted && searchData?.results.length === 0 && ( {!isRoomEncrypted && searchData?.results.length === 0 && (
<div className="room-search__help"> <div className="room-search__help">
<Text>{t("Molecules.RoomSearch.no_results")}</Text> <Text>{t('Molecules.RoomSearch.no_results')}</Text>
</div> </div>
)} )}
{isRoomEncrypted && ( {isRoomEncrypted && (
<div className="room-search__help"> <div className="room-search__help">
<Text>{t("Molecules.RoomSearch.encrypted_room")}</Text> <Text>{t('Molecules.RoomSearch.encrypted_room')}</Text>
</div> </div>
)} )}
</form> </form>
@ -189,7 +189,7 @@ function RoomSearch({ roomId }) {
{searchData?.next_batch && ( {searchData?.next_batch && (
<div className="room-search__more"> <div className="room-search__more">
{status.type !== cons.status.IN_FLIGHT && ( {status.type !== cons.status.IN_FLIGHT && (
<Button onClick={paginate}>{t("Molecules.RoomSearch.load_more")}</Button> <Button onClick={paginate}>{t('Molecules.RoomSearch.load_more')}</Button>
)} )}
{status.type === cons.status.IN_FLIGHT && <Spinner />} {status.type === cons.status.IN_FLIGHT && <Spinner />}
</div> </div>

View file

@ -2,18 +2,22 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomTile.scss'; import './RoomTile.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import Avatar from '../../atoms/avatar/Avatar'; import Avatar from '../../atoms/avatar/Avatar';
import { t } from 'i18next';
import '../../i18n';
function RoomTile({ function RoomTile({
avatarSrc, name, id, avatarSrc, name, id,
inviterName, memberCount, desc, options, inviterName, memberCount, desc, options,
}) { }) {
const { t } = useTranslation();
return ( return (
<div className="room-tile"> <div className="room-tile">
<div className="room-tile__avatar"> <div className="room-tile__avatar">
@ -28,8 +32,8 @@ function RoomTile({
<Text variant="b3"> <Text variant="b3">
{ {
inviterName !== null inviterName !== null
? t("Molecules.RoomTile.invited_by_user", {inviter: inviterName, count: memberCount || 0, id: id}) ? t('Molecules.RoomTile.invited_by_user', { inviter: inviterName, count: memberCount || 0, id })
: t("Molecules.RoomTile.invited", {count: memberCount || 0, id: id}) : t('Molecules.RoomTile.invited', { count: memberCount || 0, id })
} }
</Text> </Text>
{ {

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomVisibility.scss'; import './RoomVisibility.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
@ -15,8 +16,7 @@ import SpaceIC from '../../../../public/res/ic/outlined/space.svg';
import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg'; import SpaceLockIC from '../../../../public/res/ic/outlined/space-lock.svg';
import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg'; import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const visibility = { const visibility = {
INVITE: 'invite', INVITE: 'invite',

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './SpaceAddExisting.scss'; import './SpaceAddExisting.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -25,9 +26,7 @@ import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function SpaceAddExistingContent({ roomId }) { function SpaceAddExistingContent({ roomId }) {
const mountStore = useStore(roomId); const mountStore = useStore(roomId);
@ -41,7 +40,6 @@ function SpaceAddExistingContent({ roomId }) {
spaces, rooms, directs, roomIdToParents, spaces, rooms, directs, roomIdToParents,
} = initMatrix.roomList; } = initMatrix.roomList;
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
@ -66,7 +64,7 @@ function SpaceAddExistingContent({ roomId }) {
}; };
const handleAdd = async () => { const handleAdd = async () => {
setProcess(t("Molecules.SpaceAddExisting.adding_items", {count: selected.length})); setProcess(t('Molecules.SpaceAddExisting.adding_items', { count: selected.length }));
const promises = selected.map((rId) => { const promises = selected.map((rId) => {
const room = mx.getRoom(rId); const room = mx.getRoom(rId);
@ -126,12 +124,12 @@ function SpaceAddExistingContent({ roomId }) {
<Input <Input
name="searchInput" name="searchInput"
onChange={handleSearch} onChange={handleSearch}
placeholder={t("Molecules.SpaceAddExisting.search_rooms_placeholder")} placeholder={t('Molecules.SpaceAddExisting.search_rooms_placeholder')}
autoFocus autoFocus
/> />
<IconButton size="small" type="button" onClick={handleSearchClear} src={CrossIC} /> <IconButton size="small" type="button" onClick={handleSearchClear} src={CrossIC} />
</form> </form>
{searchIds?.length === 0 && <Text>{t("Molecules.SpaceAddExisting.no_results")}</Text>} {searchIds?.length === 0 && <Text>{t('Molecules.SpaceAddExisting.no_results')}</Text>}
{ {
(searchIds || allRoomIds).map((rId) => { (searchIds || allRoomIds).map((rId) => {
const room = mx.getRoom(rId); const room = mx.getRoom(rId);
@ -178,9 +176,9 @@ function SpaceAddExistingContent({ roomId }) {
{selected.length !== 0 && ( {selected.length !== 0 && (
<div className="space-add-existing__footer"> <div className="space-add-existing__footer">
{process && <Spinner size="small" />} {process && <Spinner size="small" />}
<Text weight="medium">{t("Molecules.SpaceAddExisting.items_selected", {count: selected.length})}</Text> <Text weight="medium">{t('Molecules.SpaceAddExisting.items_selected', { count: selected.length })}</Text>
{ !process && ( { !process && (
<Button onClick={handleAdd} variant="primary">{t("Molecules.SpaceAddExisting.add_button")}</Button> <Button onClick={handleAdd} variant="primary">{t('Molecules.SpaceAddExisting.add_button')}</Button>
)} )}
</div> </div>
)} )}
@ -212,6 +210,8 @@ function SpaceAddExisting() {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
const { t } = useTranslation();
return ( return (
<Dialog <Dialog
isOpen={roomId !== null} isOpen={roomId !== null}
@ -219,10 +219,15 @@ function SpaceAddExisting() {
title={( title={(
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{roomId && twemojify(room.name)} {roomId && twemojify(room.name)}
<span style={{ color: 'var(--tc-surface-low)' }}> {t("Molecules.SpaceAddExisting.subtitle")}</span> <span style={{ color: 'var(--tc-surface-low)' }}>
{' '}
{' '}
{t('Molecules.SpaceAddExisting.subtitle')}
</span>
</Text> </Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t('common.close')} />}
onRequestClose={requestClose} onRequestClose={requestClose}
> >
{ {

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -26,8 +27,7 @@ import PinFilledIC from '../../../../public/res/ic/filled/pin.svg';
import { confirmDialog } from '../confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function SpaceOptions({ roomId, afterOptionSelect }) { function SpaceOptions({ roomId, afterOptionSelect }) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -64,9 +64,9 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
const handleLeaveClick = async () => { const handleLeaveClick = async () => {
afterOptionSelect(); afterOptionSelect();
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Molecules.SpaceOptions.leave_space"), t('Molecules.SpaceOptions.leave_space'),
t("Molecules.SpaceOptions.leave_space_confirmation", {space: room.name}), t('Molecules.SpaceOptions.leave_space_confirmation', { space: room.name }),
t("Molecules.SpaceOptions.leave_space_confirmation"), t('Molecules.SpaceOptions.leave_space_confirmation'),
'danger', 'danger',
); );
if (!isConfirmed) return; if (!isConfirmed) return;
@ -80,29 +80,29 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
onClick={handleCategorizeClick} onClick={handleCategorizeClick}
iconSrc={isCategorized ? CategoryFilledIC : CategoryIC} iconSrc={isCategorized ? CategoryFilledIC : CategoryIC}
> >
{isCategorized ? t("Organisms.SpaceSettings.uncategorize_subspaces") : t("Organisms.SpaceSettings.categorize_subspaces")} {isCategorized ? t('Organisms.SpaceSettings.uncategorize_subspaces') : t('Organisms.SpaceSettings.categorize_subspaces')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={handlePinClick} onClick={handlePinClick}
iconSrc={isPinned ? PinFilledIC : PinIC} iconSrc={isPinned ? PinFilledIC : PinIC}
> >
{isPinned ? t("Organisms.SpaceSettings.unpin_sidebar") : t("Organisms.SpaceSettings.pin_sidebar")} {isPinned ? t('Organisms.SpaceSettings.unpin_sidebar') : t('Organisms.SpaceSettings.pin_sidebar')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
iconSrc={AddUserIC} iconSrc={AddUserIC}
onClick={handleInviteClick} onClick={handleInviteClick}
disabled={!canInvite} disabled={!canInvite}
> >
{t("Molecules.SpaceOptions.invite")} {t('Molecules.SpaceOptions.invite')}
</MenuItem> </MenuItem>
<MenuItem onClick={handleManageRoom} iconSrc={HashSearchIC}>{t("Molecules.SpaceOptions.manage_rooms")}</MenuItem> <MenuItem onClick={handleManageRoom} iconSrc={HashSearchIC}>{t('Molecules.SpaceOptions.manage_rooms')}</MenuItem>
<MenuItem onClick={handleSettingsClick} iconSrc={SettingsIC}>{t("Molecules.SpaceOptions.settings")}</MenuItem> <MenuItem onClick={handleSettingsClick} iconSrc={SettingsIC}>{t('Molecules.SpaceOptions.settings')}</MenuItem>
<MenuItem <MenuItem
variant="danger" variant="danger"
onClick={handleLeaveClick} onClick={handleLeaveClick}
iconSrc={LeaveArrowIC} iconSrc={LeaveArrowIC}
> >
{t("Molecules.SpaceOptions.leave")} {t('Molecules.SpaceOptions.leave')}
</MenuItem> </MenuItem>
</div> </div>
); );

View file

@ -2,15 +2,15 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './SSOButtons.scss'; import './SSOButtons.scss';
import { useTranslation } from 'react-i18next';
import { createTemporaryClient, startSsoLogin } from '../../../client/action/auth'; import { createTemporaryClient, startSsoLogin } from '../../../client/action/auth';
import Button from '../../atoms/button/Button'; import Button from '../../atoms/button/Button';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function SSOButtons({ type, identityProviders, baseUrl }) { function SSOButtons({ type, identityProviders, baseUrl }) {
const tempClient = createTemporaryClient(baseUrl); const tempClient = createTemporaryClient(baseUrl);
const { t } = useTranslation(); const { t } = useTranslation();
function handleClick(id) { function handleClick(id) {
startSsoLogin(baseUrl, type, id); startSsoLogin(baseUrl, type, id);
@ -28,7 +28,7 @@ function SSOButtons({ type, identityProviders, baseUrl }) {
<button key={idp.id} type="button" className="sso-btn" onClick={() => handleClick(idp.id)}> <button key={idp.id} type="button" className="sso-btn" onClick={() => handleClick(idp.id)}>
<img className="sso-btn__img" src={tempClient.mxcUrlToHttp(idp.icon)} alt={idp.name} /> <img className="sso-btn__img" src={tempClient.mxcUrlToHttp(idp.icon)} alt={idp.name} />
</button> </button>
) : <Button key={idp.id} className="sso-btn__text-only" onClick={() => handleClick(idp.id)}>{t("Molecules.SSOButtons.login_with", {idp_name: idp.name})}</Button> ) : <Button key={idp.id} className="sso-btn__text-only" onClick={() => handleClick(idp.id)}>{t('Molecules.SSOButtons.login_with', { idp_name: idp.name })}</Button>
))} ))}
</div> </div>
); );

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './CreateRoom.scss'; import './CreateRoom.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
@ -33,12 +34,9 @@ import SpaceGlobeIC from '../../../../public/res/ic/outlined/space-globe.svg';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function CreateRoomContent({ isSpace, parentId, onRequestClose }) { function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [joinRule, setJoinRule] = useState(parentId ? 'restricted' : 'invite'); const [joinRule, setJoinRule] = useState(parentId ? 'restricted' : 'invite');
@ -137,8 +135,8 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
}; };
const joinRules = ['invite', 'restricted', 'public']; const joinRules = ['invite', 'restricted', 'public'];
const joinRuleShortText = [ t("Organisms.CreateRoom.private_room_short"), t("Organisms.CreateRoom.restricted_room_short"), t("Organisms.CreateRoom.public_room_short")]; const joinRuleShortText = [t('Organisms.CreateRoom.private_room_short'), t('Organisms.CreateRoom.restricted_room_short'), t('Organisms.CreateRoom.public_room_short')];
const joinRuleText = [ t("Organisms.CreateRoom.private_room_long"), t("Organisms.CreateRoom.restricted_room_long"), t("Organisms.CreateRoom.public_room_long")]; const joinRuleText = [t('Organisms.CreateRoom.private_room_long'), t('Organisms.CreateRoom.restricted_room_long'), t('Organisms.CreateRoom.public_room_long')];
const jrRoomIC = [HashLockIC, HashIC, HashGlobeIC]; const jrRoomIC = [HashLockIC, HashIC, HashGlobeIC];
const jrSpaceIC = [SpaceLockIC, SpaceIC, SpaceGlobeIC]; const jrSpaceIC = [SpaceLockIC, SpaceIC, SpaceGlobeIC];
const handleJoinRule = (evt) => { const handleJoinRule = (evt) => {
@ -147,7 +145,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
getEventCords(evt, '.btn-surface'), getEventCords(evt, '.btn-surface'),
(closeMenu) => ( (closeMenu) => (
<> <>
<MenuHeader>{t("Organisms.CreateRoom.visibility_message")}</MenuHeader> <MenuHeader>{t('Organisms.CreateRoom.visibility_message')}</MenuHeader>
{ {
joinRules.map((rule) => ( joinRules.map((rule) => (
<MenuItem <MenuItem
@ -174,17 +172,17 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
<div className="create-room"> <div className="create-room">
<form className="create-room__form" onSubmit={handleSubmit}> <form className="create-room__form" onSubmit={handleSubmit}>
<SettingTile <SettingTile
title={t("Organisms.CreateRoom.visibility_title")} title={t('Organisms.CreateRoom.visibility_title')}
options={( options={(
<Button onClick={handleJoinRule} iconSrc={ChevronBottomIC}> <Button onClick={handleJoinRule} iconSrc={ChevronBottomIC}>
{joinRuleShortText[joinRules.indexOf(joinRule)]} {joinRuleShortText[joinRules.indexOf(joinRule)]}
</Button> </Button>
)} )}
content={<Text variant="b3">{isSpace ? t("Organisms.CreateRoom.select_who_can_join_space") : t("Organisms.CreateRoom.select_who_can_join_room")}</Text>} content={<Text variant="b3">{isSpace ? t('Organisms.CreateRoom.select_who_can_join_space') : t('Organisms.CreateRoom.select_who_can_join_room')}</Text>}
/> />
{joinRule === 'public' && ( {joinRule === 'public' && (
<div> <div>
<Text className="create-room__address__label" variant="b2">{isSpace ? t("Organisms.CreateRoom.space_address") : t("Organisms.CreateRoom.room_address")}</Text> <Text className="create-room__address__label" variant="b2">{isSpace ? t('Organisms.CreateRoom.space_address') : t('Organisms.CreateRoom.room_address')}</Text>
<div className="create-room__address"> <div className="create-room__address">
<Text variant="b1">#</Text> <Text variant="b1">#</Text>
<Input <Input
@ -197,32 +195,40 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
/> />
<Text variant="b1">{`:${userHs}`}</Text> <Text variant="b1">{`:${userHs}`}</Text>
</div> </div>
{isValidAddress === false && <Text className="create-room__address__tip" variant="b3"><span style={{ color: 'var(--bg-danger)' }}>{ t("Organisms.CreateRoom.room_address_already_in_use", {room_address: `#${addressValue}:${userHs}`})}</span></Text>} {isValidAddress === false && <Text className="create-room__address__tip" variant="b3"><span style={{ color: 'var(--bg-danger)' }}>{ t('Organisms.CreateRoom.room_address_already_in_use', { room_address: `#${addressValue}:${userHs}` })}</span></Text>}
</div> </div>
)} )}
{!isSpace && joinRule !== 'public' && ( {!isSpace && joinRule !== 'public' && (
<SettingTile <SettingTile
title={t("Organisms.CreateRoom.e2e_title")} title={t('Organisms.CreateRoom.e2e_title')}
options={<Toggle isActive={isEncrypted} onToggle={setIsEncrypted} />} options={<Toggle isActive={isEncrypted} onToggle={setIsEncrypted} />}
content={<Text variant="b3"> {t("Organisms.CreateRoom.e2e_message")}</Text>} content={(
<Text variant="b3">
{' '}
{t('Organisms.CreateRoom.e2e_message')}
</Text>
)}
/> />
)} )}
<SettingTile <SettingTile
title={t("Organisms.CreateRoom.role_title")} title={t('Organisms.CreateRoom.role_title')}
options={( options={(
<SegmentControl <SegmentControl
selected={roleIndex} selected={roleIndex}
segments={[{ text: t("Organisms.CreateRoom.role_admin")}, { text: t("Organisms.CreateRoom.role_founder")}]} segments={[{ text: t('Organisms.CreateRoom.role_admin') }, { text: t('Organisms.CreateRoom.role_founder') }]}
onSelect={setRoleIndex} onSelect={setRoleIndex}
/> />
)} )}
content={( content={(
<Text variant="b3"> {t("Organisms.CreateRoom.role_message")}</Text> <Text variant="b3">
{' '}
{t('Organisms.CreateRoom.role_message')}
</Text>
)} )}
/> />
<Input name="topic" minHeight={174} resizable label= {t("Organisms.CreateRoom.topic_label")}/> <Input name="topic" minHeight={174} resizable label={t('Organisms.CreateRoom.topic_label')} />
<div className="create-room__name-wrapper"> <div className="create-room__name-wrapper">
<Input name="name" label={isSpace ? t("Organisms.CreateRoom.space_name"): t("Organisms.CreateRoom.room_name")} required /> <Input name="name" label={isSpace ? t('Organisms.CreateRoom.space_name') : t('Organisms.CreateRoom.room_name')} required />
<Button <Button
disabled={isValidAddress === false || isCreatingRoom} disabled={isValidAddress === false || isCreatingRoom}
iconSrc={isSpace ? SpacePlusIC : HashPlusIC} iconSrc={isSpace ? SpacePlusIC : HashPlusIC}
@ -235,7 +241,7 @@ function CreateRoomContent({ isSpace, parentId, onRequestClose }) {
{isCreatingRoom && ( {isCreatingRoom && (
<div className="create-room__loading"> <div className="create-room__loading">
<Spinner size="small" /> <Spinner size="small" />
<Text>{ isSpace ? t("Organisms.CreateRoom.creating_space") : t("Organisms.CreateRoom.creating_room")}</Text> <Text>{ isSpace ? t('Organisms.CreateRoom.creating_space') : t('Organisms.CreateRoom.creating_room')}</Text>
</div> </div>
)} )}
{typeof creatingError === 'string' && <Text className="create-room__error" variant="b3">{creatingError}</Text>} {typeof creatingError === 'string' && <Text className="create-room__error" variant="b3">{creatingError}</Text>}
@ -279,18 +285,20 @@ function CreateRoom() {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const room = mx.getRoom(parentId); const room = mx.getRoom(parentId);
const { t } = useTranslation();
return ( return (
<Dialog <Dialog
isOpen={create !== null} isOpen={create !== null}
title={( title={(
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{parentId ? twemojify(room.name) : t("Organisms.CreateRoom.home")} {parentId ? twemojify(room.name) : t('Organisms.CreateRoom.home')}
<span style={{ color: 'var(--tc-surface-low)' }}> <span style={{ color: 'var(--tc-surface-low)' }}>
{`${isSpace ? t("Organisms.CreateRoom.create_space") : t("Organisms.CreateRoom.create_room")}`} {`${isSpace ? t('Organisms.CreateRoom.create_space') : t('Organisms.CreateRoom.create_room')}`}
</span> </span>
</Text> </Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t('common.close')} />}
onRequestClose={onRequestClose} onRequestClose={onRequestClose}
> >
{ {

View file

@ -2,14 +2,14 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './DragDrop.scss'; import './DragDrop.scss';
import { useTranslation } from 'react-i18next';
import RawModal from '../../atoms/modal/RawModal'; import RawModal from '../../atoms/modal/RawModal';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function DragDrop({ isOpen }) { function DragDrop({ isOpen }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -18,7 +18,7 @@ function DragDrop({ isOpen }) {
overlayClassName="drag-drop__overlay" overlayClassName="drag-drop__overlay"
isOpen={isOpen} isOpen={isOpen}
> >
<Text variant="h2" weight="medium">{t("Organisms.DragDrop.drop_file_to_upload_prompt")}</Text> <Text variant="h2" weight="medium">{t('Organisms.DragDrop.drop_file_to_upload_prompt')}</Text>
</RawModal> </RawModal>
); );
} }

View file

@ -2,6 +2,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './EmojiVerification.scss'; import './EmojiVerification.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -20,9 +21,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import { accessSecretStorage } from '../settings/SecretStorageAccess'; import { accessSecretStorage } from '../settings/SecretStorageAccess';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function EmojiVerificationContent({ data, requestClose }) { function EmojiVerificationContent({ data, requestClose }) {
const [sas, setSas] = useState(null); const [sas, setSas] = useState(null);
@ -100,14 +99,14 @@ function EmojiVerificationContent({ data, requestClose }) {
const renderWait = () => ( const renderWait = () => (
<> <>
<Spinner size="small" /> <Spinner size="small" />
<Text>{t("Organisms.EmojiVerification.waiting_for_response")}</Text> <Text>{t('Organisms.EmojiVerification.waiting_for_response')}</Text>
</> </>
); );
if (sas !== null) { if (sas !== null) {
return ( return (
<div className="emoji-verification__content"> <div className="emoji-verification__content">
<Text>{t("Organisms.EmojiVerification.confirmation_prompt")}</Text> <Text>{t('Organisms.EmojiVerification.confirmation_prompt')}</Text>
<div className="emoji-verification__emojis"> <div className="emoji-verification__emojis">
{sas.sas.emoji.map((emoji, i) => ( {sas.sas.emoji.map((emoji, i) => (
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
@ -120,8 +119,8 @@ function EmojiVerificationContent({ data, requestClose }) {
<div className="emoji-verification__buttons"> <div className="emoji-verification__buttons">
{process ? renderWait() : ( {process ? renderWait() : (
<> <>
<Button variant="primary" onClick={sasConfirm}>{t("Organisms.EmojiVerification.emojis_match_button")}</Button> <Button variant="primary" onClick={sasConfirm}>{t('Organisms.EmojiVerification.emojis_match_button')}</Button>
<Button onClick={sasMismatch}>{t("Organisms.EmojiVerification.emojis_dont_match_button")}</Button> <Button onClick={sasMismatch}>{t('Organisms.EmojiVerification.emojis_dont_match_button')}</Button>
</> </>
)} )}
</div> </div>
@ -132,7 +131,7 @@ function EmojiVerificationContent({ data, requestClose }) {
if (targetDevice) { if (targetDevice) {
return ( return (
<div className="emoji-verification__content"> <div className="emoji-verification__content">
<Text>{t("Organisms.EmojiVerification.accept_request_from_other_device_message")}</Text> <Text>{t('Organisms.EmojiVerification.accept_request_from_other_device_message')}</Text>
<div className="emoji-verification__buttons"> <div className="emoji-verification__buttons">
{renderWait()} {renderWait()}
</div> </div>
@ -142,12 +141,12 @@ function EmojiVerificationContent({ data, requestClose }) {
return ( return (
<div className="emoji-verification__content"> <div className="emoji-verification__content">
<Text>{t("Organisms.EmojiVerification.begin_verification_process_message")}</Text> <Text>{t('Organisms.EmojiVerification.begin_verification_process_message')}</Text>
<div className="emoji-verification__buttons"> <div className="emoji-verification__buttons">
{ {
process process
? renderWait() ? renderWait()
: <Button variant="primary" onClick={beginVerification}>{t("Organisms.EmojiVerification.begin_verification_button_text")}</Button> : <Button variant="primary" onClick={beginVerification}>{t('Organisms.EmojiVerification.begin_verification_button_text')}</Button>
} }
</div> </div>
</div> </div>
@ -182,16 +181,18 @@ function useVisibilityToggle() {
function EmojiVerification() { function EmojiVerification() {
const [data, requestClose] = useVisibilityToggle(); const [data, requestClose] = useVisibilityToggle();
const { t } = useTranslation();
return ( return (
<Dialog <Dialog
isOpen={data !== null} isOpen={data !== null}
className="emoji-verification" className="emoji-verification"
title={( title={(
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{t("Organisms.EmojiVerification.title")} {t('Organisms.EmojiVerification.title')}
</Text> </Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t('common.close')} />}
onRequestClose={requestClose} onRequestClose={requestClose}
> >
{ {

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './InviteList.scss'; import './InviteList.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import * as roomActions from '../../../client/action/room'; import * as roomActions from '../../../client/action/room';
@ -16,11 +17,7 @@ import RoomTile from '../../molecules/room-tile/RoomTile';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function InviteList({ isOpen, onRequestClose }) { function InviteList({ isOpen, onRequestClose }) {
const [procInvite, changeProcInvite] = useState(new Set()); const [procInvite, changeProcInvite] = useState(new Set());
@ -81,8 +78,8 @@ function InviteList({ isOpen, onRequestClose }) {
? (<Spinner size="small" />) ? (<Spinner size="small" />)
: ( : (
<div className="invite-btn__container"> <div className="invite-btn__container">
<Button onClick={() => rejectInvite(myRoom.roomId)}>{t("Organisms.InviteList.reject_invite")}</Button> <Button onClick={() => rejectInvite(myRoom.roomId)}>{t('Organisms.InviteList.reject_invite')}</Button>
<Button onClick={() => acceptInvite(myRoom.roomId)} variant="primary">{t("Organisms.InviteList.accept_invite")}</Button> <Button onClick={() => acceptInvite(myRoom.roomId)} variant="primary">{t('Organisms.InviteList.accept_invite')}</Button>
</div> </div>
) )
} }
@ -93,14 +90,14 @@ function InviteList({ isOpen, onRequestClose }) {
return ( return (
<PopupWindow <PopupWindow
isOpen={isOpen} isOpen={isOpen}
title={t("Organisms.InviteList.title")} title={t('Organisms.InviteList.title')}
contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t('common.close')} />}
onRequestClose={onRequestClose} onRequestClose={onRequestClose}
> >
<div className="invites-content"> <div className="invites-content">
{ initMatrix.roomList.inviteDirects.size !== 0 && ( { initMatrix.roomList.inviteDirects.size !== 0 && (
<div className="invites-content__subheading"> <div className="invites-content__subheading">
<Text variant="b3" weight="bold">{t("Organisms.InviteList.direct_messages_title")}</Text> <Text variant="b3" weight="bold">{t('Organisms.InviteList.direct_messages_title')}</Text>
</div> </div>
)} )}
{ {
@ -118,8 +115,8 @@ function InviteList({ isOpen, onRequestClose }) {
? (<Spinner size="small" />) ? (<Spinner size="small" />)
: ( : (
<div className="invite-btn__container"> <div className="invite-btn__container">
<Button onClick={() => rejectInvite(myRoom.roomId, true)}>{t("Organisms.InviteList.reject_invite")}</Button> <Button onClick={() => rejectInvite(myRoom.roomId, true)}>{t('Organisms.InviteList.reject_invite')}</Button>
<Button onClick={() => acceptInvite(myRoom.roomId, true)} variant="primary">{t("Organisms.InviteList.accept_invite")}</Button> <Button onClick={() => acceptInvite(myRoom.roomId, true)} variant="primary">{t('Organisms.InviteList.accept_invite')}</Button>
</div> </div>
) )
} }
@ -129,14 +126,14 @@ function InviteList({ isOpen, onRequestClose }) {
} }
{ initMatrix.roomList.inviteSpaces.size !== 0 && ( { initMatrix.roomList.inviteSpaces.size !== 0 && (
<div className="invites-content__subheading"> <div className="invites-content__subheading">
<Text variant="b3" weight="bold">{t("Organisms.InviteList.spaces_title")}</Text> <Text variant="b3" weight="bold">{t('Organisms.InviteList.spaces_title')}</Text>
</div> </div>
)} )}
{ Array.from(initMatrix.roomList.inviteSpaces).map(renderRoomTile) } { Array.from(initMatrix.roomList.inviteSpaces).map(renderRoomTile) }
{ initMatrix.roomList.inviteRooms.size !== 0 && ( { initMatrix.roomList.inviteRooms.size !== 0 && (
<div className="invites-content__subheading"> <div className="invites-content__subheading">
<Text variant="b3" weight="bold">{t("Organisms.InviteList.rooms_title")}</Text> <Text variant="b3" weight="bold">{t('Organisms.InviteList.rooms_title')}</Text>
</div> </div>
)} )}
{ Array.from(initMatrix.roomList.inviteRooms).map(renderRoomTile) } { Array.from(initMatrix.roomList.inviteRooms).map(renderRoomTile) }

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './InviteUser.scss'; import './InviteUser.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import * as roomActions from '../../../client/action/room'; import * as roomActions from '../../../client/action/room';
@ -19,9 +20,7 @@ import RoomTile from '../../molecules/room-tile/RoomTile';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import UserIC from '../../../../public/res/ic/outlined/user.svg'; import UserIC from '../../../../public/res/ic/outlined/user.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function InviteUser({ function InviteUser({
isOpen, roomId, searchTerm, onRequestClose, isOpen, roomId, searchTerm, onRequestClose,
@ -88,7 +87,7 @@ function InviteUser({
avatar_url: result.avatar_url, avatar_url: result.avatar_url,
}]); }]);
} catch (e) { } catch (e) {
updateSearchQuery({error: t("Organisms.InviteUser.user_not_found", {user_name: inputUsername})}); updateSearchQuery({ error: t('Organisms.InviteUser.user_not_found', { user_name: inputUsername }) });
} }
} else { } else {
try { try {
@ -97,13 +96,13 @@ function InviteUser({
limit: 20, limit: 20,
}); });
if (result.results.length === 0) { if (result.results.length === 0) {
updateSearchQuery({ error: t("Organisms.InviteUser.no_matches_found", {user_name: inputUsername})}); updateSearchQuery({ error: t('Organisms.InviteUser.no_matches_found', { user_name: inputUsername }) });
updateIsSearching(false); updateIsSearching(false);
return; return;
} }
updateUsers(result.results); updateUsers(result.results);
} catch (e) { } catch (e) {
updateSearchQuery({ error: t("errors.generic")}); updateSearchQuery({ error: t('errors.generic') });
} }
} }
updateIsSearching(false); updateIsSearching(false);
@ -112,9 +111,7 @@ function InviteUser({
async function hasDevices(userId) { async function hasDevices(userId) {
try { try {
const usersDeviceMap = await mx.downloadKeys([userId, mx.getUserId()]); const usersDeviceMap = await mx.downloadKeys([userId, mx.getUserId()]);
return Object.values(usersDeviceMap).every((userDevices) => return Object.values(usersDeviceMap).every((userDevices) => Object.keys(userDevices).length > 0);
Object.keys(userDevices).length > 0,
);
} catch (e) { } catch (e) {
console.error("Error determining if it's possible to encrypt to all users: ", e); console.error("Error determining if it's possible to encrypt to all users: ", e);
return false; return false;
@ -141,7 +138,7 @@ function InviteUser({
} catch (e) { } catch (e) {
deleteUserFromProc(userId); deleteUserFromProc(userId);
if (typeof e.message === 'string') procUserError.set(userId, e.message); if (typeof e.message === 'string') procUserError.set(userId, e.message);
else procUserError.set(userId, t("errors.generic")); else procUserError.set(userId, t('errors.generic'));
updateUserProcError(getMapCopy(procUserError)); updateUserProcError(getMapCopy(procUserError));
} }
} }
@ -161,7 +158,7 @@ function InviteUser({
} catch (e) { } catch (e) {
deleteUserFromProc(userId); deleteUserFromProc(userId);
if (typeof e.message === 'string') procUserError.set(userId, e.message); if (typeof e.message === 'string') procUserError.set(userId, e.message);
else procUserError.set(userId, t("errors.generic")); else procUserError.set(userId, t('errors.generic'));
updateUserProcError(getMapCopy(procUserError)); updateUserProcError(getMapCopy(procUserError));
} }
} }
@ -179,7 +176,7 @@ function InviteUser({
return <Button onClick={() => { selectRoom(createdDM.get(userId)); onRequestClose(); }}>Open</Button>; return <Button onClick={() => { selectRoom(createdDM.get(userId)); onRequestClose(); }}>Open</Button>;
} }
if (invitedUserIds.has(userId)) { if (invitedUserIds.has(userId)) {
return messageJSX(t("Organisms.InviteUser.invite_result.invited"), true); return messageJSX(t('Organisms.InviteUser.invite_result.invited'), true);
} }
if (typeof roomId === 'string') { if (typeof roomId === 'string') {
const member = mx.getRoom(roomId).getMember(userId); const member = mx.getRoom(roomId).getMember(userId);
@ -187,18 +184,18 @@ function InviteUser({
const userMembership = member.membership; const userMembership = member.membership;
switch (userMembership) { switch (userMembership) {
case 'join': case 'join':
return messageJSX(t("Organisms.InviteUser.invite_result.already_joined"), true); return messageJSX(t('Organisms.InviteUser.invite_result.already_joined'), true);
case 'invite': case 'invite':
return messageJSX(t("Organisms.InviteUser.invite_result.already_invited"), true); return messageJSX(t('Organisms.InviteUser.invite_result.already_invited'), true);
case 'ban': case 'ban':
return messageJSX(t("Organisms.InviteUser.invite_result.banned"), false); return messageJSX(t('Organisms.InviteUser.invite_result.banned'), false);
default: default:
} }
} }
} }
return (typeof roomId === 'string') return (typeof roomId === 'string')
? <Button onClick={() => inviteToRoom(userId)} variant="primary">{t("common.invite")}</Button> ? <Button onClick={() => inviteToRoom(userId)} variant="primary">{t('common.invite')}</Button>
: <Button onClick={() => createDM(userId)} variant="primary">{t("common.message_prompt")}</Button>; : <Button onClick={() => createDM(userId)} variant="primary">{t('common.message_prompt')}</Button>;
}; };
const renderError = (userId) => { const renderError = (userId) => {
if (!procUserError.has(userId)) return null; if (!procUserError.has(userId)) return null;
@ -245,27 +242,27 @@ function InviteUser({
return ( return (
<PopupWindow <PopupWindow
isOpen={isOpen} isOpen={isOpen}
title={(typeof roomId === 'string' ? t("Organisms.InviteUser.invite_to_room", {room: mx.getRoom(roomId).name}) : t("Organisms.InviteUser.invite_to_dm"))} title={(typeof roomId === 'string' ? t('Organisms.InviteUser.invite_to_room', { room: mx.getRoom(roomId).name }) : t('Organisms.InviteUser.invite_to_dm'))}
contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t('common.close')} />}
onRequestClose={onRequestClose} onRequestClose={onRequestClose}
> >
<div className="invite-user"> <div className="invite-user">
<form className="invite-user__form" onSubmit={(e) => { e.preventDefault(); searchUser(usernameRef.current.value); }}> <form className="invite-user__form" onSubmit={(e) => { e.preventDefault(); searchUser(usernameRef.current.value); }}>
<Input value={searchTerm} forwardRef={usernameRef} label={t("Organisms.InviteUser.search_label")} /> <Input value={searchTerm} forwardRef={usernameRef} label={t('Organisms.InviteUser.search_label')} />
<Button disabled={isSearching} iconSrc={UserIC} variant="primary" type="submit">{t("common.search")}</Button> <Button disabled={isSearching} iconSrc={UserIC} variant="primary" type="submit">{t('common.search')}</Button>
</form> </form>
<div className="invite-user__search-status"> <div className="invite-user__search-status">
{ {
typeof searchQuery.username !== 'undefined' && isSearching && ( typeof searchQuery.username !== 'undefined' && isSearching && (
<div className="flex--center"> <div className="flex--center">
<Spinner size="small" /> <Spinner size="small" />
<Text variant="b2">{t("Organisms.InviteUser.searching_for_user", {user_name: searchQuery.username})}</Text> <Text variant="b2">{t('Organisms.InviteUser.searching_for_user', { user_name: searchQuery.username })}</Text>
</div> </div>
) )
} }
{ {
typeof searchQuery.username !== 'undefined' && !isSearching && ( typeof searchQuery.username !== 'undefined' && !isSearching && (
<Text variant="b2">{t("Organisms.InviteUser.search_result_title", {user_name: searchQuery.username})}</Text> <Text variant="b2">{t('Organisms.InviteUser.search_result_title', { user_name: searchQuery.username })}</Text>
) )
} }
{ {

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './JoinAlias.scss'; import './JoinAlias.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
@ -19,10 +20,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/; const ALIAS_OR_ID_REG = /^[#|!].+:.+\..+$/;
@ -61,10 +59,10 @@ function JoinAliasContent({ term, requestClose }) {
const alias = e.target.alias.value; const alias = e.target.alias.value;
if (alias?.trim() === '') return; if (alias?.trim() === '') return;
if (alias.match(ALIAS_OR_ID_REG) === null) { if (alias.match(ALIAS_OR_ID_REG) === null) {
setError(t("Organisms.JoinAlias.invalid_address")); setError(t('Organisms.JoinAlias.invalid_address'));
return; return;
} }
setProcess(t("Organisms.JoinAlias.looking_for_address")); setProcess(t('Organisms.JoinAlias.looking_for_address'));
setError(undefined); setError(undefined);
let via; let via;
if (alias.startsWith('#')) { if (alias.startsWith('#')) {
@ -72,12 +70,12 @@ function JoinAliasContent({ term, requestClose }) {
const aliasData = await mx.resolveRoomAlias(alias); const aliasData = await mx.resolveRoomAlias(alias);
via = aliasData?.servers.slice(0, 3) || []; via = aliasData?.servers.slice(0, 3) || [];
if (mountStore.getItem()) { if (mountStore.getItem()) {
setProcess(t("Organisms.JoinAlias.joining_alias", {alias_name: alias})); setProcess(t('Organisms.JoinAlias.joining_alias', { alias_name: alias }));
} }
} catch (err) { } catch (err) {
if (!mountStore.getItem()) return; if (!mountStore.getItem()) return;
setProcess(false); setProcess(false);
setError(t("Organisms.JoinAlias.couldnt_find_room_or_space_alias", {alias_name: alias})); setError(t('Organisms.JoinAlias.couldnt_find_room_or_space_alias', { alias_name: alias }));
} }
} }
try { try {
@ -88,14 +86,14 @@ function JoinAliasContent({ term, requestClose }) {
} catch { } catch {
if (!mountStore.getItem()) return; if (!mountStore.getItem()) return;
setProcess(false); setProcess(false);
setError(t("Organisms.JoinAlias.couldnt_find_room_or_space", {alias_name: alias})); setError(t('Organisms.JoinAlias.couldnt_find_room_or_space', { alias_name: alias }));
} }
}; };
return ( return (
<form className="join-alias" onSubmit={handleSubmit}> <form className="join-alias" onSubmit={handleSubmit}>
<Input <Input
label={t("Organisms.JoinAlias.address_label")} label={t('Organisms.JoinAlias.address_label')}
value={term} value={term}
name="alias" name="alias"
required required
@ -110,7 +108,7 @@ function JoinAliasContent({ term, requestClose }) {
<Text>{process}</Text> <Text>{process}</Text>
</> </>
) )
: <Button variant="primary" type="submit">{t("common.join")}</Button> : <Button variant="primary" type="submit">{t('common.join')}</Button>
} }
</div> </div>
</form> </form>
@ -145,13 +143,15 @@ function useWindowToggle() {
function JoinAlias() { function JoinAlias() {
const [data, requestClose] = useWindowToggle(); const [data, requestClose] = useWindowToggle();
const { t } = useTranslation();
return ( return (
<Dialog <Dialog
isOpen={data !== null} isOpen={data !== null}
title={( title={(
<Text variant="s1" weight="medium" primary>{t("Organisms.JoinAlias.title")}</Text> <Text variant="s1" weight="medium" primary>{t('Organisms.JoinAlias.title')}</Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={"common.close"} />} contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip="common.close" />}
onRequestClose={requestClose} onRequestClose={requestClose}
> >
{ data ? <JoinAliasContent term={data.term} requestClose={requestClose} /> : <div /> } { data ? <JoinAliasContent term={data.term} requestClose={requestClose} /> : <div /> }

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './DrawerBreadcrumb.scss'; import './DrawerBreadcrumb.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -18,10 +19,7 @@ import NotificationBadge from '../../atoms/badge/NotificationBadge';
import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg'; import ChevronRightIC from '../../../../public/res/ic/outlined/chevron-right.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function DrawerBreadcrumb({ spaceId }) { function DrawerBreadcrumb({ spaceId }) {
const [, forceUpdate] = useState({}); const [, forceUpdate] = useState({});
@ -116,7 +114,7 @@ function DrawerBreadcrumb({ spaceId }) {
className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''} className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''}
onClick={() => selectSpace(id)} onClick={() => selectSpace(id)}
> >
<Text variant="b2">{id === cons.tabs.HOME ? t("Organisms.DrawerBreadcrumb.home") : twemojify(mx.getRoom(id).name)}</Text> <Text variant="b2">{id === cons.tabs.HOME ? t('Organisms.DrawerBreadcrumb.home') : twemojify(mx.getRoom(id).name)}</Text>
{ noti !== null && ( { noti !== null && (
<NotificationBadge <NotificationBadge
alert={noti.highlight !== 0} alert={noti.highlight !== 0}

View file

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './DrawerHeader.scss'; import './DrawerHeader.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -28,10 +29,7 @@ import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg';
import SpacePlusIC from '../../../../public/res/ic/outlined/space-plus.svg'; import SpacePlusIC from '../../../../public/res/ic/outlined/space-plus.svg';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg'; import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
export function HomeSpaceOptions({ spaceId, afterOptionSelect }) { export function HomeSpaceOptions({ spaceId, afterOptionSelect }) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -44,27 +42,27 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) {
return ( return (
<> <>
<MenuHeader>{t("Organisms.DrawerHeader.add_rooms_or_spaces")}</MenuHeader> <MenuHeader>{t('Organisms.DrawerHeader.add_rooms_or_spaces')}</MenuHeader>
<MenuItem <MenuItem
iconSrc={SpacePlusIC} iconSrc={SpacePlusIC}
onClick={() => { afterOptionSelect(); openCreateRoom(true, spaceId); }} onClick={() => { afterOptionSelect(); openCreateRoom(true, spaceId); }}
disabled={!canManage} disabled={!canManage}
> >
{t("Organisms.DrawerHeader.create_new_space")} {t('Organisms.DrawerHeader.create_new_space')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
iconSrc={HashPlusIC} iconSrc={HashPlusIC}
onClick={() => { afterOptionSelect(); openCreateRoom(false, spaceId); }} onClick={() => { afterOptionSelect(); openCreateRoom(false, spaceId); }}
disabled={!canManage} disabled={!canManage}
> >
{t("Organisms.DrawerHeader.create_new_room")} {t('Organisms.DrawerHeader.create_new_room')}
</MenuItem> </MenuItem>
{ !spaceId && ( { !spaceId && (
<MenuItem <MenuItem
iconSrc={HashGlobeIC} iconSrc={HashGlobeIC}
onClick={() => { afterOptionSelect(); openPublicRooms(); }} onClick={() => { afterOptionSelect(); openPublicRooms(); }}
> >
{t("Organisms.DrawerHeader.join_public_room")} {t('Organisms.DrawerHeader.join_public_room')}
</MenuItem> </MenuItem>
)} )}
{ !spaceId && ( { !spaceId && (
@ -72,7 +70,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) {
iconSrc={PlusIC} iconSrc={PlusIC}
onClick={() => { afterOptionSelect(); openJoinAlias(); }} onClick={() => { afterOptionSelect(); openJoinAlias(); }}
> >
{t("Organisms.DrawerHeader.join_with_address")} {t('Organisms.DrawerHeader.join_with_address')}
</MenuItem> </MenuItem>
)} )}
{ spaceId && ( { spaceId && (
@ -81,7 +79,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) {
onClick={() => { afterOptionSelect(); openSpaceAddExisting(spaceId); }} onClick={() => { afterOptionSelect(); openSpaceAddExisting(spaceId); }}
disabled={!canManage} disabled={!canManage}
> >
{t("Organisms.DrawerHeader.add_existing")} {t('Organisms.DrawerHeader.add_existing')}
</MenuItem> </MenuItem>
)} )}
{ spaceId && ( { spaceId && (
@ -89,7 +87,7 @@ export function HomeSpaceOptions({ spaceId, afterOptionSelect }) {
onClick={() => { afterOptionSelect(); openSpaceManage(spaceId); }} onClick={() => { afterOptionSelect(); openSpaceManage(spaceId); }}
iconSrc={HashSearchIC} iconSrc={HashSearchIC}
> >
{t("Organisms.DrawerHeader.manage_rooms")} {t('Organisms.DrawerHeader.manage_rooms')}
</MenuItem> </MenuItem>
)} )}
</> </>
@ -105,7 +103,8 @@ HomeSpaceOptions.propTypes = {
function DrawerHeader({ selectedTab, spaceId }) { function DrawerHeader({ selectedTab, spaceId }) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const tabName = selectedTab !== cons.tabs.DIRECTS ? t("Organisms.DrawerHeader.home") : t("Organisms.DrawerHeader.direct_messages"); const { t } = useTranslation();
const tabName = selectedTab !== cons.tabs.DIRECTS ? t('Organisms.DrawerHeader.home') : t('Organisms.DrawerHeader.direct_messages');
const isDMTab = selectedTab === cons.tabs.DIRECTS; const isDMTab = selectedTab === cons.tabs.DIRECTS;
const room = mx.getRoom(spaceId); const room = mx.getRoom(spaceId);
@ -149,8 +148,8 @@ function DrawerHeader({ selectedTab, spaceId }) {
</TitleWrapper> </TitleWrapper>
)} )}
{ isDMTab && <IconButton onClick={() => openInviteUser()} tooltip={t("Organisms.DrawerHeader.start_dm_tooltip")} src={PlusIC} size="small" /> } { isDMTab && <IconButton onClick={() => openInviteUser()} tooltip={t('Organisms.DrawerHeader.start_dm_tooltip')} src={PlusIC} size="small" /> }
{ !isDMTab && <IconButton onClick={openHomeSpaceOptions} tooltip={t("Organisms.DrawerHeader.add_rooms_spaces_tooltip")} src={PlusIC} size="small" /> } { !isDMTab && <IconButton onClick={openHomeSpaceOptions} tooltip={t('Organisms.DrawerHeader.add_rooms_spaces_tooltip')} src={PlusIC} size="small" /> }
</Header> </Header>
); );
} }

View file

@ -2,6 +2,7 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
@ -18,8 +19,7 @@ import VerticalMenuIC from '../../../../public/res/ic/outlined/vertical-menu.svg
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function Selector({ function Selector({
roomId, isDM, drawerPostie, onClick, roomId, isDM, drawerPostie, onClick,
@ -74,7 +74,7 @@ function Selector({
options={( options={(
<IconButton <IconButton
size="extra-small" size="extra-small"
tooltip={t("common.options")} tooltip={t('common.options')}
tooltipPlacement="right" tooltipPlacement="right"
src={VerticalMenuIC} src={VerticalMenuIC}
onClick={openOptions} onClick={openOptions}

View file

@ -5,6 +5,7 @@ import './SideBar.scss';
import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend'; import { HTML5Backend } from 'react-dnd-html5-backend';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
@ -34,10 +35,7 @@ import { useDeviceList } from '../../hooks/useDeviceList';
import { tabText as settingTabText } from '../settings/Settings'; import { tabText as settingTabText } from '../settings/Settings';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function useNotificationUpdate() { function useNotificationUpdate() {
const { notifications } = initMatrix; const { notifications } = initMatrix;
@ -55,7 +53,6 @@ function useNotificationUpdate() {
} }
function ProfileAvatarMenu() { function ProfileAvatarMenu() {
const { t } = useTranslation(); const { t } = useTranslation();
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -85,7 +82,7 @@ function ProfileAvatarMenu() {
return ( return (
<SidebarAvatar <SidebarAvatar
onClick={openSettings} onClick={openSettings}
tooltip={t("Organisms.SideBar.settings_tooltip")} tooltip={t('Organisms.SideBar.settings_tooltip')}
avatar={( avatar={(
<Avatar <Avatar
text={profile.displayName} text={profile.displayName}
@ -99,6 +96,7 @@ function ProfileAvatarMenu() {
} }
function CrossSigninAlert() { function CrossSigninAlert() {
const { t } = useTranslation();
const deviceList = useDeviceList(); const deviceList = useDeviceList();
const unverified = deviceList?.filter((device) => isCrossVerified(device.device_id) === false); const unverified = deviceList?.filter((device) => isCrossVerified(device.device_id) === false);
@ -107,7 +105,7 @@ function CrossSigninAlert() {
return ( return (
<SidebarAvatar <SidebarAvatar
className="sidebar__cross-signin-alert" className="sidebar__cross-signin-alert"
tooltip={t("Organisms.SideBar.unverified_sessions", {count: unverified.length})} tooltip={t('Organisms.SideBar.unverified_sessions', { count: unverified.length })}
onClick={() => openSettings(settingTabText.SECURITY)} onClick={() => openSettings(settingTabText.SECURITY)}
avatar={<Avatar iconSrc={ShieldUserIC} iconColor="var(--ic-danger-normal)" size="normal" />} avatar={<Avatar iconSrc={ShieldUserIC} iconColor="var(--ic-danger-normal)" size="normal" />}
/> />
@ -117,6 +115,7 @@ function CrossSigninAlert() {
function FeaturedTab() { function FeaturedTab() {
const { roomList, accountData, notifications } = initMatrix; const { roomList, accountData, notifications } = initMatrix;
const [selectedTab] = useSelectedTab(); const [selectedTab] = useSelectedTab();
const { t } = useTranslation();
useNotificationUpdate(); useNotificationUpdate();
function getHomeNoti() { function getHomeNoti() {
@ -155,7 +154,7 @@ function FeaturedTab() {
return ( return (
<> <>
<SidebarAvatar <SidebarAvatar
tooltip={t("Organisms.SideBar.home_tooltip")} tooltip={t('Organisms.SideBar.home_tooltip')}
active={selectedTab === cons.tabs.HOME} active={selectedTab === cons.tabs.HOME}
onClick={() => selectTab(cons.tabs.HOME)} onClick={() => selectTab(cons.tabs.HOME)}
avatar={<Avatar iconSrc={HomeIC} size="normal" />} avatar={<Avatar iconSrc={HomeIC} size="normal" />}
@ -167,7 +166,7 @@ function FeaturedTab() {
) : null} ) : null}
/> />
<SidebarAvatar <SidebarAvatar
tooltip={t("Organisms.SideBar.direct_messages_tooltip")} tooltip={t('Organisms.SideBar.direct_messages_tooltip')}
active={selectedTab === cons.tabs.DIRECTS} active={selectedTab === cons.tabs.DIRECTS}
onClick={() => selectTab(cons.tabs.DIRECTS)} onClick={() => selectTab(cons.tabs.DIRECTS)}
avatar={<Avatar iconSrc={UserIC} size="normal" />} avatar={<Avatar iconSrc={UserIC} size="normal" />}
@ -350,6 +349,7 @@ function useTotalInvites() {
function SideBar() { function SideBar() {
const [totalInvites] = useTotalInvites(); const [totalInvites] = useTotalInvites();
const { t } = useTranslation();
return ( return (
<div className="sidebar"> <div className="sidebar">
@ -363,7 +363,7 @@ function SideBar() {
<div className="space-container"> <div className="space-container">
<SpaceShortcut /> <SpaceShortcut />
<SidebarAvatar <SidebarAvatar
tooltip={t("Organisms.SideBar.pin_spaces_tooltip")} tooltip={t('Organisms.SideBar.pin_spaces_tooltip')}
onClick={() => openShortcutSpaces()} onClick={() => openShortcutSpaces()}
avatar={<Avatar iconSrc={AddPinIC} size="normal" />} avatar={<Avatar iconSrc={AddPinIC} size="normal" />}
/> />
@ -375,13 +375,13 @@ function SideBar() {
<div className="sidebar-divider" /> <div className="sidebar-divider" />
<div className="sticky-container"> <div className="sticky-container">
<SidebarAvatar <SidebarAvatar
tooltip={t("Organisms.SideBar.search_tooltip")} tooltip={t('Organisms.SideBar.search_tooltip')}
onClick={() => openSearch()} onClick={() => openSearch()}
avatar={<Avatar iconSrc={SearchIC} size="normal" />} avatar={<Avatar iconSrc={SearchIC} size="normal" />}
/> />
{ totalInvites !== 0 && ( { totalInvites !== 0 && (
<SidebarAvatar <SidebarAvatar
tooltip={t("Organisms.SideBar.invites_tooltip")} tooltip={t('Organisms.SideBar.invites_tooltip')}
onClick={() => openInviteList()} onClick={() => openInviteList()}
avatar={<Avatar iconSrc={InviteIC} size="normal" />} avatar={<Avatar iconSrc={InviteIC} size="normal" />}
notificationBadge={<NotificationBadge alert content={totalInvites} />} notificationBadge={<NotificationBadge alert content={totalInvites} />}

View file

@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -16,10 +17,7 @@ import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import './ProfileEditor.scss'; import './ProfileEditor.scss';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
// TODO Fix bug that prevents 'Save' button from enabling up until second changed. // TODO Fix bug that prevents 'Save' button from enabling up until second changed.
function ProfileEditor({ userId }) { function ProfileEditor({ userId }) {
@ -49,9 +47,9 @@ function ProfileEditor({ userId }) {
const handleAvatarUpload = async (url) => { const handleAvatarUpload = async (url) => {
if (url === null) { if (url === null) {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Organisms.ProfileEditor.remove_avatar"), t('Organisms.ProfileEditor.remove_avatar'),
t("Organisms.ProfileViewer.remove_avatar_confirmation"), t('Organisms.ProfileViewer.remove_avatar_confirmation'),
t("common.remove"), t('common.remove'),
'caution', 'caution',
); );
if (isConfirmed) { if (isConfirmed) {
@ -90,13 +88,13 @@ function ProfileEditor({ userId }) {
onSubmit={(e) => { e.preventDefault(); saveDisplayName(); }} onSubmit={(e) => { e.preventDefault(); saveDisplayName(); }}
> >
<Input <Input
label={t("Organisms.ProfileEditor.display_name_message", {user_name: mx.getUserId()})} label={t('Organisms.ProfileEditor.display_name_message', { user_name: mx.getUserId() })}
onChange={onDisplayNameInputChange} onChange={onDisplayNameInputChange}
value={mx.getUser(mx.getUserId()).displayName} value={mx.getUser(mx.getUserId()).displayName}
forwardRef={displayNameRef} forwardRef={displayNameRef}
/> />
<Button variant="primary" type="submit" disabled={disabled}>{t("common.save")}</Button> <Button variant="primary" type="submit" disabled={disabled}>{t('common.save')}</Button>
<Button onClick={cancelDisplayNameChanges}>{t("common.cancel")}</Button> <Button onClick={cancelDisplayNameChanges}>{t('common.cancel')}</Button>
</form> </form>
); );
@ -107,7 +105,7 @@ function ProfileEditor({ userId }) {
<IconButton <IconButton
src={PencilIC} src={PencilIC}
size="extra-small" size="extra-small"
tooltip={t("common.edit")} tooltip={t('common.edit')}
onClick={() => setIsEditing(true)} onClick={() => setIsEditing(true)}
/> />
</div> </div>

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './ProfileViewer.scss'; import './ProfileViewer.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -11,7 +12,7 @@ import { selectRoom, openReusableContextMenu } from '../../../client/action/navi
import * as roomActions from '../../../client/action/room'; import * as roomActions from '../../../client/action/room';
import { import {
getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith getUsername, getUsernameOfRoomMember, getPowerLabel, hasDMWith,
} from '../../../util/matrixUtil'; } from '../../../util/matrixUtil';
import { getEventCords } from '../../../util/common'; import { getEventCords } from '../../../util/common';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
@ -34,14 +35,11 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
function ModerationTools({ function ModerationTools({
roomId, userId, roomId, userId,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -77,14 +75,14 @@ function ModerationTools({
<div className="moderation-tools"> <div className="moderation-tools">
{canIKick && ( {canIKick && (
<form onSubmit={handleKick}> <form onSubmit={handleKick}>
<Input label={t("Organisms.ProfileViewer.kick_reason_label")} name="kick-reason" /> <Input label={t('Organisms.ProfileViewer.kick_reason_label')} name="kick-reason" />
<Button type="submit">{t("Organisms.ProfileViewer.kick_button")}</Button> <Button type="submit">{t('Organisms.ProfileViewer.kick_button')}</Button>
</form> </form>
)} )}
{canIBan && ( {canIBan && (
<form onSubmit={handleBan}> <form onSubmit={handleBan}>
<Input label={t("Organisms.ProfileViewer.ban_reason_label")} name="ban-reason" /> <Input label={t('Organisms.ProfileViewer.ban_reason_label')} name="ban-reason" />
<Button type="submit">{t("Organisms.ProfileViewer.ban_button")}</Button> <Button type="submit">{t('Organisms.ProfileViewer.ban_button')}</Button>
</form> </form>
)} )}
</div> </div>
@ -127,8 +125,8 @@ function SessionInfo({ userId }) {
if (!isVisible) return null; if (!isVisible) return null;
return ( return (
<div className="session-info__chips"> <div className="session-info__chips">
{devices === null && <Text variant="b2">{t("Organisms.ProfileViewer.loading_sessions")}</Text>} {devices === null && <Text variant="b2">{t('Organisms.ProfileViewer.loading_sessions')}</Text>}
{devices?.length === 0 && <Text variant="b2">{t("Organisms.ProfileViewer.no_sessions_found")}</Text>} {devices?.length === 0 && <Text variant="b2">{t('Organisms.ProfileViewer.no_sessions_found')}</Text>}
{devices !== null && (devices.map((device) => ( {devices !== null && (devices.map((device) => (
<Chip <Chip
key={device.deviceId} key={device.deviceId}
@ -146,7 +144,7 @@ function SessionInfo({ userId }) {
onClick={() => setIsVisible(!isVisible)} onClick={() => setIsVisible(!isVisible)}
iconSrc={isVisible ? ChevronBottomIC : ChevronRightIC} iconSrc={isVisible ? ChevronBottomIC : ChevronRightIC}
> >
<Text variant="b2">{t("Organisms.ProfileViewer.view_sessions", {count: devices?.length})}</Text> <Text variant="b2">{t('Organisms.ProfileViewer.view_sessions', { count: devices?.length })}</Text>
</MenuItem> </MenuItem>
{renderSessionChips()} {renderSessionChips()}
</div> </div>
@ -161,6 +159,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
const [isCreatingDM, setIsCreatingDM] = useState(false); const [isCreatingDM, setIsCreatingDM] = useState(false);
const [isIgnoring, setIsIgnoring] = useState(false); const [isIgnoring, setIsIgnoring] = useState(false);
const [isUserIgnored, setIsUserIgnored] = useState(initMatrix.matrixClient.isUserIgnored(userId)); const [isUserIgnored, setIsUserIgnored] = useState(initMatrix.matrixClient.isUserIgnored(userId));
const { t } = useTranslation();
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -261,7 +260,7 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
onClick={openDM} onClick={openDM}
disabled={isCreatingDM} disabled={isCreatingDM}
> >
{isCreatingDM ? t("Organisms.ProfileViewer.creating_dm_room") : t("Organisms.ProfileViewer.send_direct_message_button")} {isCreatingDM ? t('Organisms.ProfileViewer.creating_dm_room') : t('Organisms.ProfileViewer.send_direct_message_button')}
</Button> </Button>
{ isBanned && canIKick && ( { isBanned && canIKick && (
<Button <Button
@ -278,8 +277,8 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
> >
{ {
isInvited isInvited
? `${isInviting ? t("common.uninviting") : t("common.uninvite")}` ? `${isInviting ? t('common.uninviting') : t('common.uninvite')}`
: `${isInviting ? t("common.inviting") : t("common.invite")}` : `${isInviting ? t('common.inviting') : t('common.invite')}`
} }
</Button> </Button>
)} )}
@ -290,8 +289,8 @@ function ProfileFooter({ roomId, userId, onRequestClose }) {
> >
{ {
isUserIgnored isUserIgnored
? `${isIgnoring ? t("Organisms.ProfileViewer.unignoring") : t("Organisms.ProfileViewer.unignore")}` ? `${isIgnoring ? t('Organisms.ProfileViewer.unignoring') : t('Organisms.ProfileViewer.unignore')}`
: `${isIgnoring ? t("Organisms.ProfileViewer.ignoring") : t("Organisms.ProfileViewer.ignore")}` : `${isIgnoring ? t('Organisms.ProfileViewer.ignoring') : t('Organisms.ProfileViewer.ignore')}`
} }
</Button> </Button>
</div> </div>
@ -355,6 +354,7 @@ function ProfileViewer() {
const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog(); const [isOpen, roomId, userId, closeDialog, handleAfterClose] = useToggleDialog();
useRerenderOnProfileChange(roomId, userId); useRerenderOnProfileChange(roomId, userId);
const { t } = useTranslation();
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId); const room = mx.getRoom(roomId);
@ -374,16 +374,16 @@ function ProfileViewer() {
const handleChangePowerLevel = async (newPowerLevel) => { const handleChangePowerLevel = async (newPowerLevel) => {
if (newPowerLevel === powerLevel) return; if (newPowerLevel === powerLevel) return;
const SHARED_POWER_MSG = t("Organisms.ProfileViewer.shared_power_message"); const SHARED_POWER_MSG = t('Organisms.ProfileViewer.shared_power_message');
const DEMOTING_MYSELF_MSG = t("Organisms.ProfileViewer.demoting_self_message"); const DEMOTING_MYSELF_MSG = t('Organisms.ProfileViewer.demoting_self_message');
const isSharedPower = newPowerLevel === myPowerLevel; const isSharedPower = newPowerLevel === myPowerLevel;
const isDemotingMyself = userId === mx.getUserId(); const isDemotingMyself = userId === mx.getUserId();
if (isSharedPower || isDemotingMyself) { if (isSharedPower || isDemotingMyself) {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Organisms.ProfileViewer.change_power_level"), t('Organisms.ProfileViewer.change_power_level'),
isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG, isSharedPower ? SHARED_POWER_MSG : DEMOTING_MYSELF_MSG,
t("common.change"), t('common.change'),
'caution', 'caution',
); );
if (!isConfirmed) return; if (!isConfirmed) return;
@ -444,7 +444,7 @@ function ProfileViewer() {
title={room?.name ?? ''} title={room?.name ?? ''}
onAfterClose={handleAfterClose} onAfterClose={handleAfterClose}
onRequestClose={closeDialog} onRequestClose={closeDialog}
contentOptions={<IconButton src={CrossIC} onClick={closeDialog} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={closeDialog} tooltip={t('common.close')} />}
> >
{roomId ? renderProfile() : <div />} {roomId ? renderProfile() : <div />}
</Dialog> </Dialog>

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './PublicRooms.scss'; import './PublicRooms.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import { selectRoom, selectTab } from '../../../client/action/navigation'; import { selectRoom, selectTab } from '../../../client/action/navigation';
@ -18,9 +19,7 @@ import RoomTile from '../../molecules/room-tile/RoomTile';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg'; import HashSearchIC from '../../../../public/res/ic/outlined/hash-search.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const SEARCH_LIMIT = 20; const SEARCH_LIMIT = 20;
@ -59,7 +58,7 @@ function TryJoinWithAlias({ alias, onRequestClose }) {
} catch (e) { } catch (e) {
setStatus({ setStatus({
isJoining: false, isJoining: false,
error: t("Organisms.PublicRooms.could_not_join_alias", {alias: alias}), error: t('Organisms.PublicRooms.could_not_join_alias', { alias }),
roomId: null, roomId: null,
tempRoomId: null, tempRoomId: null,
}); });
@ -69,16 +68,16 @@ function TryJoinWithAlias({ alias, onRequestClose }) {
return ( return (
<div className="try-join-with-alias"> <div className="try-join-with-alias">
{status.roomId === null && !status.isJoining && status.error === null && ( {status.roomId === null && !status.isJoining && status.error === null && (
<Button onClick={() => joinWithAlias()}>{t("Organisms.PublicRooms.try_joining_alias", {alias: alias})}</Button> <Button onClick={() => joinWithAlias()}>{t('Organisms.PublicRooms.try_joining_alias', { alias })}</Button>
)} )}
{status.isJoining && ( {status.isJoining && (
<> <>
<Spinner size="small" /> <Spinner size="small" />
<Text>{t("Organisms.PublicRooms.joining_alias", {alias: alias})}</Text> <Text>{t('Organisms.PublicRooms.joining_alias', { alias })}</Text>
</> </>
)} )}
{status.roomId !== null && ( {status.roomId !== null && (
<Button onClick={() => { onRequestClose(); selectRoom(status.roomId); }}>{t("common.open")}</Button> <Button onClick={() => { onRequestClose(); selectRoom(status.roomId); }}>{t('common.open')}</Button>
)} )}
{status.error !== null && <Text variant="b2"><span style={{ color: 'var(--bg-danger)' }}>{status.error}</span></Text>} {status.error !== null && <Text variant="b2"><span style={{ color: 'var(--bg-danger)' }}>{status.error}</span></Text>}
</div> </div>
@ -147,14 +146,14 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) {
if (totalRooms.length === 0) { if (totalRooms.length === 0) {
updateSearchQuery({ updateSearchQuery({
error: inputRoomName === '' error: inputRoomName === ''
? t("Organisms.PublicRooms.no_public_rooms", {homeserver: inputHs}) ? t('Organisms.PublicRooms.no_public_rooms', { homeserver: inputHs })
: t("Organisms.PublicRooms.no_result_found", {homeserver: inputHs, input: inputRoomName}), : t('Organisms.PublicRooms.no_result_found', { homeserver: inputHs, input: inputRoomName }),
alias: isInputAlias ? inputRoomName : null, alias: isInputAlias ? inputRoomName : null,
}); });
} }
} catch (e) { } catch (e) {
updatePublicRooms([]); updatePublicRooms([]);
let err = t("errors.generic"); let err = t('errors.generic');
if (e?.httpStatus >= 400 && e?.httpStatus < 500) { if (e?.httpStatus >= 400 && e?.httpStatus < 500) {
err = e.message; err = e.message;
} }
@ -213,8 +212,8 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) {
desc={typeof room.topic === 'string' ? room.topic : null} desc={typeof room.topic === 'string' ? room.topic : null}
options={( options={(
<> <>
{isJoined && <Button onClick={() => handleViewRoom(room.room_id)}>{t("common.open")}</Button>} {isJoined && <Button onClick={() => handleViewRoom(room.room_id)}>{t('common.open')}</Button>}
{!isJoined && (joiningRooms.has(room.room_id) ? <Spinner size="small" /> : <Button onClick={() => joinRoom(room.aliases?.[0] || room.room_id)} variant="primary">{t("commom.join")}</Button>)} {!isJoined && (joiningRooms.has(room.room_id) ? <Spinner size="small" /> : <Button onClick={() => joinRoom(room.aliases?.[0] || room.room_id)} variant="primary">{t('commom.join')}</Button>)}
</> </>
)} )}
/> />
@ -225,17 +224,17 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) {
return ( return (
<PopupWindow <PopupWindow
isOpen={isOpen} isOpen={isOpen}
title={t("Organisms.PublicRooms.title")} title={t('Organisms.PublicRooms.title')}
contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={onRequestClose} tooltip={t('common.close')} />}
onRequestClose={onRequestClose} onRequestClose={onRequestClose}
> >
<div className="public-rooms"> <div className="public-rooms">
<form className="public-rooms__form" onSubmit={(e) => { e.preventDefault(); searchRooms(); }}> <form className="public-rooms__form" onSubmit={(e) => { e.preventDefault(); searchRooms(); }}>
<div className="public-rooms__input-wrapper"> <div className="public-rooms__input-wrapper">
<Input value={searchTerm} forwardRef={roomNameRef} label={t("Organisms.PublicRooms.search_room_name_alias")} /> <Input value={searchTerm} forwardRef={roomNameRef} label={t('Organisms.PublicRooms.search_room_name_alias')} />
<Input forwardRef={hsRef} value={userId.slice(userId.indexOf(':') + 1)} label={t("common.homeserver")} required /> <Input forwardRef={hsRef} value={userId.slice(userId.indexOf(':') + 1)} label={t('common.homeserver')} required />
</div> </div>
<Button disabled={isSearching} iconSrc={HashSearchIC} variant="primary" type="submit">{t("Organisms.PublicRooms.search_button")}</Button> <Button disabled={isSearching} iconSrc={HashSearchIC} variant="primary" type="submit">{t('Organisms.PublicRooms.search_button')}</Button>
</form> </form>
<div className="public-rooms__search-status"> <div className="public-rooms__search-status">
{ {
@ -244,13 +243,13 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) {
? ( ? (
<div className="flex--center"> <div className="flex--center">
<Spinner size="small" /> <Spinner size="small" />
<Text variant="b2">{t("Organisms.PublicRooms.loading", {homeserver: searchQuery.homeserver})}</Text> <Text variant="b2">{t('Organisms.PublicRooms.loading', { homeserver: searchQuery.homeserver })}</Text>
</div> </div>
) )
: ( : (
<div className="flex--center"> <div className="flex--center">
<Spinner size="small" /> <Spinner size="small" />
<Text variant="b2">{t("Organisms.PublicRooms.searching", {homeserver: searchQuery.homeserver, query: searchQuery.name})}</Text> <Text variant="b2">{t('Organisms.PublicRooms.searching', { homeserver: searchQuery.homeserver, query: searchQuery.name })}</Text>
</div> </div>
) )
) )
@ -258,8 +257,8 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) {
{ {
typeof searchQuery.name !== 'undefined' && !isSearching && ( typeof searchQuery.name !== 'undefined' && !isSearching && (
searchQuery.name === '' searchQuery.name === ''
? <Text variant="b2">{t("Organisms.PublicRooms.result_title", {homeserver: searchQuery.homeserver})}</Text> ? <Text variant="b2">{t('Organisms.PublicRooms.result_title', { homeserver: searchQuery.homeserver })}</Text>
: <Text variant="b2">{t("Organisms.PublicRooms.search_result_title", {homeserver: searchQuery.homeserver, query: searchQuery.name})}</Text> : <Text variant="b2">{t('Organisms.PublicRooms.search_result_title', { homeserver: searchQuery.homeserver, query: searchQuery.name })}</Text>
) )
} }
{ searchQuery.error && ( { searchQuery.error && (
@ -279,7 +278,7 @@ function PublicRooms({ isOpen, searchTerm, onRequestClose }) {
{ publicRooms.length !== 0 && publicRooms.length % SEARCH_LIMIT === 0 && ( { publicRooms.length !== 0 && publicRooms.length % SEARCH_LIMIT === 0 && (
<div className="public-rooms__view-more"> <div className="public-rooms__view-more">
{ isViewMore !== true && ( { isViewMore !== true && (
<Button onClick={() => searchRooms(true)}>{t("commom.view_more")}</Button> <Button onClick={() => searchRooms(true)}>{t('commom.view_more')}</Button>
)} )}
{ isViewMore && <Spinner /> } { isViewMore && <Spinner /> }
</div> </div>

View file

@ -4,6 +4,7 @@ import React, {
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './PeopleDrawer.scss'; import './PeopleDrawer.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil'; import { getPowerLabel, getUsernameOfRoomMember } from '../../../util/matrixUtil';
import colorMXID from '../../../util/colorMXID'; import colorMXID from '../../../util/colorMXID';
@ -25,8 +26,7 @@ import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import SearchIC from '../../../../public/res/ic/outlined/search.svg'; import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function simplyfiMembers(members) { function simplyfiMembers(members) {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -134,11 +134,11 @@ function PeopleDrawer({ roomId }) {
<Header> <Header>
<TitleWrapper> <TitleWrapper>
<Text variant="s1" primary> <Text variant="s1" primary>
{t("Organisms.PeopleDrawer.title")} {t('Organisms.PeopleDrawer.title')}
<Text className="people-drawer__member-count" variant="b3">{t("Organisms.PeopleDrawer.members", {count: room.getJoinedMemberCount()})}</Text> <Text className="people-drawer__member-count" variant="b3">{t('Organisms.PeopleDrawer.members', { count: room.getJoinedMemberCount() })}</Text>
</Text> </Text>
</TitleWrapper> </TitleWrapper>
<IconButton onClick={() => openInviteUser(roomId)} tooltip={t("Organisms.PeopleDrawer.invite_tooltip")} src={AddUserIC} disabled={!canInvite} /> <IconButton onClick={() => openInviteUser(roomId)} tooltip={t('Organisms.PeopleDrawer.invite_tooltip')} src={AddUserIC} disabled={!canInvite} />
</Header> </Header>
<div className="people-drawer__content-wrapper"> <div className="people-drawer__content-wrapper">
<div className="people-drawer__scrollable"> <div className="people-drawer__scrollable">
@ -155,7 +155,7 @@ function PeopleDrawer({ roomId }) {
return getSegmentIndex[membership]; return getSegmentIndex[membership];
})() })()
} }
segments={[{ text: t("Organisms.PeopleDrawer.joined")}, { text: t("Organisms.PeopleDrawer.invited") }, { text: t("Organisms.PeopleDrawer.banned") }]} segments={[{ text: t('Organisms.PeopleDrawer.joined') }, { text: t('Organisms.PeopleDrawer.invited') }, { text: t('Organisms.PeopleDrawer.banned') }]}
onSelect={(index) => { onSelect={(index) => {
const selectSegment = [ const selectSegment = [
() => setMembership('join'), () => setMembership('join'),
@ -181,7 +181,7 @@ function PeopleDrawer({ roomId }) {
(searchedMembers?.data.length === 0 || memberList.length === 0) (searchedMembers?.data.length === 0 || memberList.length === 0)
&& ( && (
<div className="people-drawer__noresult"> <div className="people-drawer__noresult">
<Text variant="b2">{t("Organisms.PeopleDrawer.search_no_results")}</Text> <Text variant="b2">{t('Organisms.PeopleDrawer.search_no_results')}</Text>
</div> </div>
) )
} }
@ -191,7 +191,7 @@ function PeopleDrawer({ roomId }) {
&& memberList.length > itemCount && memberList.length > itemCount
&& searchedMembers === null && searchedMembers === null
&& ( && (
<Button onClick={loadMorePeople}>{t("Organisms.PeopleDrawer.view_more")}</Button> <Button onClick={loadMorePeople}>{t('Organisms.PeopleDrawer.view_more')}</Button>
) )
} }
</div> </div>
@ -201,7 +201,7 @@ function PeopleDrawer({ roomId }) {
<div className="people-drawer__sticky"> <div className="people-drawer__sticky">
<form onSubmit={(e) => e.preventDefault()} className="people-search"> <form onSubmit={(e) => e.preventDefault()} className="people-search">
<RawIcon size="small" src={SearchIC} /> <RawIcon size="small" src={SearchIC} />
<Input forwardRef={searchRef} type="text" onChange={handleSearch} placeholder={t("Organisms.PeopleDrawer.placeholder")} required /> <Input forwardRef={searchRef} type="text" onChange={handleSearch} placeholder={t('Organisms.PeopleDrawer.placeholder')} required />
{ {
searchedMembers !== null searchedMembers !== null
&& <IconButton onClick={handleSearch} size="small" src={CrossIC} /> && <IconButton onClick={handleSearch} size="small" src={CrossIC} />

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomSettings.scss'; import './RoomSettings.scss';
import { useTranslation } from 'react-i18next';
import { blurOnBubbling } from '../../atoms/button/script'; import { blurOnBubbling } from '../../atoms/button/script';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -38,9 +39,7 @@ import ChevronTopIC from '../../../../public/res/ic/outlined/chevron-top.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { t } from 'i18next';
const tabText = { const tabText = {
GENERAL: 'General', GENERAL: 'General',
@ -94,9 +93,9 @@ function GeneralSettings({ roomId }) {
variant="danger" variant="danger"
onClick={async () => { onClick={async () => {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Organisms.RoomSettings.leave_room"), t('Organisms.RoomSettings.leave_room'),
t("Organisms.RoomSettings.leave_room_confirm_message", {room_name: room.name}), t('Organisms.RoomSettings.leave_room_confirm_message', { room_name: room.name }),
t("Organisms.RoomSettings.leave_room_confirm_button"), t('Organisms.RoomSettings.leave_room_confirm_button'),
'danger', 'danger',
); );
if (!isConfirmed) return; if (!isConfirmed) return;
@ -108,15 +107,15 @@ function GeneralSettings({ roomId }) {
</MenuItem> </MenuItem>
</div> </div>
<div className="room-settings__card"> <div className="room-settings__card">
<MenuHeader>{t("Organisms.RoomSettings.notification_header")}</MenuHeader> <MenuHeader>{t('Organisms.RoomSettings.notification_header')}</MenuHeader>
<RoomNotification roomId={roomId} /> <RoomNotification roomId={roomId} />
</div> </div>
<div className="room-settings__card"> <div className="room-settings__card">
<MenuHeader>{t("Organisms.RoomSettings.visibility_header")}</MenuHeader> <MenuHeader>{t('Organisms.RoomSettings.visibility_header')}</MenuHeader>
<RoomVisibility roomId={roomId} /> <RoomVisibility roomId={roomId} />
</div> </div>
<div className="room-settings__card"> <div className="room-settings__card">
<MenuHeader>{t("Organisms.RoomSettings.address_header")}</MenuHeader> <MenuHeader>{t('Organisms.RoomSettings.address_header')}</MenuHeader>
<RoomAliases roomId={roomId} /> <RoomAliases roomId={roomId} />
</div> </div>
</> </>
@ -128,14 +127,15 @@ GeneralSettings.propTypes = {
}; };
function SecuritySettings({ roomId }) { function SecuritySettings({ roomId }) {
const { t } = useTranslation();
return ( return (
<> <>
<div className="room-settings__card"> <div className="room-settings__card">
<MenuHeader>{t("Organisms.RoomSettings.encryption_header")}</MenuHeader> <MenuHeader>{t('Organisms.RoomSettings.encryption_header')}</MenuHeader>
<RoomEncryption roomId={roomId} /> <RoomEncryption roomId={roomId} />
</div> </div>
<div className="room-settings__card"> <div className="room-settings__card">
<MenuHeader>{t("Organisms.RoomSettings.message_history_header")}</MenuHeader> <MenuHeader>{t('Organisms.RoomSettings.message_history_header')}</MenuHeader>
<RoomHistoryVisibility roomId={roomId} /> <RoomHistoryVisibility roomId={roomId} />
</div> </div>
</> </>
@ -149,6 +149,7 @@ function RoomSettings({ roomId }) {
const [, forceUpdate] = useForceUpdate(); const [, forceUpdate] = useForceUpdate();
const [selectedTab, setSelectedTab] = useState(tabItems[0]); const [selectedTab, setSelectedTab] = useState(tabItems[0]);
const room = initMatrix.matrixClient.getRoom(roomId); const room = initMatrix.matrixClient.getRoom(roomId);
const { t } = useTranslation();
const handleTabChange = (tabItem) => { const handleTabChange = (tabItem) => {
setSelectedTab(tabItem); setSelectedTab(tabItem);
@ -187,7 +188,12 @@ function RoomSettings({ roomId }) {
<TitleWrapper> <TitleWrapper>
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{`${room.name}`} {`${room.name}`}
<span style={{ color: 'var(--tc-surface-low)' }}> {t("Organisms.RoomSettings.room_settings_subtitle")}</span> <span style={{ color: 'var(--tc-surface-low)' }}>
{' '}
{' '}
{t('Organisms.RoomSettings.room_settings_subtitle')}
</span>
</Text> </Text>
</TitleWrapper> </TitleWrapper>
<RawIcon size="small" src={ChevronTopIC} /> <RawIcon size="small" src={ChevronTopIC} />

View file

@ -8,6 +8,7 @@ import PropTypes from 'prop-types';
import './RoomViewContent.scss'; import './RoomViewContent.scss';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { useTranslation, Trans } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -29,9 +30,7 @@ import { parseTimelineChange } from './common';
import TimelineScroll from './TimelineScroll'; import TimelineScroll from './TimelineScroll';
import EventLimit from './EventLimit'; import EventLimit from './EventLimit';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
const PAG_LIMIT = 30; const PAG_LIMIT = 30;
const MAX_MSG_DIFF_MINUTES = 5; const MAX_MSG_DIFF_MINUTES = 5;
@ -68,30 +67,30 @@ function RoomIntroContainer({ event, timeline }) {
let avatarSrc = room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop'); let avatarSrc = room.getAvatarUrl(mx.baseUrl, 80, 80, 'crop');
avatarSrc = isDM ? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc; avatarSrc = isDM ? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 80, 80, 'crop') : avatarSrc;
const heading = isDM ? room.name : t("Organisms.RoomViewContent.welcome_to_room", {room_name: room.name}); const heading = isDM ? room.name : t('Organisms.RoomViewContent.welcome_to_room', { room_name: room.name });
const topic = twemojify(roomTopic || '', undefined, true); const topic = twemojify(roomTopic || '', undefined, true);
const nameJsx = twemojify(room.name); const nameJsx = twemojify(room.name);
const desc = isDM const desc = isDM
? ( ? (
<> <>
<Trans <Trans
i18nKey={"Organisms.RoomViewContent.beginning_dm"} i18nKey="Organisms.RoomViewContent.beginning_dm"
values={{user_name: nameJsx}} values={{ user_name: nameJsx }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
{topic == "" ? "" : " - "} {topic == '' ? '' : ' - '}
{topic } {topic }
</> </>
) )
: ( : (
<> <>
<Trans <Trans
i18nKey={"Organisms.RoomViewContent.beginning_room"} i18nKey="Organisms.RoomViewContent.beginning_room"
values={{room_name: nameJsx}} values={{ room_name: nameJsx }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
{topic == "" ? "" : " - "} {topic == '' ? '' : ' - '}
{topic} {topic}
</> </>
); );
@ -112,7 +111,14 @@ function RoomIntroContainer({ event, timeline }) {
name={room.name} name={room.name}
heading={twemojify(heading)} heading={twemojify(heading)}
desc={desc} desc={desc}
time={event ? t("Organisms.RoomViewContent.created_on", {date: event.getDate(), formatParams: { date: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}}}) : null} time={event ? t('Organisms.RoomViewContent.created_on', {
date: event.getDate(),
formatParams: {
date: {
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
},
},
}) : null}
/> />
); );
} }
@ -539,7 +545,7 @@ function RoomViewContent({ eventId, roomTimeline }) {
&& readUptoEvent.getTs() < mEvent.getTs()); && readUptoEvent.getTs() < mEvent.getTs());
if (unreadDivider) { if (unreadDivider) {
isNewEvent = true; isNewEvent = true;
tl.push(<Divider key={`new-${mEvent.getId()}`} variant="positive" text={t("Organisms.RoomViewContent.new_messages")} />); tl.push(<Divider key={`new-${mEvent.getId()}`} variant="positive" text={t('Organisms.RoomViewContent.new_messages')} />);
itemCountIndex += 1; itemCountIndex += 1;
if (jumpToItemIndex === -1) jumpToItemIndex = itemCountIndex; if (jumpToItemIndex === -1) jumpToItemIndex = itemCountIndex;
} }

View file

@ -3,6 +3,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomViewFloating.scss'; import './RoomViewFloating.scss';
import { useTranslation, Trans } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -18,11 +19,7 @@ import TickMarkIC from '../../../../public/res/ic/outlined/tick-mark.svg';
import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil'; import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil';
import { getUsersActionJsx } from './common'; import '../../i18n';
import '../../i18n.jsx'
import { useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
function useJumpToEvent(roomTimeline) { function useJumpToEvent(roomTimeline) {
const [eventId, setEventId] = useState(null); const [eventId, setEventId] = useState(null);
@ -94,14 +91,13 @@ function useScrollToBottom(roomTimeline) {
function RoomViewFloating({ function RoomViewFloating({
roomId, roomTimeline, roomId, roomTimeline,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [isJumpToEvent, jumpToEvent, cancelJumpToEvent] = useJumpToEvent(roomTimeline); const [isJumpToEvent, jumpToEvent, cancelJumpToEvent] = useJumpToEvent(roomTimeline);
const [typingMembers] = useTypingMembers(roomTimeline); const [typingMembers] = useTypingMembers(roomTimeline);
const [isAtBottom, setIsAtBottom] = useScrollToBottom(roomTimeline); const [isAtBottom, setIsAtBottom] = useScrollToBottom(roomTimeline);
const room = initMatrix.matrixClient.getRoom(roomId) const room = initMatrix.matrixClient.getRoom(roomId);
const getUserDisplayName = (userId) => { const getUserDisplayName = (userId) => {
if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId)); if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId));
@ -113,41 +109,41 @@ function RoomViewFloating({
setIsAtBottom(true); setIsAtBottom(true);
}; };
console.log(typingMembers) console.log(typingMembers);
let typingMemberValues = [...typingMembers]; const typingMemberValues = [...typingMembers];
console.log(typingMemberValues) console.log(typingMemberValues);
return ( return (
<> <>
<div className={`room-view__unread ${isJumpToEvent ? 'room-view__unread--open' : ''}`}> <div className={`room-view__unread ${isJumpToEvent ? 'room-view__unread--open' : ''}`}>
<Button iconSrc={MessageUnreadIC} onClick={jumpToEvent} variant="primary"> <Button iconSrc={MessageUnreadIC} onClick={jumpToEvent} variant="primary">
<Text variant="b3" weight="medium">{t("Organisms.RoomViewFloating.jump_unread")}</Text> <Text variant="b3" weight="medium">{t('Organisms.RoomViewFloating.jump_unread')}</Text>
</Button> </Button>
<Button iconSrc={TickMarkIC} onClick={cancelJumpToEvent} variant="primary"> <Button iconSrc={TickMarkIC} onClick={cancelJumpToEvent} variant="primary">
<Text variant="b3" weight="bold">{t("Organisms.RoomViewFloating.mark_read")}</Text> <Text variant="b3" weight="bold">{t('Organisms.RoomViewFloating.mark_read')}</Text>
</Button> </Button>
</div> </div>
<div className={`room-view__typing${typingMembers.size > 0 ? ' room-view__typing--open' : ''}`}> <div className={`room-view__typing${typingMembers.size > 0 ? ' room-view__typing--open' : ''}`}>
<div className="bouncing-loader"><div /></div> <div className="bouncing-loader"><div /></div>
<Text variant="b2"> <Text variant="b2">
<Trans <Trans
i18nKey="Organisms.RoomViewFloating.user_typing" i18nKey="Organisms.RoomViewFloating.user_typing"
values={{ values={{
count: typingMembers.size, count: typingMembers.size,
user_one: twemojify(getUserDisplayName(typingMemberValues?.[0])), user_one: twemojify(getUserDisplayName(typingMemberValues?.[0])),
user_two: twemojify(getUserDisplayName(typingMemberValues?.[1])), user_two: twemojify(getUserDisplayName(typingMemberValues?.[1])),
user_three: twemojify(getUserDisplayName(typingMemberValues?.[2])), user_three: twemojify(getUserDisplayName(typingMemberValues?.[2])),
user_four: twemojify(getUserDisplayName(typingMemberValues?.[3])) user_four: twemojify(getUserDisplayName(typingMemberValues?.[3])),
}} }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</Text> </Text>
</div> </div>
<div className={`room-view__STB${isAtBottom ? '' : ' room-view__STB--open'}`}> <div className={`room-view__STB${isAtBottom ? '' : ' room-view__STB--open'}`}>
<Button iconSrc={MessageIC} onClick={handleScrollToBottom}> <Button iconSrc={MessageIC} onClick={handleScrollToBottom}>
<Text variant="b3" weight="medium">{t("Organisms.RoomViewFloating.jump_latest")}</Text> <Text variant="b3" weight="medium">{t('Organisms.RoomViewFloating.jump_latest')}</Text>
</Button> </Button>
</div> </div>
</> </>

View file

@ -2,6 +2,7 @@ import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './RoomViewHeader.scss'; import './RoomViewHeader.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import { blurOnBubbling } from '../../atoms/button/script'; import { blurOnBubbling } from '../../atoms/button/script';
@ -29,8 +30,7 @@ import BackArrowIC from '../../../../public/res/ic/outlined/chevron-left.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function RoomViewHeader({ roomId }) { function RoomViewHeader({ roomId }) {
const [, forceUpdate] = useForceUpdate(); const [, forceUpdate] = useForceUpdate();
@ -98,12 +98,12 @@ function RoomViewHeader({ roomId }) {
</TitleWrapper> </TitleWrapper>
<RawIcon src={ChevronBottomIC} /> <RawIcon src={ChevronBottomIC} />
</button> </button>
<IconButton onClick={() => toggleRoomSettings(tabText.SEARCH)} tooltip={t("Organisms.RoomViewHeader.search_tooltip")} src={SearchIC} /> <IconButton onClick={() => toggleRoomSettings(tabText.SEARCH)} tooltip={t('Organisms.RoomViewHeader.search_tooltip')} src={SearchIC} />
<IconButton className="room-header__drawer-btn" onClick={togglePeopleDrawer} tooltip={t("Organisms.RoomViewHeader.people_tooltip")} src={UserIC} /> <IconButton className="room-header__drawer-btn" onClick={togglePeopleDrawer} tooltip={t('Organisms.RoomViewHeader.people_tooltip')} src={UserIC} />
<IconButton className="room-header__members-btn" onClick={() => toggleRoomSettings(tabText.MEMBERS)} tooltip={t("Organisms.RoomViewHeader.members_tooltip")} src={UserIC} /> <IconButton className="room-header__members-btn" onClick={() => toggleRoomSettings(tabText.MEMBERS)} tooltip={t('Organisms.RoomViewHeader.members_tooltip')} src={UserIC} />
<IconButton <IconButton
onClick={openRoomOptions} onClick={openRoomOptions}
tooltip={t("common.options")} tooltip={t('common.options')}
src={VerticalMenuIC} src={VerticalMenuIC}
/> />
</Header> </Header>

View file

@ -5,6 +5,7 @@ import './RoomViewInput.scss';
import TextareaAutosize from 'react-autosize-textarea'; import TextareaAutosize from 'react-autosize-textarea';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import settings from '../../../client/state/settings'; import settings from '../../../client/state/settings';
@ -30,8 +31,7 @@ import MarkdownIC from '../../../../public/res/ic/outlined/markdown.svg';
import FileIC from '../../../../public/res/ic/outlined/file.svg'; import FileIC from '../../../../public/res/ic/outlined/file.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const CMD_REGEX = /(^\/|:|@)(\S*)$/; const CMD_REGEX = /(^\/|:|@)(\S*)$/;
let isTyping = false; let isTyping = false;
@ -86,7 +86,7 @@ function RoomViewInput({
function uploadingProgress(myRoomId, { loaded, total }) { function uploadingProgress(myRoomId, { loaded, total }) {
if (myRoomId !== roomId) return; if (myRoomId !== roomId) return;
const progressPer = Math.round((loaded * 100) / total); const progressPer = Math.round((loaded * 100) / total);
uploadProgressRef.current.textContent = t("Organisms.RoomViewInput.upload_progress", {progress: bytesToSize(loaded), total:bytesToSize(total), percent: progressPer}); uploadProgressRef.current.textContent = t('Organisms.RoomViewInput.upload_progress', { progress: bytesToSize(loaded), total: bytesToSize(total), percent: progressPer });
inputBaseRef.current.style.backgroundImage = `linear-gradient(90deg, var(--bg-surface-hover) ${progressPer}%, var(--bg-surface-low) ${progressPer}%)`; inputBaseRef.current.style.backgroundImage = `linear-gradient(90deg, var(--bg-surface-hover) ${progressPer}%, var(--bg-surface-low) ${progressPer}%)`;
} }
function clearAttachment(myRoomId) { function clearAttachment(myRoomId) {
@ -133,9 +133,7 @@ function RoomViewInput({
} }
function firedCmd(cmdData) { function firedCmd(cmdData) {
const msg = textAreaRef.current.value; const msg = textAreaRef.current.value;
textAreaRef.current.value = replaceCmdWith( textAreaRef.current.value = replaceCmdWith(msg, cmdCursorPos, typeof cmdData?.replace !== 'undefined' ? cmdData.replace : '');
msg, cmdCursorPos, typeof cmdData?.replace !== 'undefined' ? cmdData.replace : '',
);
deactivateCmd(); deactivateCmd();
} }
@ -316,8 +314,8 @@ function RoomViewInput({
<Text className="room-input__alert"> <Text className="room-input__alert">
{ {
tombstoneEvent tombstoneEvent
? tombstoneEvent.getContent()?.body ?? t("Organisms.RoomViewInput.tombstone_replaced") ? tombstoneEvent.getContent()?.body ?? t('Organisms.RoomViewInput.tombstone_replaced')
: t("Organisms.RoomViewInput.tombstone_permission_denied") : t('Organisms.RoomViewInput.tombstone_permission_denied')
} }
</Text> </Text>
); );
@ -338,7 +336,7 @@ function RoomViewInput({
onChange={handleMsgTyping} onChange={handleMsgTyping}
onPaste={handlePaste} onPaste={handlePaste}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
placeholder={t("Organisms.RoomViewInput.send_message_placeholder")} placeholder={t('Organisms.RoomViewInput.send_message_placeholder')}
/> />
</Text> </Text>
</ScrollView> </ScrollView>
@ -352,10 +350,10 @@ function RoomViewInput({
cords.y -= 250; cords.y -= 250;
openEmojiBoard(cords, addEmoji); openEmojiBoard(cords, addEmoji);
}} }}
tooltip={t("Organisms.RoomViewInput.emoji_tooltip")} tooltip={t('Organisms.RoomViewInput.emoji_tooltip')}
src={EmojiIC} src={EmojiIC}
/> />
<IconButton onClick={sendMessage} tooltip={t("common.send")} src={SendIC} /> <IconButton onClick={sendMessage} tooltip={t('common.send')} src={SendIC} />
</div> </div>
</> </>
); );
@ -373,7 +371,7 @@ function RoomViewInput({
</div> </div>
<div className="room-attachment__info"> <div className="room-attachment__info">
<Text variant="b1">{attachment.name}</Text> <Text variant="b1">{attachment.name}</Text>
<Text variant="b3"><span ref={uploadProgressRef}>{t("Organisms.RoomViewInput.file_size", {size: bytesToSize(attachment.size)})}</span></Text> <Text variant="b3"><span ref={uploadProgressRef}>{t('Organisms.RoomViewInput.file_size', { size: bytesToSize(attachment.size) })}</span></Text>
</div> </div>
</div> </div>
); );
@ -388,7 +386,7 @@ function RoomViewInput({
setReplyTo(null); setReplyTo(null);
}} }}
src={CrossIC} src={CrossIC}
tooltip={t("Organisms.RoomViewInput.cancel_reply_tooltip")} tooltip={t('Organisms.RoomViewInput.cancel_reply_tooltip')}
size="extra-small" size="extra-small"
/> />
<MessageReply <MessageReply

View file

@ -1,173 +1,151 @@
import React from 'react'; import React from 'react';
import { Trans } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil'; import { getUsername, getUsernameOfRoomMember } from '../../../util/matrixUtil';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
import { Trans } from 'react-i18next';
function getTimelineJSXMessages() { function getTimelineJSXMessages() {
return { return {
join(user) { join(user) {
return ( return (
<>
<Trans <Trans
i18nKey={"Organisms.RoomCommon.user_joined"} i18nKey="Organisms.RoomCommon.user_joined"
values={{user_name: twemojify(user)}} values={{ user_name: twemojify(user) }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</>
); );
}, },
leave(user, reason) { leave(user, reason) {
const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : ''; const reasonMsg = (typeof reason === 'string') ? `: ${reason}` : '';
return ( return (
<>
<Trans <Trans
i18nKey={"Organisms.RoomCommon.user_left"} i18nKey="Organisms.RoomCommon.user_left"
values={{user_name: twemojify(user)}} values={{ user_name: twemojify(user) }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</>
); );
}, },
invite(inviter, user) { invite(inviter, user) {
return ( return (
<>
<Trans <Trans
i18nKey={"Organisms.RoomCommon.user_invited"} i18nKey="Organisms.RoomCommon.user_invited"
values={{user_name: twemojify(user), inviter_name: twemojify(inviter)}} values={{ user_name: twemojify(user), inviter_name: twemojify(inviter) }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</>
); );
}, },
cancelInvite(inviter, user) { cancelInvite(inviter, user) {
return ( return (
<>
<Trans <Trans
i18nKey={"Organisms.RoomCommon.invite_cancelled"} i18nKey="Organisms.RoomCommon.invite_cancelled"
values={{user_name: twemojify(user), inviter_name: twemojify(inviter)}} values={{ user_name: twemojify(user), inviter_name: twemojify(inviter) }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</>
); );
}, },
rejectInvite(user) { rejectInvite(user) {
return ( return (
<>
<Trans <Trans
i18nKey={"Organisms.RoomCommon.invite_rejected"} i18nKey="Organisms.RoomCommon.invite_rejected"
values={{user_name: twemojify(user)}} values={{ user_name: twemojify(user) }}
components={{bold: <b/>}} components={{ bold: <b /> }}
/> />
</>
); );
}, },
kick(actor, user, reason) { kick(actor, user, reason) {
const reasonMsg = (typeof reason === 'string') ? `${reason}` : ''; const reasonMsg = (typeof reason === 'string') ? `${reason}` : '';
return ( return (
<>
<Trans <Trans
i18nKey={"Organisms.RoomCommon.user_kicked"} i18nKey="Organisms.RoomCommon.user_kicked"
values={{user_name: twemojify(user), actor: twemojify(actor), reason: twemojify(reasonMsg)}} values={{
components={{bold: <b/>}} user_name: twemojify(user),
actor: twemojify(actor),
reason: twemojify(reasonMsg),
}}
components={{ bold: <b /> }}
/> />
</>
); );
}, },
ban(actor, user, reason) { ban(actor, user, reason) {
const reasonMsg = (typeof reason === 'string') ? `${reason}` : ''; const reasonMsg = (typeof reason === 'string') ? `${reason}` : '';
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.user_banned"
i18nKey={"Organisms.RoomCommon.user_banned"} values={{
values={{user_name: twemojify(user), actor: twemojify(actor), reason: twemojify(reasonMsg)}} user_name: twemojify(user),
components={{bold: <b/>}} actor: twemojify(actor),
/> reason: twemojify(reasonMsg),
</> }}
components={{ bold: <b /> }}
/>
); );
}, },
unban(actor, user) { unban(actor, user) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.user_unbanned"
i18nKey={"Organisms.RoomCommon.user_unbanned"} values={{ user_name: twemojify(user), actor: twemojify(actor) }}
values={{user_name: twemojify(user), actor: twemojify(actor)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
avatarSets(user) { avatarSets(user) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.avatar_set"
i18nKey={"Organisms.RoomCommon.avatar_set"} values={{ user_name: twemojify(user) }}
values={{user_name: twemojify(user)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
avatarChanged(user) { avatarChanged(user) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.avatar_changed"
i18nKey={"Organisms.RoomCommon.avatar_changed"} values={{ user_name: twemojify(user) }}
values={{user_name: twemojify(user)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
avatarRemoved(user) { avatarRemoved(user) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.avatar_removed"
i18nKey={"Organisms.RoomCommon.avatar_removed"} values={{ user_name: twemojify(user) }}
values={{user_name: twemojify(user)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
nameSets(user, newName) { nameSets(user, newName) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.name_set"
i18nKey={"Organisms.RoomCommon.name_set"} values={{ user_name: twemojify(user), new_name: twemojify(newName) }}
values={{user_name: twemojify(user), new_name: twemojify(newName)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
nameChanged(user, newName) { nameChanged(user, newName) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.name_changed"
i18nKey={"Organisms.RoomCommon.name_changed"} values={{ user_name: twemojify(user), new_name: twemojify(newName) }}
values={{user_name: twemojify(user), new_name: twemojify(newName)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
nameRemoved(user, lastName) { nameRemoved(user, lastName) {
return ( return (
<> <Trans
<Trans i18nKey="Organisms.RoomCommon.name_removed"
i18nKey={"Organisms.RoomCommon.name_removed"} values={{ user_name: twemojify(user), new_name: twemojify(lastName) }}
values={{user_name: twemojify(user), new_name: twemojify(newName)}} components={{ bold: <b /> }}
components={{bold: <b/>}} />
/>
</>
); );
}, },
}; };

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import './Search.scss'; import './Search.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
@ -20,8 +21,7 @@ import RoomSelector from '../../molecules/room-selector/RoomSelector';
import SearchIC from '../../../../public/res/ic/outlined/search.svg'; import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function useVisiblityToggle(setResult) { function useVisiblityToggle(setResult) {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -212,12 +212,12 @@ function Search() {
size="small" size="small"
> >
<div className="search-dialog"> <div className="search-dialog">
<form className="search-dialog__input" onSubmit={(e) => { e.preventDefault(); openFirstResult()}}> <form className="search-dialog__input" onSubmit={(e) => { e.preventDefault(); openFirstResult(); }}>
<RawIcon src={SearchIC} size="small" /> <RawIcon src={SearchIC} size="small" />
<Input <Input
onChange={handleOnChange} onChange={handleOnChange}
forwardRef={searchRef} forwardRef={searchRef}
placeholder={t("common.search")} placeholder={t('common.search')}
/> />
<IconButton size="small" src={CrossIC} type="reset" onClick={handleCross} tabIndex={-1} /> <IconButton size="small" src={CrossIC} type="reset" onClick={handleCross} tabIndex={-1} />
</form> </form>
@ -229,7 +229,7 @@ function Search() {
</ScrollView> </ScrollView>
</div> </div>
<div className="search-dialog__footer"> <div className="search-dialog__footer">
<Text variant="b3">{t("Organisms.Search.description")}</Text> <Text variant="b3">{t('Organisms.Search.description')}</Text>
</div> </div>
</div> </div>
</RawModal> </RawModal>

View file

@ -12,7 +12,7 @@ import Spinner from '../../atoms/spinner/Spinner';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
let lastUsedPassword; let lastUsedPassword;

View file

@ -20,7 +20,7 @@ import { authRequest } from './AuthRequest';
import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus'; import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import './DeviceManage.scss'; import './DeviceManage.scss';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { isCrossVerified } from '../../../util/matrixUtil'; import { isCrossVerified } from '../../../util/matrixUtil';
import { openReusableDialog, openEmojiVerification } from '../../../client/action/navigation'; import { openReusableDialog, openEmojiVerification } from '../../../client/action/navigation';
@ -27,9 +28,7 @@ import { useDeviceList } from '../../hooks/useDeviceList';
import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus'; import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
import { accessSecretStorage } from './SecretStorageAccess'; import { accessSecretStorage } from './SecretStorageAccess';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
const promptDeviceName = async (deviceName) => new Promise((resolve) => { const promptDeviceName = async (deviceName) => new Promise((resolve) => {
let isCompleted = false; let isCompleted = false;
@ -45,17 +44,17 @@ const promptDeviceName = async (deviceName) => new Promise((resolve) => {
}; };
return ( return (
<form className="device-manage__rename" onSubmit={handleSubmit}> <form className="device-manage__rename" onSubmit={handleSubmit}>
<Input value={deviceName} label={t("Organisms.DeviceManage.edit_session_name_subtitle")} name="session" /> <Input value={deviceName} label={t('Organisms.DeviceManage.edit_session_name_subtitle')} name="session" />
<div className="device-manage__rename-btn"> <div className="device-manage__rename-btn">
<Button variant="primary" type="submit">{t("common.save")}</Button> <Button variant="primary" type="submit">{t('common.save')}</Button>
<Button onClick={() => onComplete(null)}>{t("common.cancel")}</Button> <Button onClick={() => onComplete(null)}>{t('common.cancel')}</Button>
</div> </div>
</form> </form>
); );
}; };
openReusableDialog( openReusableDialog(
<Text variant="s1" weight="medium">{t("Organisms.DeviceManage.edit_session_name_title")}</Text>, <Text variant="s1" weight="medium">{t('Organisms.DeviceManage.edit_session_name_title')}</Text>,
(requestClose) => renderContent((name) => { (requestClose) => renderContent((name) => {
isCompleted = true; isCompleted = true;
resolve(name); resolve(name);
@ -95,17 +94,17 @@ function DeviceManage() {
}; };
return ( return (
<form className="device-manage__rename" onSubmit={handleSubmit}> <form className="device-manage__rename" onSubmit={handleSubmit}>
<Input value={deviceName} label={t("Organisms.DeviceManage.edit_session_name_subtitle")} name="session" /> <Input value={deviceName} label={t('Organisms.DeviceManage.edit_session_name_subtitle')} name="session" />
<div className="device-manage__rename-btn"> <div className="device-manage__rename-btn">
<Button variant="primary" type="submit">{t("common.save")}</Button> <Button variant="primary" type="submit">{t('common.save')}</Button>
<Button onClick={() => onComplete(null)}>{t("common.cancel")}</Button> <Button onClick={() => onComplete(null)}>{t('common.cancel')}</Button>
</div> </div>
</form> </form>
); );
}; };
openReusableDialog( openReusableDialog(
<Text variant="s1" weight="medium">{t("Organisms.DeviceManage.edit_session_name_title")}</Text>, <Text variant="s1" weight="medium">{t('Organisms.DeviceManage.edit_session_name_title')}</Text>,
(requestClose) => renderContent((name) => { (requestClose) => renderContent((name) => {
isCompleted = true; isCompleted = true;
resolve(name); resolve(name);
@ -132,7 +131,7 @@ function DeviceManage() {
<div className="device-manage"> <div className="device-manage">
<div className="device-manage__loading"> <div className="device-manage__loading">
<Spinner size="small" /> <Spinner size="small" />
<Text>{t("Organisms.DeviceManage.loading_devices")}</Text> <Text>{t('Organisms.DeviceManage.loading_devices')}</Text>
</div> </div>
</div> </div>
); );
@ -155,14 +154,14 @@ function DeviceManage() {
const handleRemove = async (device) => { const handleRemove = async (device) => {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Organisms.DeviceManage.logout_device_title", {device: device.display_name}), t('Organisms.DeviceManage.logout_device_title', { device: device.display_name }),
t("Organisms.DeviceManage.logout_device_message", {device: device.display_name}), t('Organisms.DeviceManage.logout_device_message', { device: device.display_name }),
t("Organisms.DeviceManage.logout_device_confirm"), t('Organisms.DeviceManage.logout_device_confirm'),
'danger', 'danger',
); );
if (!isConfirmed) return; if (!isConfirmed) return;
addToProcessing(device); addToProcessing(device);
await authRequest(t("Organisms.DeviceManage.logout_device_title", {device: device.display_name}), async (auth) => { await authRequest(t('Organisms.DeviceManage.logout_device_title', { device: device.display_name }), async (auth) => {
await mx.deleteDevice(device.device_id, auth); await mx.deleteDevice(device.device_id, auth);
}); });
@ -171,7 +170,7 @@ function DeviceManage() {
}; };
const verifyWithKey = async (device) => { const verifyWithKey = async (device) => {
const keyData = await accessSecretStorage(t("Organisms.DeviceManage.session_verification_title")); const keyData = await accessSecretStorage(t('Organisms.DeviceManage.session_verification_title'));
if (!keyData) return; if (!keyData) return;
addToProcessing(device); addToProcessing(device);
await mx.checkOwnCrossSigningTrust(); await mx.checkOwnCrossSigningTrust();
@ -205,7 +204,7 @@ function DeviceManage() {
<Text style={{ color: isVerified !== false ? '' : 'var(--tc-danger-high)' }}> <Text style={{ color: isVerified !== false ? '' : 'var(--tc-danger-high)' }}>
{displayName} {displayName}
<Text variant="b3" span>{`${displayName ? ' — ' : ''}${deviceId}`}</Text> <Text variant="b3" span>{`${displayName ? ' — ' : ''}${deviceId}`}</Text>
{isCurrentDevice && <Text span className="device-manage__current-label" variant="b3">{t("Organisms.DeviceManage.current_device_label")}</Text>} {isCurrentDevice && <Text span className="device-manage__current-label" variant="b3">{t('Organisms.DeviceManage.current_device_label')}</Text>}
</Text> </Text>
)} )}
options={ options={
@ -213,9 +212,9 @@ function DeviceManage() {
? <Spinner size="small" /> ? <Spinner size="small" />
: ( : (
<> <>
{(isCSEnabled && canVerify) && <Button onClick={() => verify(deviceId, isCurrentDevice)} variant="positive">{t("Organisms.DeviceManage.verify_session_button")}</Button>} {(isCSEnabled && canVerify) && <Button onClick={() => verify(deviceId, isCurrentDevice)} variant="positive">{t('Organisms.DeviceManage.verify_session_button')}</Button>}
<IconButton size="small" onClick={() => handleRename(device)} src={PencilIC} tooltip={t("Organisms.DeviceManage.edit_session_name_tooltip")} /> <IconButton size="small" onClick={() => handleRename(device)} src={PencilIC} tooltip={t('Organisms.DeviceManage.edit_session_name_tooltip')} />
<IconButton size="small" onClick={() => handleRemove(device)} src={BinIC} tooltip={t("Organisms.DeviceManage.logout_device_tooltip")}/> <IconButton size="small" onClick={() => handleRemove(device)} src={BinIC} tooltip={t('Organisms.DeviceManage.logout_device_tooltip')} />
</> </>
) )
} }
@ -256,46 +255,46 @@ function DeviceManage() {
return ( return (
<div className="device-manage"> <div className="device-manage">
<div> <div>
<MenuHeader>{t("Organisms.DeviceManage.unverified_sessions_title")}</MenuHeader> <MenuHeader>{t('Organisms.DeviceManage.unverified_sessions_title')}</MenuHeader>
{!isCSEnabled && ( {!isCSEnabled && (
<div style={{ padding: 'var(--sp-extra-tight) var(--sp-normal)' }}> <div style={{ padding: 'var(--sp-extra-tight) var(--sp-normal)' }}>
<InfoCard <InfoCard
rounded rounded
variant="caution" variant="caution"
iconSrc={InfoIC} iconSrc={InfoIC}
title={t("Organisms.DeviceManage.setup_cross_signing_message")} title={t('Organisms.DeviceManage.setup_cross_signing_message')}
/> />
</div> </div>
)} )}
{ {
unverified.length > 0 unverified.length > 0
? unverified.map((device) => renderDevice(device, false)) ? unverified.map((device) => renderDevice(device, false))
: <Text className="device-manage__info">{t("Organisms.DeviceManage.unverified_sessions_none")}</Text> : <Text className="device-manage__info">{t('Organisms.DeviceManage.unverified_sessions_none')}</Text>
} }
</div> </div>
{noEncryption.length > 0 && ( {noEncryption.length > 0 && (
<div> <div>
<MenuHeader>{t("Organisms.DeviceManage.unencrypted_sessions_title")}</MenuHeader> <MenuHeader>{t('Organisms.DeviceManage.unencrypted_sessions_title')}</MenuHeader>
{noEncryption.map((device) => renderDevice(device, null))} {noEncryption.map((device) => renderDevice(device, null))}
</div> </div>
)} )}
<div> <div>
<MenuHeader>{t("Organisms.DeviceManage.verified_sessions_title")}</MenuHeader> <MenuHeader>{t('Organisms.DeviceManage.verified_sessions_title')}</MenuHeader>
{ {
verified.length > 0 verified.length > 0
? verified.map((device, index) => { ? verified.map((device, index) => {
if (truncated && index >= TRUNCATED_COUNT) return null; if (truncated && index >= TRUNCATED_COUNT) return null;
return renderDevice(device, true); return renderDevice(device, true);
}) })
: <Text className="device-manage__info">{t("Organisms.DeviceManage.verified_sessions_none")}</Text> : <Text className="device-manage__info">{t('Organisms.DeviceManage.verified_sessions_none')}</Text>
} }
{ verified.length > TRUNCATED_COUNT && ( { verified.length > TRUNCATED_COUNT && (
<Button className="device-manage__info" onClick={() => setTruncated(!truncated)}> <Button className="device-manage__info" onClick={() => setTruncated(!truncated)}>
{t(truncated ? "common.view_more" : "common.view_less")} {t(truncated ? 'common.view_more' : 'common.view_less')}
</Button> </Button>
)} )}
{ deviceList.length > 0 && ( { deviceList.length > 0 && (
<Text className="device-manage__info" variant="b3">{t("Organisms.DeviceManage.session_name_privacy_message")}</Text> <Text className="device-manage__info" variant="b3">{t('Organisms.DeviceManage.session_name_privacy_message')}</Text>
)} )}
</div> </div>
</div> </div>

View file

@ -24,7 +24,7 @@ import DownloadIC from '../../../../public/res/ic/outlined/download.svg';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus'; import { useCrossSigningStatus } from '../../hooks/useCrossSigningStatus';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';

View file

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import './SecretStorageAccess.scss'; import './SecretStorageAccess.scss';
import { deriveKey } from 'matrix-js-sdk/lib/crypto/key_passphrase'; import { deriveKey } from 'matrix-js-sdk/lib/crypto/key_passphrase';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import { openReusableDialog } from '../../../client/action/navigation'; import { openReusableDialog } from '../../../client/action/navigation';
import { getDefaultSSKey, getSSKeyInfo } from '../../../util/matrixUtil'; import { getDefaultSSKey, getSSKeyInfo } from '../../../util/matrixUtil';
@ -13,10 +14,7 @@ import Button from '../../atoms/button/Button';
import Input from '../../atoms/input/Input'; import Input from '../../atoms/input/Input';
import Spinner from '../../atoms/spinner/Spinner'; import Spinner from '../../atoms/spinner/Spinner';
import '../../i18n';
import '../../i18n.jsx'
import { useTranslation } from 'react-i18next';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
@ -45,7 +43,7 @@ function SecretStorageAccess({ onComplete }) {
if (!mountStore.getItem()) return; if (!mountStore.getItem()) return;
if (!isCorrect) { if (!isCorrect) {
setError(t(key ? "SecretStorageAccess.incorrect_security_key" : "SecretStorageAccess.incorrect_security_phrase")); setError(t(key ? 'SecretStorageAccess.incorrect_security_key' : 'SecretStorageAccess.incorrect_security_phrase'));
setProcess(false); setProcess(false);
return; return;
} }
@ -57,7 +55,7 @@ function SecretStorageAccess({ onComplete }) {
}); });
} catch (e) { } catch (e) {
if (!mountStore.getItem()) return; if (!mountStore.getItem()) return;
setError(t(key ? "SecretStorageAccess.incorrect_security_key" : "SecretStorageAccess.incorrect_security_phrase")); setError(t(key ? 'SecretStorageAccess.incorrect_security_key' : 'SecretStorageAccess.incorrect_security_phrase'));
setProcess(false); setProcess(false);
} }
}; };
@ -82,7 +80,7 @@ function SecretStorageAccess({ onComplete }) {
<form onSubmit={handleForm}> <form onSubmit={handleForm}>
<Input <Input
name="password" name="password"
label={t(withPhrase ? "SecretStorageAccess.security_phrase" : "SecretStorageAccess.security_key")} label={t(withPhrase ? 'SecretStorageAccess.security_phrase' : 'SecretStorageAccess.security_key')}
type="password" type="password"
onChange={handleChange} onChange={handleChange}
required required
@ -90,8 +88,8 @@ function SecretStorageAccess({ onComplete }) {
{error && <Text variant="b3">{error}</Text>} {error && <Text variant="b3">{error}</Text>}
{!process && ( {!process && (
<div className="secret-storage-access__btn"> <div className="secret-storage-access__btn">
<Button variant="primary" type="submit">{t("common.continue")}</Button> <Button variant="primary" type="submit">{t('common.continue')}</Button>
{isPassphrase && <Button onClick={toggleWithPhrase}>{t( withPhrase ? "SecretStorageAccess.use_security_key" : "SecretStorageAccess.use_security_phrase")}</Button>} {isPassphrase && <Button onClick={toggleWithPhrase}>{t(withPhrase ? 'SecretStorageAccess.use_security_key' : 'SecretStorageAccess.use_security_phrase')}</Button>}
</div> </div>
)} )}
</form> </form>

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import './Settings.scss'; import './Settings.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import settings from '../../../client/state/settings'; import settings from '../../../client/state/settings';
@ -40,8 +41,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import CinnySVG from '../../../../public/res/svg/cinny.svg'; import CinnySVG from '../../../../public/res/svg/cinny.svg';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function AppearanceSection() { function AppearanceSection() {
const [, updateState] = useState({}); const [, updateState] = useState({});
@ -53,26 +53,26 @@ function AppearanceSection() {
<div className="settings-appearance__card"> <div className="settings-appearance__card">
<MenuHeader>Theme</MenuHeader> <MenuHeader>Theme</MenuHeader>
<SettingTile <SettingTile
title={t("Organisms.Settings.theme.follow_system.title")} title={t('Organisms.Settings.theme.follow_system.title')}
options={( options={(
<Toggle <Toggle
isActive={settings.useSystemTheme} isActive={settings.useSystemTheme}
onToggle={() => { toggleSystemTheme(); updateState({}); }} onToggle={() => { toggleSystemTheme(); updateState({}); }}
/> />
)} )}
content={<Text variant="b3">{t("Organisms.Settings.theme.follow_system.description")}</Text>} content={<Text variant="b3">{t('Organisms.Settings.theme.follow_system.description')}</Text>}
/> />
{!settings.useSystemTheme && ( {!settings.useSystemTheme && (
<SettingTile <SettingTile
title={t("Organisms.Settings.theme.title")} title={t('Organisms.Settings.theme.title')}
content={( content={(
<SegmentedControls <SegmentedControls
selected={settings.getThemeIndex()} selected={settings.getThemeIndex()}
segments={[ segments={[
{ text: t("Organisms.Settings.theme.theme_light") }, { text: t('Organisms.Settings.theme.theme_light') },
{ text: t("Organisms.Settings.theme.theme_silver") }, { text: t('Organisms.Settings.theme.theme_silver') },
{ text: t("Organisms.Settings.theme.theme_dark") }, { text: t('Organisms.Settings.theme.theme_dark') },
{ text: t("Organisms.Settings.theme.theme_butter") }, { text: t('Organisms.Settings.theme.theme_butter') },
]} ]}
onSelect={(index) => settings.setTheme(index)} onSelect={(index) => settings.setTheme(index)}
/> />
@ -83,34 +83,34 @@ function AppearanceSection() {
<div className="settings-appearance__card"> <div className="settings-appearance__card">
<MenuHeader>Room messages</MenuHeader> <MenuHeader>Room messages</MenuHeader>
<SettingTile <SettingTile
title={t("Organisms.Settings.markdown.title")} title={t('Organisms.Settings.markdown.title')}
options={( options={(
<Toggle <Toggle
isActive={settings.isMarkdown} isActive={settings.isMarkdown}
onToggle={() => { toggleMarkdown(); updateState({}); }} onToggle={() => { toggleMarkdown(); updateState({}); }}
/> />
)} )}
content={<Text variant="b3">{t("Organisms.Settings.markdown.description")}</Text>} content={<Text variant="b3">{t('Organisms.Settings.markdown.description')}</Text>}
/> />
<SettingTile <SettingTile
title={t("Organisms.Settings.hide_membership_events.title")} title={t('Organisms.Settings.hide_membership_events.title')}
options={( options={(
<Toggle <Toggle
isActive={settings.hideMembershipEvents} isActive={settings.hideMembershipEvents}
onToggle={() => { toggleMembershipEvents(); updateState({}); }} onToggle={() => { toggleMembershipEvents(); updateState({}); }}
/> />
)} )}
content={<Text variant="b3">{t("Organisms.Settings.hide_membership_events.description")}</Text>} content={<Text variant="b3">{t('Organisms.Settings.hide_membership_events.description')}</Text>}
/> />
<SettingTile <SettingTile
title={t("Organisms.Settings.hide_nickname_avatar_events.title")} title={t('Organisms.Settings.hide_nickname_avatar_events.title')}
options={( options={(
<Toggle <Toggle
isActive={settings.hideNickAvatarEvents} isActive={settings.hideNickAvatarEvents}
onToggle={() => { toggleNickAvatarEvents(); updateState({}); }} onToggle={() => { toggleNickAvatarEvents(); updateState({}); }}
/> />
)} )}
content={<Text variant="b3">{t("Organisms.Settings.hide_nickname_avatar_events.description")}</Text>} content={<Text variant="b3">{t('Organisms.Settings.hide_nickname_avatar_events.description')}</Text>}
/> />
</div> </div>
</div> </div>
@ -126,7 +126,7 @@ function NotificationsSection() {
const renderOptions = () => { const renderOptions = () => {
if (window.Notification === undefined) { if (window.Notification === undefined) {
return <Text className="settings-notifications__not-supported">{t("errors.browser_not_supported")}</Text>; return <Text className="settings-notifications__not-supported">{t('errors.browser_not_supported')}</Text>;
} }
if (permission === 'granted') { if (permission === 'granted') {
@ -154,54 +154,53 @@ function NotificationsSection() {
return ( return (
<div className="settings-notifications"> <div className="settings-notifications">
<MenuHeader>{t("Organisms.Settings.notifications_and_sound.title")}</MenuHeader> <MenuHeader>{t('Organisms.Settings.notifications_and_sound.title')}</MenuHeader>
<SettingTile <SettingTile
title={t("Organisms.Settings.notifications_and_sound.desktop.title")} title={t('Organisms.Settings.notifications_and_sound.desktop.title')}
options={renderOptions()} options={renderOptions()}
content={<Text variant="b3">{t("Organisms.Settings.notifications_and_sound.desktop.description")}</Text>} content={<Text variant="b3">{t('Organisms.Settings.notifications_and_sound.desktop.description')}</Text>}
/> />
<SettingTile <SettingTile
title={t("Organisms.Settings.notifications_and_sound.sound.title")} title={t('Organisms.Settings.notifications_and_sound.sound.title')}
options={( options={(
<Toggle <Toggle
isActive={settings.isNotificationSounds} isActive={settings.isNotificationSounds}
onToggle={() => { toggleNotificationSounds(); updateState({}); }} onToggle={() => { toggleNotificationSounds(); updateState({}); }}
/> />
)} )}
content={<Text variant="b3">{t("Organisms.Settings.notifications_and_sound.desktop.description")}</Text>} content={<Text variant="b3">{t('Organisms.Settings.notifications_and_sound.desktop.description')}</Text>}
/> />
</div> </div>
); );
} }
function SecuritySection() { function SecuritySection() {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className="settings-security"> <div className="settings-security">
<div className="settings-security__card"> <div className="settings-security__card">
<MenuHeader>{t("Organisms.Settings.security.cross_signing.title")}</MenuHeader> <MenuHeader>{t('Organisms.Settings.security.cross_signing.title')}</MenuHeader>
<CrossSigning /> <CrossSigning />
<KeyBackup /> <KeyBackup />
</div> </div>
<DeviceManage /> <DeviceManage />
<div className="settings-security__card"> <div className="settings-security__card">
<MenuHeader>{t("Organisms.Settings.security.export_import_encryption_keys.title")}</MenuHeader> <MenuHeader>{t('Organisms.Settings.security.export_import_encryption_keys.title')}</MenuHeader>
<SettingTile <SettingTile
title={t("Organisms.Settings.security.export_encryption_keys.title")} title={t('Organisms.Settings.security.export_encryption_keys.title')}
content={( content={(
<> <>
<Text variant="b3">{t("Organisms.Settings.security.export_encryption_keys.description")}</Text> <Text variant="b3">{t('Organisms.Settings.security.export_encryption_keys.description')}</Text>
<ExportE2ERoomKeys /> <ExportE2ERoomKeys />
</> </>
)} )}
/> />
<SettingTile <SettingTile
title={t("Organisms.Settings.security.import_encryption_keys.title")} title={t('Organisms.Settings.security.import_encryption_keys.title')}
content={( content={(
<> <>
<Text variant="b3">{t("Organisms.Settings.security.import_encryption_keys.description")}</Text> <Text variant="b3">{t('Organisms.Settings.security.import_encryption_keys.description')}</Text>
<ImportE2ERoomKeys /> <ImportE2ERoomKeys />
</> </>
)} )}
@ -212,31 +211,30 @@ function SecuritySection() {
} }
function AboutSection() { function AboutSection() {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className="settings-about"> <div className="settings-about">
<div className="settings-about__card"> <div className="settings-about__card">
<MenuHeader>{t("Organisms.Settings.about.application")}</MenuHeader> <MenuHeader>{t('Organisms.Settings.about.application')}</MenuHeader>
<div className="settings-about__branding"> <div className="settings-about__branding">
<img width="60" height="60" src={CinnySVG} alt="Cinny logo" /> <img width="60" height="60" src={CinnySVG} alt="Cinny logo" />
<div> <div>
<Text variant="h2" weight="medium"> <Text variant="h2" weight="medium">
{t("common.cinny")} {t('common.cinny')}
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}`}</span> <span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}`}</span>
</Text> </Text>
<Text>{t("common.slogan")}</Text> <Text>{t('common.slogan')}</Text>
<div className="settings-about__btns"> <div className="settings-about__btns">
<Button onClick={() => window.open('https://github.com/ajbura/cinny')}>{t("common.source_code")}</Button> <Button onClick={() => window.open('https://github.com/ajbura/cinny')}>{t('common.source_code')}</Button>
<Button onClick={() => window.open('https://cinny.in/#sponsor')}>{t("common.sponsor")}</Button> <Button onClick={() => window.open('https://cinny.in/#sponsor')}>{t('common.sponsor')}</Button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div className="settings-about__card"> <div className="settings-about__card">
<MenuHeader>{t("Organisms.Settings.about.credits")}</MenuHeader> <MenuHeader>{t('Organisms.Settings.about.credits')}</MenuHeader>
<div className="settings-about__credits"> <div className="settings-about__credits">
<ul> <ul>
<li> <li>
@ -314,7 +312,7 @@ function Settings() {
const handleTabChange = (tabItem) => setSelectedTab(tabItem); const handleTabChange = (tabItem) => setSelectedTab(tabItem);
const handleLogout = async () => { const handleLogout = async () => {
if (await confirmDialog(t("Organisms.Settings.logout.dialog.title"), t("Organisms.Settings.logout.dialog.description"), t("Organisms.Settings.logout.dialog.confirm"), 'danger')) { if (await confirmDialog(t('Organisms.Settings.logout.dialog.title'), t('Organisms.Settings.logout.dialog.description'), t('Organisms.Settings.logout.dialog.confirm'), 'danger')) {
logout(); logout();
} }
}; };
@ -323,13 +321,13 @@ function Settings() {
<PopupWindow <PopupWindow
isOpen={isOpen} isOpen={isOpen}
className="settings-window" className="settings-window"
title={<Text variant="s1" weight="medium" primary>{t("Organisms.Settings.title")}</Text>} title={<Text variant="s1" weight="medium" primary>{t('Organisms.Settings.title')}</Text>}
contentOptions={( contentOptions={(
<> <>
<Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}> <Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}>
{t("Organisms.Settings.logout.title")} {t('Organisms.Settings.logout.title')}
</Button> </Button>
<IconButton src={CrossIC} onClick={requestClose} tooltip={t("common.close")} /> <IconButton src={CrossIC} onClick={requestClose} tooltip={t('common.close')} />
</> </>
)} )}
onRequestClose={requestClose} onRequestClose={requestClose}

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import './ShortcutSpaces.scss'; import './ShortcutSpaces.scss';
import { useTranslation } from 'react-i18next';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
@ -22,9 +23,7 @@ import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import { useSpaceShortcut } from '../../hooks/useSpaceShortcut'; import { useSpaceShortcut } from '../../hooks/useSpaceShortcut';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ShortcutSpacesContent() { function ShortcutSpacesContent() {
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -77,8 +76,6 @@ function ShortcutSpacesContent() {
const toggleSelected = () => toggleSelection(spaceId); const toggleSelected = () => toggleSelection(spaceId);
const deleteShortcut = () => deleteSpaceShortcut(spaceId); const deleteShortcut = () => deleteSpaceShortcut(spaceId);
const { t } = useTranslation();
return ( return (
<RoomSelector <RoomSelector
key={spaceId} key={spaceId}
@ -115,18 +112,18 @@ function ShortcutSpacesContent() {
return ( return (
<> <>
<Text className="shortcut-spaces__header" variant="b3" weight="bold">{t("Organisms.ShortcutSpaces.pinned_spaces")}</Text> <Text className="shortcut-spaces__header" variant="b3" weight="bold">{t('Organisms.ShortcutSpaces.pinned_spaces')}</Text>
{spaceShortcut.length === 0 && <Text>{t("Organisms.ShortcutSpaces.no_pinned_spaces")}</Text>} {spaceShortcut.length === 0 && <Text>{t('Organisms.ShortcutSpaces.no_pinned_spaces')}</Text>}
{spaceShortcut.map((spaceId) => renderSpace(spaceId, true))} {spaceShortcut.map((spaceId) => renderSpace(spaceId, true))}
<Text className="shortcut-spaces__header" variant="b3" weight="bold">{t("Organisms.ShortcutSpaces.unpinned_spaces")}</Text> <Text className="shortcut-spaces__header" variant="b3" weight="bold">{t('Organisms.ShortcutSpaces.unpinned_spaces')}</Text>
{spaceWithoutShortcut.length === 0 && <Text>{t("Organisms.ShortcutSpaces.no_unpinned_spaces")}</Text>} {spaceWithoutShortcut.length === 0 && <Text>{t('Organisms.ShortcutSpaces.no_unpinned_spaces')}</Text>}
{spaceWithoutShortcut.map((spaceId) => renderSpace(spaceId, false))} {spaceWithoutShortcut.map((spaceId) => renderSpace(spaceId, false))}
{selected.length !== 0 && ( {selected.length !== 0 && (
<div className="shortcut-spaces__footer"> <div className="shortcut-spaces__footer">
{process && <Spinner size="small" />} {process && <Spinner size="small" />}
<Text weight="medium">{process || t("Organisms.ShortcutSpaces.spaces_selected", {count: selected.length})}</Text> <Text weight="medium">{process || t('Organisms.ShortcutSpaces.spaces_selected', { count: selected.length })}</Text>
{ !process && ( { !process && (
<Button onClick={handleAdd} variant="primary">{t("Organisms.ShortcutSpaces.pin_button")}</Button> <Button onClick={handleAdd} variant="primary">{t('Organisms.ShortcutSpaces.pin_button')}</Button>
)} )}
</div> </div>
)} )}
@ -160,10 +157,10 @@ function ShortcutSpaces() {
className="shortcut-spaces" className="shortcut-spaces"
title={( title={(
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{t("Organisms.ShortcutSpaces.header")} {t('Organisms.ShortcutSpaces.header')}
</Text> </Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip= {t("common.close")}/>} contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t('common.close')} />}
onRequestClose={requestClose} onRequestClose={requestClose}
> >
{ {

View file

@ -3,6 +3,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './SpaceManage.scss'; import './SpaceManage.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -32,9 +33,7 @@ import InfoIC from '../../../../public/res/ic/outlined/info.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import { useStore } from '../../hooks/useStore'; import { useStore } from '../../hooks/useStore';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function SpaceManageBreadcrumb({ path, onSelect }) { function SpaceManageBreadcrumb({ path, onSelect }) {
return ( return (
@ -120,7 +119,12 @@ function SpaceManageItem({
const roomNameJSX = ( const roomNameJSX = (
<Text> <Text>
{twemojify(name)} {twemojify(name)}
<Text variant="b3" span> {t("Organisms.SpaceManage.room_members", {count: roomInfo.num_joined_members})}</Text> <Text variant="b3" span>
{' '}
{' '}
{t('Organisms.SpaceManage.room_members', { count: roomInfo.num_joined_members })}
</Text>
</Text> </Text>
); );
@ -148,13 +152,13 @@ function SpaceManageItem({
> >
{roomAvatarJSX} {roomAvatarJSX}
{roomNameJSX} {roomNameJSX}
{isSuggested && <Text variant="b2">{t("Organisms.SpaceManage.suggested")}</Text>} {isSuggested && <Text variant="b2">{t('Organisms.SpaceManage.suggested')}</Text>}
</button> </button>
{roomInfo.topic && expandBtnJsx} {roomInfo.topic && expandBtnJsx}
{ {
isJoined isJoined
? <Button onClick={handleOpen}>{t("common.open")}</Button> ? <Button onClick={handleOpen}>{t('common.open')}</Button>
: <Button variant="primary" onClick={handleJoin} disabled={isJoining}>{t(isJoining ? "common.joining" : "common.join")}</Button> : <Button variant="primary" onClick={handleJoin} disabled={isJoining}>{t(isJoining ? 'common.joining' : 'common.join')}</Button>
} }
</div> </div>
{isExpand && roomInfo.topic && <Text variant="b2">{twemojify(roomInfo.topic, undefined, true)}</Text>} {isExpand && roomInfo.topic && <Text variant="b2">{twemojify(roomInfo.topic, undefined, true)}</Text>}
@ -162,7 +166,6 @@ function SpaceManageItem({
); );
} }
SpaceManageItem.propTypes = { SpaceManageItem.propTypes = {
parentId: PropTypes.string.isRequired, parentId: PropTypes.string.isRequired,
roomHierarchy: PropTypes.shape({}).isRequired, roomHierarchy: PropTypes.shape({}).isRequired,
@ -187,15 +190,15 @@ function SpaceManageFooter({ parentId, selected }) {
}); });
const handleRemove = () => { const handleRemove = () => {
setProcess(t("Organisms.SpaceManage.remove", {count: selected.length})); setProcess(t('Organisms.SpaceManage.remove', { count: selected.length }));
selected.forEach((roomId) => { selected.forEach((roomId) => {
mx.sendStateEvent(parentId, 'm.space.child', {}, roomId); mx.sendStateEvent(parentId, 'm.space.child', {}, roomId);
}); });
}; };
const handleToggleSuggested = (isMark) => { const handleToggleSuggested = (isMark) => {
if (isMark) setProcess(t("Organisms.SpaceManage.mark_suggested", {count: selected.length})); if (isMark) setProcess(t('Organisms.SpaceManage.mark_suggested', { count: selected.length }));
else setProcess(t("Organisms.SpaceManage.mark_not_suggested", {count: selected.length})); else setProcess(t('Organisms.SpaceManage.mark_not_suggested', { count: selected.length }));
selected.forEach((roomId) => { selected.forEach((roomId) => {
const sEvent = room.currentState.getStateEvents('m.space.child', roomId); const sEvent = room.currentState.getStateEvents('m.space.child', roomId);
if (!sEvent) return; if (!sEvent) return;
@ -210,10 +213,10 @@ function SpaceManageFooter({ parentId, selected }) {
return ( return (
<div className="space-manage__footer"> <div className="space-manage__footer">
{process && <Spinner size="small" />} {process && <Spinner size="small" />}
<Text weight="medium">{process || t("Organisms.SpaceManage.items_selected", {count: selected.length})}</Text> <Text weight="medium">{process || t('Organisms.SpaceManage.items_selected', { count: selected.length })}</Text>
{ !process && ( { !process && (
<> <>
<Button onClick={handleRemove} variant="danger">{t("common.remove")}</Button> <Button onClick={handleRemove} variant="danger">{t('common.remove')}</Button>
<Button <Button
onClick={() => handleToggleSuggested(!allSuggested)} onClick={() => handleToggleSuggested(!allSuggested)}
variant={allSuggested ? 'surface' : 'primary'} variant={allSuggested ? 'surface' : 'primary'}
@ -292,7 +295,6 @@ function useChildUpdate(roomId, roomsHierarchy) {
} }
function SpaceManageContent({ roomId, requestClose }) { function SpaceManageContent({ roomId, requestClose }) {
const { t } = useTranslation(); const { t } = useTranslation();
const mx = initMatrix.matrixClient; const mx = initMatrix.matrixClient;
@ -352,11 +354,11 @@ function SpaceManageContent({ roomId, requestClose }) {
{spacePath.length > 1 && ( {spacePath.length > 1 && (
<SpaceManageBreadcrumb path={spacePath} onSelect={addPathItem} /> <SpaceManageBreadcrumb path={spacePath} onSelect={addPathItem} />
)} )}
<Text variant="b3" weight="bold">{t("Organisms.SpaceManage.rooms_and_spaces")}</Text> <Text variant="b3" weight="bold">{t('Organisms.SpaceManage.rooms_and_spaces')}</Text>
<div className="space-manage__content-items"> <div className="space-manage__content-items">
{!isLoading && currentHierarchy?.rooms?.length === 1 && ( {!isLoading && currentHierarchy?.rooms?.length === 1 && (
<Text> <Text>
{t("Organisms.SpaceManage.private_rooms_message")} {t('Organisms.SpaceManage.private_rooms_message')}
</Text> </Text>
)} )}
{currentHierarchy && (currentHierarchy.rooms?.map((roomInfo) => ( {currentHierarchy && (currentHierarchy.rooms?.map((roomInfo) => (
@ -375,15 +377,15 @@ function SpaceManageContent({ roomId, requestClose }) {
/> />
) )
)))} )))}
{!currentHierarchy && <Text>{t("common.loading")}</Text>} {!currentHierarchy && <Text>{t('common.loading')}</Text>}
</div> </div>
{currentHierarchy?.canLoadMore && !isLoading && ( {currentHierarchy?.canLoadMore && !isLoading && (
<Button onClick={loadRoomHierarchy}>{t("Organisms.SpaceManage.load_more")}</Button> <Button onClick={loadRoomHierarchy}>{t('Organisms.SpaceManage.load_more')}</Button>
)} )}
{isLoading && ( {isLoading && (
<div className="space-manage__content-loading"> <div className="space-manage__content-loading">
<Spinner size="small" /> <Spinner size="small" />
<Text>{t("common.loading")}</Text> <Text>{t('common.loading')}</Text>
</div> </div>
)} )}
{selected.length > 0 && ( {selected.length > 0 && (
@ -428,7 +430,12 @@ function SpaceManage() {
title={( title={(
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{roomId && twemojify(room.name)} {roomId && twemojify(room.name)}
<span style={{ color: 'var(--tc-surface-low)' }}> {t("Organisms.SpaceManage.subtitle")}</span> <span style={{ color: 'var(--tc-surface-low)' }}>
{' '}
{' '}
{t('Organisms.SpaceManage.subtitle')}
</span>
</Text> </Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip="Close" />} contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip="Close" />}

View file

@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './SpaceSettings.scss'; import './SpaceSettings.scss';
import { useTranslation } from 'react-i18next';
import { twemojify } from '../../../util/twemojify'; import { twemojify } from '../../../util/twemojify';
import initMatrix from '../../../client/initMatrix'; import initMatrix from '../../../client/initMatrix';
@ -39,9 +40,7 @@ import CategoryFilledIC from '../../../../public/res/ic/filled/category.svg';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog'; import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import { useForceUpdate } from '../../hooks/useForceUpdate'; import { useForceUpdate } from '../../hooks/useForceUpdate';
import '../../i18n';
import '../../i18n.jsx'
import { useTranslation } from 'react-i18next';
const tabText = { const tabText = {
GENERAL: 'General', GENERAL: 'General',
@ -74,7 +73,7 @@ function GeneralSettings({ roomId }) {
return ( return (
<> <>
<div className="room-settings__card"> <div className="room-settings__card">
<MenuHeader>{t("common.options")}</MenuHeader> <MenuHeader>{t('common.options')}</MenuHeader>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
if (isCategorized) unCategorizeSpace(roomId); if (isCategorized) unCategorizeSpace(roomId);
@ -83,7 +82,7 @@ function GeneralSettings({ roomId }) {
}} }}
iconSrc={isCategorized ? CategoryFilledIC : CategoryIC} iconSrc={isCategorized ? CategoryFilledIC : CategoryIC}
> >
{isCategorized ? t("Organisms.SpaceSettings.uncategorize_subspaces") : t("Organisms.SpaceSettings.categorize_subspaces")} {isCategorized ? t('Organisms.SpaceSettings.uncategorize_subspaces') : t('Organisms.SpaceSettings.categorize_subspaces')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
onClick={() => { onClick={() => {
@ -93,30 +92,30 @@ function GeneralSettings({ roomId }) {
}} }}
iconSrc={isPinned ? PinFilledIC : PinIC} iconSrc={isPinned ? PinFilledIC : PinIC}
> >
{isPinned ? t("Organisms.SpaceSettings.unpin_sidebar") : t("Organisms.SpaceSettings.pin_sidebar")} {isPinned ? t('Organisms.SpaceSettings.unpin_sidebar') : t('Organisms.SpaceSettings.pin_sidebar')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
variant="danger" variant="danger"
onClick={async () => { onClick={async () => {
const isConfirmed = await confirmDialog( const isConfirmed = await confirmDialog(
t("Organisms.SpaceSettings.leave.leave_dialog_title"), t('Organisms.SpaceSettings.leave.leave_dialog_title'),
t("Organisms.SpaceSettings.leave.leave_dialog_message", {space: roomName}), t('Organisms.SpaceSettings.leave.leave_dialog_message', { space: roomName }),
t("Organisms.SpaceSettings.leave.leave_space"), t('Organisms.SpaceSettings.leave.leave_space'),
'danger', 'danger',
); );
if (isConfirmed) leave(roomId); if (isConfirmed) leave(roomId);
}} }}
iconSrc={LeaveArrowIC} iconSrc={LeaveArrowIC}
> >
{t("Organisms.SpaceSettings.leave.leave_space")} {t('Organisms.SpaceSettings.leave.leave_space')}
</MenuItem> </MenuItem>
</div> </div>
<div className="space-settings__card"> <div className="space-settings__card">
<MenuHeader>{t("Organisms.SpaceSettings.visibility.header")}</MenuHeader> <MenuHeader>{t('Organisms.SpaceSettings.visibility.header')}</MenuHeader>
<RoomVisibility roomId={roomId} /> <RoomVisibility roomId={roomId} />
</div> </div>
<div className="space-settings__card"> <div className="space-settings__card">
<MenuHeader>{t("Organisms.SpaceSettings.addresses.header")}</MenuHeader> <MenuHeader>{t('Organisms.SpaceSettings.addresses.header')}</MenuHeader>
<RoomAliases roomId={roomId} /> <RoomAliases roomId={roomId} />
</div> </div>
</> </>
@ -169,10 +168,15 @@ function SpaceSettings() {
title={( title={(
<Text variant="s1" weight="medium" primary> <Text variant="s1" weight="medium" primary>
{isOpen && twemojify(room.name)} {isOpen && twemojify(room.name)}
<span style={{ color: 'var(--tc-surface-low)' }}> {t("Organisms.SpaceSettings.subtitle")}</span> <span style={{ color: 'var(--tc-surface-low)' }}>
{' '}
{' '}
{t('Organisms.SpaceSettings.subtitle')}
</span>
</Text> </Text>
)} )}
contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={requestClose} tooltip={t('common.close')} />}
onRequestClose={requestClose} onRequestClose={requestClose}
> >
{isOpen && ( {isOpen && (

View file

@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import './ViewSource.scss'; import './ViewSource.scss';
import { useTranslation } from 'react-i18next';
import cons from '../../../client/state/cons'; import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation'; import navigation from '../../../client/state/navigation';
@ -12,8 +13,7 @@ import PopupWindow from '../../molecules/popup-window/PopupWindow';
import CrossIC from '../../../../public/res/ic/outlined/cross.svg'; import CrossIC from '../../../../public/res/ic/outlined/cross.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function ViewSourceBlock({ title, json }) { function ViewSourceBlock({ title, json }) {
return ( return (
@ -56,18 +56,18 @@ function ViewSource() {
const renderViewSource = () => ( const renderViewSource = () => (
<div className="view-source"> <div className="view-source">
{event.isEncrypted() && <ViewSourceBlock title={t("Organisms.ViewSource.decrypted_source")} json={event.getEffectiveEvent()} />} {event.isEncrypted() && <ViewSourceBlock title={t('Organisms.ViewSource.decrypted_source')} json={event.getEffectiveEvent()} />}
<ViewSourceBlock title={t("Organisms.ViewSource.original_source")} json={event.event} /> <ViewSourceBlock title={t('Organisms.ViewSource.original_source')} json={event.event} />
</div> </div>
); );
return ( return (
<PopupWindow <PopupWindow
isOpen={isOpen} isOpen={isOpen}
title={t("Organisms.ViewSource.title")} title={t('Organisms.ViewSource.title')}
onAfterClose={handleAfterClose} onAfterClose={handleAfterClose}
onRequestClose={() => setIsOpen(false)} onRequestClose={() => setIsOpen(false)}
contentOptions={<IconButton src={CrossIC} onClick={() => setIsOpen(false)} tooltip={t("common.close")} />} contentOptions={<IconButton src={CrossIC} onClick={() => setIsOpen(false)} tooltip={t('common.close')} />}
> >
{event ? renderViewSource() : <div />} {event ? renderViewSource() : <div />}
</PopupWindow> </PopupWindow>

View file

@ -1,16 +1,15 @@
import React from 'react'; import React from 'react';
import './Welcome.scss'; import './Welcome.scss';
import { useTranslation } from 'react-i18next';
import Text from '../../atoms/text/Text'; import Text from '../../atoms/text/Text';
import CinnySvg from '../../../../public/res/svg/cinny.svg'; import CinnySvg from '../../../../public/res/svg/cinny.svg';
import '../../i18n.jsx' import '../../i18n';
import { useTranslation } from 'react-i18next';
function Welcome() { function Welcome() {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
return ( return (
<div className="app-welcome flex--center"> <div className="app-welcome flex--center">

View file

@ -68,9 +68,7 @@ async function verifyEmail(baseUrl, email, client_secret, send_attempt, next_lin
return data; return data;
} }
async function completeRegisterStage( async function completeRegisterStage(baseUrl, username, password, auth) {
baseUrl, username, password, auth,
) {
const tempClient = createTemporaryClient(baseUrl); const tempClient = createTemporaryClient(baseUrl);
try { try {

View file

@ -177,7 +177,6 @@ function unpackMegolmKeyFile(data) {
return decodeBase64(fileStr.slice(dataStart, dataEnd)); return decodeBase64(fileStr.slice(dataStart, dataEnd));
} }
/** /**
* ascii-armour a megolm key file * ascii-armour a megolm key file
* *
@ -196,7 +195,7 @@ function packMegolmKeyFile(data) {
let o = 0; let o = 0;
let i; let i;
for (i = 1; i <= nLines; i += 1) { for (i = 1; i <= nLines; i += 1) {
lines[i] = encodeBase64(data.subarray(o, o+LINE_LENGTH)); lines[i] = encodeBase64(data.subarray(o, o + LINE_LENGTH));
o += LINE_LENGTH; o += LINE_LENGTH;
} }
lines[i] = TRAILER_LINE; lines[i] = TRAILER_LINE;
@ -305,11 +304,11 @@ export async function encryptMegolmKeyFile(data, password, options) {
encodedData, encodedData,
); );
} catch (e) { } catch (e) {
throw friendlyError('subtleCrypto.encrypt failed: ' + e, cryptoFailMsg()); throw friendlyError(`subtleCrypto.encrypt failed: ${e}`, cryptoFailMsg());
} }
const cipherArray = new Uint8Array(ciphertext); const cipherArray = new Uint8Array(ciphertext);
const bodyLength = (1+salt.length+iv.length+4+cipherArray.length+32); const bodyLength = (1 + salt.length + iv.length + 4 + cipherArray.length + 32);
const resultBuffer = new Uint8Array(bodyLength); const resultBuffer = new Uint8Array(bodyLength);
let idx = 0; let idx = 0;
resultBuffer[idx++] = 1; // version resultBuffer[idx++] = 1; // version
@ -331,7 +330,7 @@ export async function encryptMegolmKeyFile(data, password, options) {
toSign, toSign,
); );
} catch (e) { } catch (e) {
throw friendlyError('subtleCrypto.sign failed: ' + e, cryptoFailMsg()); throw friendlyError(`subtleCrypto.sign failed: ${e}`, cryptoFailMsg());
} }
const hmacArray = new Uint8Array(hmac); const hmacArray = new Uint8Array(hmac);

View file

@ -39,9 +39,9 @@ export function getUsernameOfRoomMember(roomMember) {
return roomMember.name || roomMember.userId; return roomMember.name || roomMember.userId;
} }
export function getUserDisplayName(room, userId){ export function getUserDisplayName(room, userId) {
if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId)); if (room?.getMember(userId)) return getUsernameOfRoomMember(room.getMember(userId));
return getUsername(userId); return getUsername(userId);
} }
export async function isRoomAliasAvailable(alias) { export async function isRoomAliasAvailable(alias) {