diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/App.jsx | 31 | ||||
-rw-r--r-- | src/common/attributes/useServerAttributes.js | 8 | ||||
-rw-r--r-- | src/common/attributes/useUserAttributes.js | 4 | ||||
-rw-r--r-- | src/common/components/TermsDialog.jsx | 34 | ||||
-rw-r--r-- | src/resources/l10n/en.json | 5 |
5 files changed, 77 insertions, 5 deletions
diff --git a/src/App.jsx b/src/App.jsx index 4fe34f64..b6e864b2 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -6,9 +6,10 @@ import makeStyles from '@mui/styles/makeStyles'; import BottomMenu from './common/components/BottomMenu'; import SocketController from './SocketController'; import CachingController from './CachingController'; -import { useEffectAsync } from './reactHelper'; +import { useCatch, useEffectAsync } from './reactHelper'; import { sessionActions } from './store'; import UpdateController from './UpdateController'; +import TermsDialog from './common/components/TermsDialog'; const useStyles = makeStyles(() => ({ page: { @@ -29,10 +30,24 @@ const App = () => { const desktop = useMediaQuery(theme.breakpoints.up('md')); const newServer = useSelector((state) => state.session.server.newServer); - const initialized = useSelector((state) => !!state.session.user); + const termsUrl = useSelector((state) => state.session.server.attributes.termsUrl); + const user = useSelector((state) => state.session.user); + + const acceptTerms = useCatch(async () => { + const response = await fetch(`/api/users/${user.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...user, attributes: { ...user.attributes, termsAccepted: true } }), + }); + if (response.ok) { + dispatch(sessionActions.updateUser(await response.json())); + } else { + throw Error(await response.text()); + } + }); useEffectAsync(async () => { - if (!initialized) { + if (!user) { const response = await fetch('/api/session'); if (response.ok) { dispatch(sessionActions.updateUser(await response.json())); @@ -43,9 +58,15 @@ const App = () => { } } return null; - }, [initialized]); + }, [user]); - return !initialized ? (<LinearProgress />) : ( + if (user == null) { + return (<LinearProgress />); + } + if (termsUrl && !user.attributes.termsAccepted) { + return (<TermsDialog open onCancel={() => navigate('/login')} onAccept={() => acceptTerms()} />); + } + return ( <> <SocketController /> <CachingController /> diff --git a/src/common/attributes/useServerAttributes.js b/src/common/attributes/useServerAttributes.js index 80ac3c7d..f46f715c 100644 --- a/src/common/attributes/useServerAttributes.js +++ b/src/common/attributes/useServerAttributes.js @@ -39,6 +39,14 @@ export default (t) => useMemo(() => ({ name: t('settingsDarkMode'), type: 'boolean', }, + termsUrl: { + name: t('userTerms'), + type: 'string', + }, + privacyUrl: { + name: t('userPrivacy'), + type: 'string', + }, totpEnable: { name: t('settingsTotpEnable'), type: 'boolean', diff --git a/src/common/attributes/useUserAttributes.js b/src/common/attributes/useUserAttributes.js index 81230884..e819412c 100644 --- a/src/common/attributes/useUserAttributes.js +++ b/src/common/attributes/useUserAttributes.js @@ -57,4 +57,8 @@ export default (t) => useMemo(() => ({ name: t('attributeMailSmtpPassword'), type: 'string', }, + termsAccepted: { + name: t('userTermsAccepted'), + type: 'boolean', + }, }), [t]); diff --git a/src/common/components/TermsDialog.jsx b/src/common/components/TermsDialog.jsx new file mode 100644 index 00000000..4b909415 --- /dev/null +++ b/src/common/components/TermsDialog.jsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { useSelector } from 'react-redux'; +import { Button, Dialog, DialogActions, DialogContent, DialogContentText, Link } from '@mui/material'; +import { useTranslation } from './LocalizationProvider'; + +const TermsDialog = ({ open, onCancel, onAccept }) => { + const t = useTranslation(); + + const termsUrl = useSelector((state) => state.session.server.attributes.termsUrl); + const privacyUrl = useSelector((state) => state.session.server.attributes.privacyUrl); + + return ( + <Dialog + open={open} + onClose={onCancel} + > + <DialogContent> + <DialogContentText> + {t('userTermsPrompt')} + <ul> + <li><Link href={termsUrl} target="_blank">{t('userTerms')}</Link></li> + <li><Link href={privacyUrl} target="_blank">{t('userPrivacy')}</Link></li> + </ul> + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={onCancel}>{t('sharedCancel')}</Button> + <Button onClick={onAccept}>{t('sharedAccept')}</Button> + </DialogActions> + </Dialog> + ); +}; + +export default TermsDialog; diff --git a/src/resources/l10n/en.json b/src/resources/l10n/en.json index a9303f63..945dfe4a 100644 --- a/src/resources/l10n/en.json +++ b/src/resources/l10n/en.json @@ -4,6 +4,7 @@ "sharedSave": "Save", "sharedUpload": "Upload", "sharedSet": "Set", + "sharedAccept": "Accept", "sharedCancel": "Cancel", "sharedCopy": "Copy", "sharedAdd": "Add", @@ -179,6 +180,10 @@ "userToken": "Token", "userDeleteAccount": "Delete Account", "userTemporary": "Temporary", + "userTerms": "Terms of Service", + "userPrivacy": "Privacy Policy", + "userTermsPrompt": "By clicking Accept, you agree to our Terms of Service and confirm that you have read our Privacy Policy.", + "userTermsAccepted": "Terms Accepted", "loginTitle": "Login", "loginLanguage": "Language", "loginReset": "Reset Password", |