fix: Add translation hooks on remaining files (#2311)
This commit is contained in:
@ -3,6 +3,7 @@ import * as Sentry from "@sentry/react";
|
||||
import { observable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { withTranslation, type TFunction, Trans } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import Button from "components/Button";
|
||||
import CenteredContent from "components/CenteredContent";
|
||||
@ -11,10 +12,11 @@ import PageTitle from "components/PageTitle";
|
||||
import { githubIssuesUrl } from "../../shared/utils/routeHelpers";
|
||||
import env from "env";
|
||||
|
||||
type Props = {
|
||||
type Props = {|
|
||||
children: React.Node,
|
||||
reloadOnChunkMissing?: boolean,
|
||||
};
|
||||
t: TFunction,
|
||||
|};
|
||||
|
||||
@observer
|
||||
class ErrorBoundary extends React.Component<Props> {
|
||||
@ -55,6 +57,8 @@ class ErrorBoundary extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
|
||||
if (this.error) {
|
||||
const error = this.error;
|
||||
const isReported = !!env.SENTRY_DSN && env.DEPLOYMENT === "hosted";
|
||||
@ -63,15 +67,21 @@ class ErrorBoundary extends React.Component<Props> {
|
||||
if (isChunkError) {
|
||||
return (
|
||||
<CenteredContent>
|
||||
<PageTitle title="Module failed to load" />
|
||||
<h1>Loading Failed</h1>
|
||||
<PageTitle title={t("Module failed to load")} />
|
||||
<h1>
|
||||
<Trans>Loading Failed</Trans>
|
||||
</h1>
|
||||
<HelpText>
|
||||
Sorry, part of the application failed to load. This may be because
|
||||
it was updated since you opened the tab or because of a failed
|
||||
network request. Please try reloading.
|
||||
<Trans>
|
||||
Sorry, part of the application failed to load. This may be
|
||||
because it was updated since you opened the tab or because of a
|
||||
failed network request. Please try reloading.
|
||||
</Trans>
|
||||
</HelpText>
|
||||
<p>
|
||||
<Button onClick={this.handleReload}>Reload</Button>
|
||||
<Button onClick={this.handleReload}>
|
||||
<Trans>Reload</Trans>
|
||||
</Button>
|
||||
</p>
|
||||
</CenteredContent>
|
||||
);
|
||||
@ -79,23 +89,32 @@ class ErrorBoundary extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<CenteredContent>
|
||||
<PageTitle title="Something Unexpected Happened" />
|
||||
<h1>Something Unexpected Happened</h1>
|
||||
<PageTitle title={t("Something Unexpected Happened")} />
|
||||
<h1>
|
||||
<Trans>Something Unexpected Happened</Trans>
|
||||
</h1>
|
||||
<HelpText>
|
||||
Sorry, an unrecoverable error occurred
|
||||
{isReported && " – our engineers have been notified"}. Please try
|
||||
reloading the page, it may have been a temporary glitch.
|
||||
<Trans
|
||||
defaults="Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch."
|
||||
values={{
|
||||
notified: isReported
|
||||
? ` – ${t("our engineers have been notified")}`
|
||||
: undefined,
|
||||
}}
|
||||
/>
|
||||
</HelpText>
|
||||
{this.showDetails && <Pre>{error.toString()}</Pre>}
|
||||
<p>
|
||||
<Button onClick={this.handleReload}>Reload</Button>{" "}
|
||||
<Button onClick={this.handleReload}>
|
||||
<Trans>Reload</Trans>
|
||||
</Button>{" "}
|
||||
{this.showDetails ? (
|
||||
<Button onClick={this.handleReportBug} neutral>
|
||||
Report a Bug…
|
||||
<Trans>Report a Bug</Trans>…
|
||||
</Button>
|
||||
) : (
|
||||
<Button onClick={this.handleShowDetails} neutral>
|
||||
Show Details…
|
||||
<Trans>Show Detail</Trans>…
|
||||
</Button>
|
||||
)}
|
||||
</p>
|
||||
@ -114,4 +133,4 @@ const Pre = styled.pre`
|
||||
white-space: pre-wrap;
|
||||
`;
|
||||
|
||||
export default ErrorBoundary;
|
||||
export default withTranslation()<ErrorBoundary>(ErrorBoundary);
|
||||
|
@ -1,58 +1,58 @@
|
||||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import styled, { withTheme } from "styled-components";
|
||||
import UiStore from "stores/UiStore";
|
||||
import Editor from "components/Editor";
|
||||
import HelpText from "components/HelpText";
|
||||
import { LabelText, Outline } from "components/Input";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {|
|
||||
label: string,
|
||||
minHeight?: number,
|
||||
maxHeight?: number,
|
||||
readOnly?: boolean,
|
||||
ui: UiStore,
|
||||
|};
|
||||
|
||||
@observer
|
||||
class InputRich extends React.Component<Props> {
|
||||
@observable editorComponent: React.ComponentType<any>;
|
||||
@observable focused: boolean = false;
|
||||
function InputRich({ label, minHeight, maxHeight, ...rest }: Props) {
|
||||
const [focused, setFocused] = React.useState<boolean>(false);
|
||||
const { ui } = useStores();
|
||||
|
||||
handleBlur = () => {
|
||||
this.focused = false;
|
||||
};
|
||||
const handleBlur = React.useCallback(() => {
|
||||
setFocused(false);
|
||||
}, []);
|
||||
|
||||
handleFocus = () => {
|
||||
this.focused = true;
|
||||
};
|
||||
const handleFocus = React.useCallback(() => {
|
||||
setFocused(true);
|
||||
}, []);
|
||||
|
||||
render() {
|
||||
const { label, minHeight, maxHeight, ui, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<LabelText>{label}</LabelText>
|
||||
<StyledOutline
|
||||
maxHeight={maxHeight}
|
||||
minHeight={minHeight}
|
||||
focused={this.focused}
|
||||
return (
|
||||
<>
|
||||
<LabelText>{label}</LabelText>
|
||||
<StyledOutline
|
||||
maxHeight={maxHeight}
|
||||
minHeight={minHeight}
|
||||
focused={focused}
|
||||
>
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<HelpText>
|
||||
<Trans>Loading editor</Trans>…
|
||||
</HelpText>
|
||||
}
|
||||
>
|
||||
<React.Suspense fallback={<HelpText>Loading editor…</HelpText>}>
|
||||
<Editor
|
||||
onBlur={this.handleBlur}
|
||||
onFocus={this.handleFocus}
|
||||
ui={ui}
|
||||
grow
|
||||
{...rest}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</StyledOutline>
|
||||
</>
|
||||
);
|
||||
}
|
||||
<Editor
|
||||
onBlur={handleBlur}
|
||||
onFocus={handleFocus}
|
||||
ui={ui}
|
||||
grow
|
||||
{...rest}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</StyledOutline>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const StyledOutline = styled(Outline)`
|
||||
@ -67,4 +67,4 @@ const StyledOutline = styled(Outline)`
|
||||
}
|
||||
`;
|
||||
|
||||
export default inject("ui")(withTheme(InputRich));
|
||||
export default observer(withTheme(InputRich));
|
||||
|
@ -1,14 +1,10 @@
|
||||
// @flow
|
||||
import { observable, action } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import { LinkIcon, CloseIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { Link, withRouter, type RouterHistory } from "react-router-dom";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import PoliciesStore from "stores/PoliciesStore";
|
||||
import UiStore from "stores/UiStore";
|
||||
import UsersStore from "stores/UsersStore";
|
||||
import Button from "components/Button";
|
||||
import CopyToClipboard from "components/CopyToClipboard";
|
||||
import Flex from "components/Flex";
|
||||
@ -16,198 +12,210 @@ import HelpText from "components/HelpText";
|
||||
import Input from "components/Input";
|
||||
import NudeButton from "components/NudeButton";
|
||||
import Tooltip from "components/Tooltip";
|
||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||
import useCurrentUser from "hooks/useCurrentUser";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
const MAX_INVITES = 20;
|
||||
|
||||
type Props = {
|
||||
auth: AuthStore,
|
||||
users: UsersStore,
|
||||
history: RouterHistory,
|
||||
policies: PoliciesStore,
|
||||
ui: UiStore,
|
||||
type Props = {|
|
||||
onSubmit: () => void,
|
||||
};
|
||||
|};
|
||||
|
||||
type InviteRequest = {
|
||||
email: string,
|
||||
name: string,
|
||||
};
|
||||
|
||||
@observer
|
||||
class Invite extends React.Component<Props> {
|
||||
@observable isSaving: boolean;
|
||||
@observable linkCopied: boolean = false;
|
||||
@observable
|
||||
invites: InviteRequest[] = [
|
||||
function Invite({ onSubmit }: Props) {
|
||||
const [isSaving, setIsSaving] = React.useState();
|
||||
const [linkCopied, setLinkCopied] = React.useState<boolean>(false);
|
||||
const [invites, setInvites] = React.useState<InviteRequest[]>([
|
||||
{ email: "", name: "" },
|
||||
{ email: "", name: "" },
|
||||
{ email: "", name: "" },
|
||||
];
|
||||
]);
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
this.isSaving = true;
|
||||
const { users, policies, ui } = useStores();
|
||||
const user = useCurrentUser();
|
||||
const team = useCurrentTeam();
|
||||
const { t } = useTranslation();
|
||||
|
||||
try {
|
||||
await this.props.users.invite(this.invites);
|
||||
this.props.onSubmit();
|
||||
this.props.ui.showToast("We sent out your invites!", { type: "success" });
|
||||
} catch (err) {
|
||||
this.props.ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
this.isSaving = false;
|
||||
}
|
||||
};
|
||||
const predictedDomain = user.email.split("@")[1];
|
||||
const can = policies.abilities(team.id);
|
||||
|
||||
@action
|
||||
handleChange = (ev, index) => {
|
||||
this.invites[index][ev.target.name] = ev.target.value;
|
||||
};
|
||||
const handleSubmit = React.useCallback(
|
||||
async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
setIsSaving(true);
|
||||
|
||||
@action
|
||||
handleGuestChange = (ev, index) => {
|
||||
this.invites[index][ev.target.name] = ev.target.checked;
|
||||
};
|
||||
try {
|
||||
await users.invite(invites);
|
||||
onSubmit();
|
||||
ui.showToast(t("We sent out your invites!"), { type: "success" });
|
||||
} catch (err) {
|
||||
ui.showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
},
|
||||
[onSubmit, ui, invites, t, users]
|
||||
);
|
||||
|
||||
@action
|
||||
handleAdd = () => {
|
||||
if (this.invites.length >= MAX_INVITES) {
|
||||
this.props.ui.showToast(
|
||||
`Sorry, you can only send ${MAX_INVITES} invites at a time`,
|
||||
const handleChange = React.useCallback((ev, index) => {
|
||||
setInvites((prevInvites) => {
|
||||
const newInvites = [...prevInvites];
|
||||
newInvites[index][ev.target.name] = ev.target.value;
|
||||
return newInvites;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleAdd = React.useCallback(() => {
|
||||
if (invites.length >= MAX_INVITES) {
|
||||
ui.showToast(
|
||||
t("Sorry, you can only send {{MAX_INVITES}} invites at a time", {
|
||||
MAX_INVITES,
|
||||
}),
|
||||
{ type: "warning" }
|
||||
);
|
||||
}
|
||||
|
||||
this.invites.push({ email: "", name: "" });
|
||||
};
|
||||
setInvites((prevInvites) => {
|
||||
const newInvites = [...prevInvites];
|
||||
newInvites.push({ email: "", name: "" });
|
||||
return newInvites;
|
||||
});
|
||||
}, [ui, invites, t]);
|
||||
|
||||
@action
|
||||
handleRemove = (ev: SyntheticEvent<>, index: number) => {
|
||||
ev.preventDefault();
|
||||
this.invites.splice(index, 1);
|
||||
};
|
||||
const handleRemove = React.useCallback(
|
||||
(ev: SyntheticEvent<>, index: number) => {
|
||||
ev.preventDefault();
|
||||
|
||||
handleCopy = () => {
|
||||
this.linkCopied = true;
|
||||
this.props.ui.showToast("Share link copied", {
|
||||
setInvites((prevInvites) => {
|
||||
const newInvites = [...prevInvites];
|
||||
newInvites.splice(index, 1);
|
||||
return newInvites;
|
||||
});
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleCopy = React.useCallback(() => {
|
||||
setLinkCopied(true);
|
||||
ui.showToast(t("Share link copied"), {
|
||||
type: "success",
|
||||
});
|
||||
};
|
||||
}, [ui, t]);
|
||||
|
||||
render() {
|
||||
const { team, user } = this.props.auth;
|
||||
if (!team || !user) return null;
|
||||
|
||||
const predictedDomain = user.email.split("@")[1];
|
||||
const can = this.props.policies.abilities(team.id);
|
||||
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
{team.guestSignin ? (
|
||||
<HelpText>
|
||||
Invite team members or guests to join your knowledge base. Team
|
||||
members can sign in with {team.signinMethods} or use their email
|
||||
address.
|
||||
</HelpText>
|
||||
) : (
|
||||
<HelpText>
|
||||
Invite team members to join your knowledge base. They will need to
|
||||
sign in with {team.signinMethods}.{" "}
|
||||
{can.update && (
|
||||
<>
|
||||
As an admin you can also{" "}
|
||||
<Link to="/settings/security">enable email sign-in</Link>.
|
||||
</>
|
||||
)}
|
||||
</HelpText>
|
||||
)}
|
||||
{team.subdomain && (
|
||||
<CopyBlock>
|
||||
<Flex align="flex-end">
|
||||
<Input
|
||||
type="text"
|
||||
value={team.url}
|
||||
label="Want a link to share directly with your team?"
|
||||
readOnly
|
||||
flex
|
||||
/>
|
||||
|
||||
<CopyToClipboard text={team.url} onCopy={this.handleCopy}>
|
||||
<Button
|
||||
type="button"
|
||||
icon={<LinkIcon />}
|
||||
style={{ marginBottom: "16px" }}
|
||||
neutral
|
||||
>
|
||||
{this.linkCopied ? "Link copied" : "Copy link"}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</Flex>
|
||||
<p>
|
||||
<hr />
|
||||
</p>
|
||||
</CopyBlock>
|
||||
)}
|
||||
{this.invites.map((invite, index) => (
|
||||
<Flex key={index}>
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
{team.guestSignin ? (
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="Invite team members or guests to join your knowledge base. Team members can sign in with {{signinMethods}} or use their email address."
|
||||
values={{ signinMethods: team.signinMethods }}
|
||||
/>
|
||||
</HelpText>
|
||||
) : (
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="Invite team members to join your knowledge base. They will need to sign in with {{signinMethods}}."
|
||||
values={{ signinMethods: team.signinMethods }}
|
||||
/>{" "}
|
||||
{can.update && (
|
||||
<Trans>
|
||||
As an admin you can also{" "}
|
||||
<Link to="/settings/security">enable email sign-in</Link>.
|
||||
</Trans>
|
||||
)}
|
||||
</HelpText>
|
||||
)}
|
||||
{team.subdomain && (
|
||||
<CopyBlock>
|
||||
<Flex align="flex-end">
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
label="Email"
|
||||
labelHidden={index !== 0}
|
||||
onChange={(ev) => this.handleChange(ev, index)}
|
||||
placeholder={`example@${predictedDomain}`}
|
||||
value={invite.email}
|
||||
required={index === 0}
|
||||
autoFocus={index === 0}
|
||||
type="text"
|
||||
value={team.url}
|
||||
label={t("Want a link to share directly with your team?")}
|
||||
readOnly
|
||||
flex
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
label="Full name"
|
||||
labelHidden={index !== 0}
|
||||
onChange={(ev) => this.handleChange(ev, index)}
|
||||
value={invite.name}
|
||||
required={!!invite.email}
|
||||
flex
|
||||
/>
|
||||
{index !== 0 && (
|
||||
<Remove>
|
||||
<Tooltip tooltip="Remove invite" placement="top">
|
||||
<NudeButton onClick={(ev) => this.handleRemove(ev, index)}>
|
||||
<CloseIcon />
|
||||
</NudeButton>
|
||||
</Tooltip>
|
||||
</Remove>
|
||||
)}
|
||||
<CopyToClipboard text={team.url} onCopy={handleCopy}>
|
||||
<Button
|
||||
type="button"
|
||||
icon={<LinkIcon />}
|
||||
style={{ marginBottom: "16px" }}
|
||||
neutral
|
||||
>
|
||||
{linkCopied ? t("Link copied") : t("Copy link")}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</Flex>
|
||||
))}
|
||||
|
||||
<Flex justify="space-between">
|
||||
{this.invites.length <= MAX_INVITES ? (
|
||||
<Button type="button" onClick={this.handleAdd} neutral>
|
||||
Add another…
|
||||
</Button>
|
||||
) : (
|
||||
<span />
|
||||
<p>
|
||||
<hr />
|
||||
</p>
|
||||
</CopyBlock>
|
||||
)}
|
||||
{invites.map((invite, index) => (
|
||||
<Flex key={index}>
|
||||
<Input
|
||||
type="email"
|
||||
name="email"
|
||||
label={t("Email")}
|
||||
labelHidden={index !== 0}
|
||||
onChange={(ev) => handleChange(ev, index)}
|
||||
placeholder={`example@${predictedDomain}`}
|
||||
value={invite.email}
|
||||
required={index === 0}
|
||||
autoFocus={index === 0}
|
||||
flex
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
name="name"
|
||||
label={t("Full name")}
|
||||
labelHidden={index !== 0}
|
||||
onChange={(ev) => handleChange(ev, index)}
|
||||
value={invite.name}
|
||||
required={!!invite.email}
|
||||
flex
|
||||
/>
|
||||
{index !== 0 && (
|
||||
<Remove>
|
||||
<Tooltip tooltip={t("Remove invite")} placement="top">
|
||||
<NudeButton onClick={(ev) => handleRemove(ev, index)}>
|
||||
<CloseIcon />
|
||||
</NudeButton>
|
||||
</Tooltip>
|
||||
</Remove>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={this.isSaving}
|
||||
data-on="click"
|
||||
data-event-category="invite"
|
||||
data-event-action="sendInvites"
|
||||
>
|
||||
{this.isSaving ? "Inviting…" : "Send Invites"}
|
||||
</Button>
|
||||
</Flex>
|
||||
<br />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
))}
|
||||
|
||||
<Flex justify="space-between">
|
||||
{invites.length <= MAX_INVITES ? (
|
||||
<Button type="button" onClick={handleAdd} neutral>
|
||||
<Trans>Add another</Trans>…
|
||||
</Button>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSaving}
|
||||
data-on="click"
|
||||
data-event-category="invite"
|
||||
data-event-action="sendInvites"
|
||||
>
|
||||
{isSaving ? `${t("Inviting")}…` : t("Send Invites")}
|
||||
</Button>
|
||||
</Flex>
|
||||
<br />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
const CopyBlock = styled("div")`
|
||||
@ -221,4 +229,4 @@ const Remove = styled("div")`
|
||||
right: -32px;
|
||||
`;
|
||||
|
||||
export default inject("auth", "users", "policies", "ui")(withRouter(Invite));
|
||||
export default observer(Invite);
|
||||
|
@ -1,63 +1,64 @@
|
||||
// @flow
|
||||
import { observable } from "mobx";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import AuthStore from "stores/AuthStore";
|
||||
import UiStore from "stores/UiStore";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import Button from "components/Button";
|
||||
import Flex from "components/Flex";
|
||||
import HelpText from "components/HelpText";
|
||||
import Modal from "components/Modal";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {
|
||||
auth: AuthStore,
|
||||
ui: UiStore,
|
||||
type Props = {|
|
||||
onRequestClose: () => void,
|
||||
};
|
||||
|};
|
||||
|
||||
@observer
|
||||
class UserDelete extends React.Component<Props> {
|
||||
@observable isDeleting: boolean;
|
||||
function UserDelete({ onRequestClose }: Props) {
|
||||
const [isDeleting, setIsDeleting] = React.useState();
|
||||
const { auth, ui } = useStores();
|
||||
const { t } = useTranslation();
|
||||
|
||||
handleSubmit = async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
this.isDeleting = true;
|
||||
const handleSubmit = React.useCallback(
|
||||
async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
setIsDeleting(true);
|
||||
|
||||
try {
|
||||
await this.props.auth.deleteUser();
|
||||
this.props.auth.logout();
|
||||
} catch (error) {
|
||||
this.props.ui.showToast(error.message, { type: "error" });
|
||||
} finally {
|
||||
this.isDeleting = false;
|
||||
}
|
||||
};
|
||||
try {
|
||||
await auth.deleteUser();
|
||||
auth.logout();
|
||||
} catch (error) {
|
||||
ui.showToast(error.message, { type: "error" });
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
},
|
||||
[auth, ui]
|
||||
);
|
||||
|
||||
render() {
|
||||
const { onRequestClose } = this.props;
|
||||
|
||||
return (
|
||||
<Modal isOpen title="Delete Account" onRequestClose={onRequestClose}>
|
||||
<Flex column>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<HelpText>
|
||||
return (
|
||||
<Modal isOpen title={t("Delete Account")} onRequestClose={onRequestClose}>
|
||||
<Flex column>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HelpText>
|
||||
<Trans>
|
||||
Are you sure? Deleting your account will destroy identifying data
|
||||
associated with your user and cannot be undone. You will be
|
||||
immediately logged out of Outline and all your API tokens will be
|
||||
revoked.
|
||||
</HelpText>
|
||||
<HelpText>
|
||||
<strong>Note:</strong> Signing back in will cause a new account to
|
||||
be automatically reprovisioned.
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
{this.isDeleting ? "Deleting…" : "Delete My Account"}
|
||||
</Button>
|
||||
</form>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
</Trans>
|
||||
</HelpText>
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="<em>Note:</em> Signing back in will cause a new account to be automatically reprovisioned."
|
||||
components={{ em: <strong /> }}
|
||||
/>
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
{isDeleting ? `${t("Deleting")}…` : t("Delete My Account")}
|
||||
</Button>
|
||||
</form>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default inject("auth", "ui")(UserDelete);
|
||||
export default observer(UserDelete);
|
||||
|
@ -95,10 +95,20 @@
|
||||
"Tip notice": "Tip notice",
|
||||
"Warning": "Warning",
|
||||
"Warning notice": "Warning notice",
|
||||
"Module failed to load": "Module failed to load",
|
||||
"Loading Failed": "Loading Failed",
|
||||
"Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.": "Sorry, part of the application failed to load. This may be because it was updated since you opened the tab or because of a failed network request. Please try reloading.",
|
||||
"Reload": "Reload",
|
||||
"Something Unexpected Happened": "Something Unexpected Happened",
|
||||
"Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch.": "Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch.",
|
||||
"our engineers have been notified": "our engineers have been notified",
|
||||
"Report a Bug": "Report a Bug",
|
||||
"Show Detail": "Show Detail",
|
||||
"Icon": "Icon",
|
||||
"Show menu": "Show menu",
|
||||
"Choose icon": "Choose icon",
|
||||
"Loading": "Loading",
|
||||
"Loading editor": "Loading editor",
|
||||
"Search": "Search",
|
||||
"Default access": "Default access",
|
||||
"View and edit": "View and edit",
|
||||
@ -345,6 +355,18 @@
|
||||
"Group members": "Group members",
|
||||
"Recently viewed": "Recently viewed",
|
||||
"Created by me": "Created by me",
|
||||
"We sent out your invites!": "We sent out your invites!",
|
||||
"Sorry, you can only send {{MAX_INVITES}} invites at a time": "Sorry, you can only send {{MAX_INVITES}} invites at a time",
|
||||
"Invite team members or guests to join your knowledge base. Team members can sign in with {{signinMethods}} or use their email address.": "Invite team members or guests to join your knowledge base. Team members can sign in with {{signinMethods}} or use their email address.",
|
||||
"Invite team members to join your knowledge base. They will need to sign in with {{signinMethods}}.": "Invite team members to join your knowledge base. They will need to sign in with {{signinMethods}}.",
|
||||
"As an admin you can also <2>enable email sign-in</2>.": "As an admin you can also <2>enable email sign-in</2>.",
|
||||
"Want a link to share directly with your team?": "Want a link to share directly with your team?",
|
||||
"Email": "Email",
|
||||
"Full name": "Full name",
|
||||
"Remove invite": "Remove invite",
|
||||
"Add another": "Add another",
|
||||
"Inviting": "Inviting",
|
||||
"Send Invites": "Send Invites",
|
||||
"Navigation": "Navigation",
|
||||
"Edit current document": "Edit current document",
|
||||
"Move current document": "Move current document",
|
||||
@ -392,7 +414,6 @@
|
||||
"No documents found for your search filters. <1></1>": "No documents found for your search filters. <1></1>",
|
||||
"Create a new document?": "Create a new document?",
|
||||
"Clear filters": "Clear filters",
|
||||
"Email": "Email",
|
||||
"Last active": "Last active",
|
||||
"Role": "Role",
|
||||
"Viewer": "Viewer",
|
||||
@ -428,7 +449,6 @@
|
||||
"Unable to upload new profile picture": "Unable to upload new profile picture",
|
||||
"Photo": "Photo",
|
||||
"Upload": "Upload",
|
||||
"Full name": "Full name",
|
||||
"Language": "Language",
|
||||
"Please note that translations are currently in early access.<1></1>Community contributions are accepted though our <4>translation portal</4>": "Please note that translations are currently in early access.<1></1>Community contributions are accepted though our <4>translation portal</4>",
|
||||
"Delete Account": "Delete Account",
|
||||
@ -454,6 +474,9 @@
|
||||
"There are no templates just yet.": "There are no templates just yet.",
|
||||
"You can create templates to help your team create consistent and accurate documentation.": "You can create templates to help your team create consistent and accurate documentation.",
|
||||
"Trash is empty at the moment.": "Trash is empty at the moment.",
|
||||
"Are you sure? Deleting your account will destroy identifying data associated with your user and cannot be undone. You will be immediately logged out of Outline and all your API tokens will be revoked.": "Are you sure? Deleting your account will destroy identifying data associated with your user and cannot be undone. You will be immediately logged out of Outline and all your API tokens will be revoked.",
|
||||
"<em>Note:</em> Signing back in will cause a new account to be automatically reprovisioned.": "<em>Note:</em> Signing back in will cause a new account to be automatically reprovisioned.",
|
||||
"Delete My Account": "Delete My Account",
|
||||
"You joined": "You joined",
|
||||
"Joined": "Joined",
|
||||
"{{ time }} ago.": "{{ time }} ago.",
|
||||
|
Reference in New Issue
Block a user