diff --git a/frontend/src/Components/CommentComponent.tsx b/frontend/src/Components/CommentComponent.tsx
index 938bc7ca..cfbd339a 100644
--- a/frontend/src/Components/CommentComponent.tsx
+++ b/frontend/src/Components/CommentComponent.tsx
@@ -15,6 +15,7 @@ import {useInterpreter} from '../API/use/useInterpreter';
import OutsideClickHandler from 'react-outside-click-handler';
import {AltTranslateButton, AnnotateButton, TranslateButton} from './ContentButtons';
import {InviewContext} from '../Pages/PostPage';
+import {getPreferredLang, getShowInlineTranslateButton} from './UserProfileSettings';
interface CommentProps {
comment: CommentInfo;
@@ -135,7 +136,7 @@ interface ControlsProps {
setAltContent: (value: string | undefined) => void;
}
-function Controls({contentRef, comment, setEditingText, hideRating, onEdit, onAnswer, answerOpen, setAnswerOpen, setAltContent}: ControlsProps) {
+function Controls({contentRef, comment, setEditingText, hideRating, onEdit, onAnswer, answerOpen, setAnswerOpen, setAltContent: setCommentAltContent}: ControlsProps) {
const api = useAPI();
const handleVote = useMemo(() => {
@@ -168,7 +169,10 @@ function Controls({contentRef, comment, setEditingText, hideRating, onEdit, onAn
e.preventDefault();
setAnswerOpen(!answerOpen);
};
- useEffect(() => setAltContent(altContent), [altContent]);
+ useEffect(() => setCommentAltContent(altContent), [setCommentAltContent, altContent]);
+ const showTranslateButtonInline = useMemo(() => {
+ return getShowInlineTranslateButton() && comment.language !== getPreferredLang();
+ }, [comment]);
return (
@@ -179,9 +183,9 @@ function Controls({contentRef, comment, setEditingText, hideRating, onEdit, onAn
{comment.canEdit && onEdit &&
}
- {currentMode === 'translate' &&
+ {(showTranslateButtonInline || currentMode === 'translate') &&
-
+
}
{currentMode === 'altTranslate' &&
@@ -196,7 +200,7 @@ function Controls({contentRef, comment, setEditingText, hideRating, onEdit, onAn
{showOptions &&
setShowOptions(false)}>
-
{setShowOptions(false);translate();}} isActive={currentMode === 'translate'} />
+ {!showTranslateButtonInline && {setShowOptions(false);translate();}} isActive={currentMode === 'translate'} />}
{calcShowAltTranslate() &&
{setShowOptions(false);altTranslate();}} isActive={currentMode === 'altTranslate'}/>}
{calcShowAnnotate() &&
diff --git a/frontend/src/Components/PostComponent.tsx b/frontend/src/Components/PostComponent.tsx
index 6a4b5da3..4c719bc4 100644
--- a/frontend/src/Components/PostComponent.tsx
+++ b/frontend/src/Components/PostComponent.tsx
@@ -17,6 +17,7 @@ import {SignatureComponent} from './SignatureComponent';
import Conf from '../Conf';
import {useInterpreter} from '../API/use/useInterpreter';
import {AltTranslateButton, AnnotateButton, TranslateButton, UnwatchButton, WatchButton} from './ContentButtons';
+import {getPreferredLang, getShowInlineTranslateButton} from './UserProfileSettings';
interface PostComponentProps {
post: PostInfo;
@@ -135,6 +136,9 @@ export default function PostComponent(props: PostComponentProps) {
const altMode = currentMode !== undefined || inProgress;
const autoCut = altMode ? undefined : props.autoCut;
+ const showTranslateButtonInline = useMemo(() => {
+ return getShowInlineTranslateButton() && props.post.language !== getPreferredLang();
+ }, [props.post]);
return (
@@ -172,9 +176,9 @@ export default function PostComponent(props: PostComponentProps) {
{/*
*/}
{props.post.canEdit && props.onEdit &&
}
- {currentMode === 'translate' &&
+ {(showTranslateButtonInline || currentMode === 'translate') &&
-
+
}
{currentMode === 'altTranslate' &&
@@ -189,7 +193,7 @@ export default function PostComponent(props: PostComponentProps) {
{showOptions &&
setShowOptions(false)}>
-
{setShowOptions(false);translate();}} isActive={currentMode === 'translate'} />
+ {!showTranslateButtonInline && {setShowOptions(false);translate();}} isActive={currentMode === 'translate'} />}
{calcShowAltTranslate() &&
{setShowOptions(false);altTranslate();}} isActive={currentMode === 'altTranslate'}/>}
{calcShowAnnotate() &&
diff --git a/frontend/src/Components/UserProfileSettings.tsx b/frontend/src/Components/UserProfileSettings.tsx
index db985aa5..eecf56b9 100644
--- a/frontend/src/Components/UserProfileSettings.tsx
+++ b/frontend/src/Components/UserProfileSettings.tsx
@@ -1,24 +1,28 @@
-import React, {useEffect} from 'react';
-import buttonStyles from '../Components/Buttons.module.scss';
-import {ReactComponent as LogoutIcon} from '../Assets/logout.svg';
-import {ReactComponent as UserIcon} from '../Assets/user.svg';
-import {useAPI, useAppState} from '../AppState/AppState';
+import React, {useEffect, useState} from 'react';
import {useLocation, useNavigate} from 'react-router-dom';
-import ThemeToggleComponent from './ThemeToggleComponent';
-import {BarmaliniAccessResult, UserGender} from '../Types/UserInfo';
-import {toast} from 'react-toastify';
-import {observer} from 'mobx-react-lite';
-import styles from './UserProfileSettings.module.scss';
+
import classNames from 'classnames';
+import {observer} from 'mobx-react-lite';
import {confirmAlert} from 'react-confirm-alert';
+import {toast} from 'react-toastify';
+
import {useUserProfile} from '../API/use/useUserProfile';
+import {useAPI, useAppState} from '../AppState/AppState';
+import {BarmaliniAccessResult, UserGender} from '../Types/UserInfo';
import {SecretMailKeyGeneratorForm} from './SecretMailbox';
+import ThemeToggleComponent from './ThemeToggleComponent';
+
+import {ReactComponent as LogoutIcon} from '../Assets/logout.svg';
+import {ReactComponent as UserIcon} from '../Assets/user.svg';
+import {ReactComponent as TranslateIcon} from '../Assets/translate.svg';
+import buttonStyles from '../Components/Buttons.module.scss';
+import styles from './UserProfileSettings.module.scss';
type UserProfileSettingsProps = {
- onChange: any;
- gender: UserGender;
- barmaliniAccess?: boolean;
- isBarmalini?: boolean;
+ onChange: () => void;
+ gender: UserGender;
+ barmaliniAccess?: boolean;
+ isBarmalini?: boolean;
};
const languages = new Map([
@@ -52,57 +56,65 @@ export function getLegacyZoom(): boolean {
export function getPreferredLang(): string {
return localStorage.getItem('preferredLang') || 'ru';
}
+export function getShowInlineTranslateButton(): boolean {
+ return localStorage.getItem('showInlineTranslateButton') === 'true';
+}
export default function UserProfileSettings(props: UserProfileSettingsProps) {
- useEffect(() => {
- window.scrollTo({ top: 0 });
- }, []);
-
- const api = useAPI();
- const navigate = useNavigate();
- const location = useLocation();
-
- let gender = props.gender;
-
-const [autoStop, setAutoStop] = React.useState(getVideoAutopause());
-const [legacyZoom, setLegacyZoom] = React.useState(getLegacyZoom());
-const [preferredLang, setPreferredLang] = React.useState(getPreferredLang());
-
-const confirmWrapper = (message: string, callback: () => void) => (e: React.MouseEvent) => {
- e.preventDefault();
- confirmAlert({
- title: 'Астанавитесь! Подумайте!',
- message,
- buttons: [
- {
- label: 'Да!',
- onClick: callback
- },
- {
- label: 'Отмена',
- className: 'cancel'
- }
- ],
- overlayClassName: 'orbitar-confirm-overlay'
- });
-};
+ useEffect(() => {
+ window.scrollTo({top: 0});
+ }, []);
+
+ const api = useAPI();
+ const navigate = useNavigate();
+ const location = useLocation();
+
+ let gender = props.gender;
+
+ const [autoStop, setAutoStop] = useState(getVideoAutopause());
+ const [legacyZoom, setLegacyZoom] = useState(getLegacyZoom());
+ const [preferredLang, setPreferredLang] = useState(getPreferredLang());
+ const [showInlineTranslateButton, setShowInlineTranslateButton] =
+ useState(getShowInlineTranslateButton());
+
+ const confirmWrapper = (message: string, callback: () => void) => (e: React.MouseEvent) => {
+ e.preventDefault();
+ confirmAlert({
+ title: 'Астанавитесь! Подумайте!',
+ message,
+ buttons: [
+ {
+ label: 'Да!',
+ onClick: callback,
+ },
+ {
+ label: 'Отмена',
+ className: 'cancel',
+ },
+ ],
+ overlayClassName: 'orbitar-confirm-overlay',
+ });
+ };
- const handleLogout = confirmWrapper(
- 'Вы действительно хотите выйти? Вы будете вынуждены войти в аккаунт заново.', () => {
- api.auth.signOut().then(() => {
- navigate(location.pathname);
- });
- });
+ const handleLogout = confirmWrapper(
+ 'Вы действительно хотите выйти? Вы будете вынуждены войти в аккаунт заново.',
+ () => {
+ api.auth.signOut().then(() => {
+ navigate(location.pathname);
+ });
+ },
+ );
- const handleResetSessions = confirmWrapper(
- `Вы действительно хотите сбросить пароль и все сессии?
+ const handleResetSessions = confirmWrapper(
+ `Вы действительно хотите сбросить пароль и все сессии?
Вы будете разлогинены на ВСЕХ устройствах, текущий пароль больше не будет работать.
- На почту использованную при регистрации (у вас же всё ещё есть доступ к ней?) придет ссылка для сброса пароля через которую вы сможете войти в аккаунт заново и установить новый пароль.`, () => {
- api.auth.dropPasswordAndSessions().then(() => {
- navigate(location.pathname);
- });
- }
- );
+ На почту использованную при регистрации (у вас же всё ещё есть доступ к ней?) придет ссылка для сброса пароля через которую вы сможете войти в аккаунт заново и установить новый пароль.`,
+ () => {
+ api.auth.dropPasswordAndSessions().then(() => {
+ navigate(location.pathname);
+ });
+ },
+ );
const toggleAutoStop = () => {
setAutoStop(!autoStop);
@@ -112,30 +124,37 @@ const confirmWrapper = (message: string, callback: () => void) => (e: React.Mous
setLegacyZoom(!legacyZoom);
};
- const changeLang = (ev: React.FormEvent) => {
- const lang = ev.currentTarget.value;
- setPreferredLang(lang);
- };
-
- const handleGenderChange = (e: React.MouseEvent) => {
- e.preventDefault();
- if (gender === undefined) {
- return;
- }
-
- if (gender === UserGender.fluid) {
- gender = UserGender.he;
- } else if (gender === UserGender.he) {
- gender = UserGender.she;
- } else {
- gender = UserGender.fluid;
- }
- api.userAPI.saveGender(gender).then((data) => {
- props.onChange();
- }).catch((error) => {
- toast.error(error?.message || 'Не удалось сохранить.');
- });
- };
+ const toggleShowInlineTranslateButton = () => {
+ setShowInlineTranslateButton(!showInlineTranslateButton);
+ };
+
+ const changeLang = (ev: React.FormEvent) => {
+ const lang = ev.currentTarget.value;
+ setPreferredLang(lang);
+ };
+
+ const handleGenderChange = (e: React.MouseEvent) => {
+ e.preventDefault();
+ if (gender === undefined) {
+ return;
+ }
+
+ if (gender === UserGender.fluid) {
+ gender = UserGender.he;
+ } else if (gender === UserGender.he) {
+ gender = UserGender.she;
+ } else {
+ gender = UserGender.fluid;
+ }
+ api.userAPI
+ .saveGender(gender)
+ .then(() => {
+ props.onChange();
+ })
+ .catch((error) => {
+ toast.error(error?.message || 'Не удалось сохранить.');
+ });
+ };
useEffect(() => {
localStorage.setItem('autoStopVideos', JSON.stringify(autoStop));
@@ -145,6 +164,10 @@ const confirmWrapper = (message: string, callback: () => void) => (e: React.Mous
localStorage.setItem('legacyZoom', JSON.stringify(legacyZoom));
}, [legacyZoom]);
+ useEffect(() => {
+ localStorage.setItem('showInlineTranslateButton', JSON.stringify(showInlineTranslateButton));
+ }, [showInlineTranslateButton]);
+
useEffect(() => {
localStorage.setItem('preferredLang', preferredLang);
}, [preferredLang]);
@@ -152,40 +175,53 @@ const confirmWrapper = (message: string, callback: () => void) => (e: React.Mous
return (
<>
- {gender !== undefined &&
- }
+ {gender !== undefined && (
+
+ )}
- {}
+ {}
Язык перевода:
+
+
{/**/}
{props.barmaliniAccess && }
- {!props.isBarmalini && }
-
+ {!props.isBarmalini && (
+
+ )}
+
>
);
}
-
/**
* Component that displays the button, when clicked,
* the button turns into a div with the password/token (got from the server via userAPi.getBarmaliniPassword)
@@ -195,24 +231,28 @@ const BarmaliniAccess = observer(() => {
const api = useAPI();
const [access, setAccess] = React.useState();
-
const handleCopy = (e: React.MouseEvent) => {
e.preventDefault();
if (access === undefined) {
return;
}
- navigator.clipboard?.writeText(access.password)
- ?.then(() => toast('В буфере!'))?.catch();
+ navigator.clipboard
+ ?.writeText(access.password)
+ ?.then(() => toast('В буфере!'))
+ ?.catch();
};
const handleShowPassword = (e: React.MouseEvent) => {
e.preventDefault();
if (!access) {
- api.userAPI.getBarmaliniAccess().then((data) => {
- setAccess(data);
- }).catch((error) => {
- toast.error(error?.message || 'Не удалось получить пароль.');
- });
+ api.userAPI
+ .getBarmaliniAccess()
+ .then((data) => {
+ setAccess(data);
+ })
+ .catch((error) => {
+ toast.error(error?.message || 'Не удалось получить пароль.');
+ });
} else {
setAccess(undefined);
}
@@ -233,16 +273,30 @@ const BarmaliniAccess = observer(() => {
return (
- {access &&
+ {(access && (
- Логин: {access.login}
-
Пароль:
-
{access.password}
-
+
+ Логин: {access.login}
+
+
+ Пароль:
+
+ {access.password}
+
+
+
+
+
+ Счастливого бармаления. Пароль истекает через час.
+
-
Счастливого бармаления. Пароль истекает через час.
-
||
}
+ )) || (
+
+ )}
);
});
@@ -272,25 +326,29 @@ export const MailboxSettings = observer(() => {
e.preventDefault();
confirmAlert({
title: 'Астанавитесь! Подумайте!',
- message: ('Вы действительно хотите удалить почтовый ящик? Вы больше не сможете получать новые шифровки, ' +
- 'но вы сможете читать старые шифровки, адресованные вам.'),
+ message:
+ 'Вы действительно хотите удалить почтовый ящик? Вы больше не сможете получать новые шифровки, ' +
+ 'но вы сможете читать старые шифровки, адресованные вам.',
buttons: [
{
label: 'Да!',
onClick: () => {
- api.userAPI.savePublicKey('').then(() => {
- refreshProfile();
- }).catch((error) => {
- toast.error(error?.message || 'Не удалось удалить почтовый ящик.');
- });
- }
+ api.userAPI
+ .savePublicKey('')
+ .then(() => {
+ refreshProfile();
+ })
+ .catch((error) => {
+ toast.error(error?.message || 'Не удалось удалить почтовый ящик.');
+ });
+ },
},
{
label: 'Отмена',
- className: 'cancel'
- }
+ className: 'cancel',
+ },
],
- overlayClassName: 'orbitar-confirm-overlay'
+ overlayClassName: 'orbitar-confirm-overlay',
});
};
@@ -298,53 +356,93 @@ export const MailboxSettings = observer(() => {
e.preventDefault();
if (publicKey) {
// Copy to clipboard
- navigator.clipboard?.writeText(publicKey)
- ?.then(() => toast('В буфере!'))?.catch();
+ navigator.clipboard
+ ?.writeText(publicKey)
+ ?.then(() => toast('В буфере!'))
+ ?.catch();
}
};
const handleCreatePublicKey = (key: string) => {
- api.userAPI.savePublicKey(key).then(() => {
- refreshProfile();
- }).catch((error) => {
- toast.error(error?.message || 'Не удалось создать почтовый ящик.');
- }).finally(() => {
- setCreatingMailbox(false);
- });
+ api.userAPI
+ .savePublicKey(key)
+ .then(() => {
+ refreshProfile();
+ })
+ .catch((error) => {
+ toast.error(error?.message || 'Не удалось создать почтовый ящик.');
+ })
+ .finally(() => {
+ setCreatingMailbox(false);
+ });
};
- return <>{state.status === 'ready' &&
-
- {publicKey &&
- <>{/*mailbox exists*/}
-
-
- Почтовый ящик готов!
-
-
-
- {!revealPublicKey &&
}
- {revealPublicKey &&
- Публичный ключ:
- {publicKey}
-
}
-
- > ||
-
{/*Mailbox doesn't exist*/}
-
-
}
-
|| null}
- {creatingMailbox && setCreatingMailbox(false)}
- />
}
- >;
+ return (
+ <>
+ {(state.status === 'ready' && (
+
+ {(publicKey && (
+ <>
+ {/*mailbox exists*/}
+
+
+ Почтовый ящик готов!
+
+
+
+ {!revealPublicKey && (
+
+ )}
+ {revealPublicKey && (
+
+ Публичный ключ:
+
+
+ {publicKey}
+
+
+ )}
+
+ >
+ )) || (
+
+ {/*Mailbox doesn't exist*/}
+
+
+ )}
+
+ )) ||
+ null}
+ {creatingMailbox && (
+
+ setCreatingMailbox(false)}
+ />
+
+ )}
+ >
+ );
});