Translate SettingsPage

This commit is contained in:
Paul Bienkowski 2022-07-24 19:06:56 +02:00
parent 76943fb1f0
commit fe7d7ce274
3 changed files with 235 additions and 120 deletions

View file

@ -1,178 +1,208 @@
import React from 'react'
import {connect} from 'react-redux'
import {Message, Icon, Grid, Form, Button, TextArea, Ref, Input, Header, Divider, Popup} from 'semantic-ui-react'
import {useForm} from 'react-hook-form'
import React from "react";
import { connect } from "react-redux";
import {
Message,
Icon,
Grid,
Form,
Button,
TextArea,
Ref,
Input,
Header,
Divider,
Popup,
} from "semantic-ui-react";
import { useForm } from "react-hook-form";
import Markdown from "react-markdown";
import { useTranslation } from "react-i18next";
import {setLogin} from 'reducers/login'
import {Page, Stats} from 'components'
import api from 'api'
import {findInput} from 'utils'
import {useConfig} from 'config'
import { setLogin } from "reducers/login";
import { Page, Stats } from "components";
import api from "api";
import { findInput } from "utils";
import { useConfig } from "config";
const SettingsPage = connect((state) => ({login: state.login}), {setLogin})(function SettingsPage({login, setLogin}) {
const {register, handleSubmit} = useForm()
const [loading, setLoading] = React.useState(false)
const [errors, setErrors] = React.useState(null)
const SettingsPage = connect((state) => ({ login: state.login }), { setLogin })(
function SettingsPage({ login, setLogin }) {
const { t } = useTranslation();
const { register, handleSubmit } = useForm();
const [loading, setLoading] = React.useState(false);
const [errors, setErrors] = React.useState(null);
const onSave = React.useCallback(
async (changes) => {
setLoading(true)
setErrors(null)
const onSave = React.useCallback(
async (changes) => {
setLoading(true);
setErrors(null);
try {
const response = await api.put("/user", { body: changes });
setLogin(response);
} catch (err) {
setErrors(err.errors);
} finally {
setLoading(false);
}
},
[setLoading, setLogin, setErrors]
);
const onGenerateNewKey = React.useCallback(async () => {
setLoading(true);
setErrors(null);
try {
const response = await api.put('/user', {body: changes})
setLogin(response)
const response = await api.put("/user", {
body: { updateApiKey: true },
});
setLogin(response);
} catch (err) {
setErrors(err.errors)
setErrors(err.errors);
} finally {
setLoading(false)
setLoading(false);
}
},
[setLoading, setLogin, setErrors]
)
}, [setLoading, setLogin, setErrors]);
const onGenerateNewKey = React.useCallback(async () => {
setLoading(true)
setErrors(null)
try {
const response = await api.put('/user', {body: {updateApiKey: true}})
setLogin(response)
} catch (err) {
setErrors(err.errors)
} finally {
setLoading(false)
}
}, [setLoading, setLogin, setErrors])
return (
<Page title={t("SettingsPage.title")}>
<Grid centered relaxed divided stackable>
<Grid.Row>
<Grid.Column width={8}>
<Header as="h2">{t("SettingsPage.profile.title")}</Header>
return (
<Page title="Settings">
<Grid centered relaxed divided stackable>
<Grid.Row>
<Grid.Column width={8}>
<Header as="h2">My profile</Header>
<Message info>{t("SettingsPage.profile.publicNotice")}</Message>
<Message info>All of this information is public.</Message>
<Form onSubmit={handleSubmit(onSave)} loading={loading}>
<Ref innerRef={findInput(register)}>
<Form.Input
error={errors?.username}
label="Username"
name="username"
defaultValue={login.username}
disabled
/>
</Ref>
<Form.Field error={errors?.bio}>
<label>Bio</label>
<Ref innerRef={register}>
<TextArea name="bio" rows={4} defaultValue={login.bio} />
</Ref>
</Form.Field>
<Form.Field error={errors?.image}>
<label>Avatar URL</label>
<Form onSubmit={handleSubmit(onSave)} loading={loading}>
<Ref innerRef={findInput(register)}>
<Input name="image" defaultValue={login.image} />
<Form.Input
error={errors?.username}
label={t("SettingsPage.profile.username.label")}
name="username"
defaultValue={login.username}
disabled
/>
</Ref>
</Form.Field>
<Form.Field error={errors?.bio}>
<label>{t("SettingsPage.profile.bio.label")}</label>
<Ref innerRef={register}>
<TextArea name="bio" rows={4} defaultValue={login.bio} />
</Ref>
</Form.Field>
<Form.Field error={errors?.image}>
<label>{t("SettingsPage.profile.avatarUrl.label")}</label>
<Ref innerRef={findInput(register)}>
<Input name="image" defaultValue={login.image} />
</Ref>
</Form.Field>
<Button type="submit" primary>
Save
</Button>
</Form>
</Grid.Column>
<Grid.Column width={6}>
<ApiKeyDialog {...{login, onGenerateNewKey}} />
<Button type="submit" primary>
{t("general.save")}
</Button>
</Form>
</Grid.Column>
<Grid.Column width={6}>
<ApiKeyDialog {...{ login, onGenerateNewKey }} />
<Divider />
<Divider />
<Stats user={login.username} />
</Grid.Column>
</Grid.Row>
</Grid>
</Page>
)
})
<Stats user={login.username} />
</Grid.Column>
</Grid.Row>
</Grid>
</Page>
);
}
);
function CopyInput({value, ...props}) {
const [success, setSuccess] = React.useState(null)
function CopyInput({ value, ...props }) {
const { t } = useTranslation();
const [success, setSuccess] = React.useState(null);
const onClick = async () => {
try {
await window.navigator?.clipboard?.writeText(value)
setSuccess(true)
await window.navigator?.clipboard?.writeText(value);
setSuccess(true);
} catch (err) {
setSuccess(false)
setSuccess(false);
} finally {
setTimeout(() => {
setSuccess(null)
}, 2000)
setSuccess(null);
}, 2000);
}
}
};
return (
<Popup
trigger={<Input {...props} value={value} fluid action={{icon: 'copy', onClick}} />}
trigger={
<Input
{...props}
value={value}
fluid
action={{ icon: "copy", onClick }}
/>
}
position="top right"
open={success != null}
content={success ? 'Copied.' : 'Failed to copy.'}
content={success ? t('general.copied') : t('general.copyError')}
/>
)
);
}
const selectField = findInput((ref) => ref?.select())
const selectField = findInput((ref) => ref?.select());
function ApiKeyDialog({login, onGenerateNewKey}) {
const config = useConfig()
const [show, setShow] = React.useState(false)
function ApiKeyDialog({ login, onGenerateNewKey }) {
const { t } = useTranslation();
const config = useConfig();
const [show, setShow] = React.useState(false);
const onClick = React.useCallback(
(e) => {
e.preventDefault()
setShow(true)
e.preventDefault();
setShow(true);
},
[setShow]
)
);
const onGenerateNewKeyInner = React.useCallback(
(e) => {
e.preventDefault()
onGenerateNewKey()
e.preventDefault();
onGenerateNewKey();
},
[onGenerateNewKey]
)
);
return (
<>
<Header as="h2">My API Key</Header>
<p>
Here you find your API Key, for use in the OpenBikeSensor. You can to copy and paste it into your sensor's
configuration interface to allow direct upload from the device.
</p>
<p>Please protect your API Key carefully as it allows full control over your account.</p>
<div style={{minHeight: 40, marginBottom: 16}}>
<Header as="h2">{t("SettingsPage.apiKey.title")}</Header>
<Markdown>{t("SettingsPage.apiKey.description")}</Markdown>
<div style={{ minHeight: 40, marginBottom: 16 }}>
{show ? (
login.apiKey ? (
<Ref innerRef={selectField}>
<CopyInput label="Personal API Key" value={login.apiKey} />
<CopyInput
label={t("SettingsPage.apiKey.key.label")}
value={login.apiKey}
/>
</Ref>
) : (
<Message warning content="You have no API Key, please generate one below." />
<Message warning content={t("SettingsPage.apiKey.key.empty")} />
)
) : (
<Button onClick={onClick}>
<Icon name="lock" /> Show API Key
<Icon name="lock" /> {t("SettingsPage.apiKey.key.show")}
</Button>
)}
</div>
<p>The API URL should be set to:</p>
<div style={{marginBottom: 16}}>
<CopyInput label="API URL" value={config?.apiUrl?.replace(/\/api$/, '') ?? '...'} />
<Markdown>{t("SettingsPage.apiKey.urlDescription")}</Markdown>
<div style={{ marginBottom: 16 }}>
<CopyInput
label={t("SettingsPage.apiKey.url.label")}
value={config?.apiUrl?.replace(/\/api$/, "") ?? "..."}
/>
</div>
<p>
You can generate a new API Key here, which will invalidate the old one, disconnecting all devices you used it on
from your account.
</p>
<Button onClick={onGenerateNewKeyInner}>Generate new API key</Button>
<Markdown>{t("SettingsPage.apiKey.generateDescription")}</Markdown>
<p></p>
<Button onClick={onGenerateNewKeyInner}>
{t("SettingsPage.apiKey.generate")}
</Button>
</>
)
);
}
export default SettingsPage
export default SettingsPage;

View file

@ -179,3 +179,45 @@ MapPage:
southWest: südwestwärts
west: westwärts
northWest: nordwestwärts
SettingsPage:
title: Einstellungen
profile:
title: Mein Profil
publicNotice: All diese Informationen sind öffentlich.
username:
label: Kontoname
bio:
label: Bio
avatarUrl:
label: Avatar URL
apiKey:
title: Mein API-Schlüssel
description: |
Hier findest du deinen API-Schlüssel für die Nutzung mit dem
OpenBikeSensor. Du kannst ihn dir herauskopieren und in der Seite für die
Einstellungen deines Geräts einfügen, um dann direkt vom Gerät aus
Fahrten hochladen zu können.
Bitte schütze deinen API-Schlüssel vor ungewolltem Zugriff, denn er
erlaubt Zugang zu deinem Konto auf dieser Seite.
urlDescription: |
Die API-URL sollte wie folgt gesetzt werden:
generateDescription: |
Hier kannst du einen neuen API-Schlüssel für dein Konto erstellen. Der
alte wird damit ungültig, und alle Geräte, die damit konfiguriert wurden,
können nicht mehr auf dein Konto zugreifen.
key:
label: Persönlicher API-Schlüssel
empty: Du hast keinen API-Schlüssel, kannst dir aber unten einen erstellen.
show: API-Schlüssel zeigen
url:
label: API URL
generate: Neuen API-Schlüssel erstellen

View file

@ -11,6 +11,10 @@ general:
private: Private
show: Show
edit: Edit
save: Save
copied: Copied.
copyError: Failed to copy.
App:
footer:
@ -183,3 +187,42 @@ MapPage:
southWest: south-west bound
west: west bound
northWest: north-west bound
SettingsPage:
title: Settings
profile:
title: My profile
publicNotice: All of this information is public.
username:
label: Username
bio:
label: Bio
avatarUrl:
label: Avatar URL
apiKey:
title: My API Key
description: |
Here you find your API Key, for use in the OpenBikeSensor. You can to
copy and paste it into your sensor's configuration interface to allow
direct upload from the device.
Please protect your API Key carefully as it allows full control over
your account.
urlDescription: |
The API URL should be set to:
generateDescription: |
You can generate a new API Key here, which will invalidate the old one,
disconnecting all devices you used it on from your account.
key:
label: Personal API Key
empty: You have no API Key, please generate one below.
show: Show API Key
url:
label: API URL
generate: Generate new API key