Files
ThreadNet-Web/src/components/views/dialogs/TermsDialog.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

218 lines
7.6 KiB
TypeScript
Raw Normal View History

2019-07-09 18:51:56 +01:00
/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
2019-07-09 18:51:56 +01:00
Copyright 2019 The Matrix.org Foundation C.I.C.
2024-09-09 14:57:16 +01:00
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
2019-07-09 18:51:56 +01:00
*/
import React from "react";
import { SERVICE_TYPES } from "matrix-js-sdk/src/matrix";
2021-10-22 17:23:32 -05:00
import { _t, pickBestLanguage } from "../../../languageHandler";
2021-07-03 11:24:33 +02:00
import DialogButtons from "../elements/DialogButtons";
import BaseDialog from "./BaseDialog";
2023-02-13 11:39:16 +00:00
import { ServicePolicyPair } from "../../../Terms";
import ExternalLink from "../elements/ExternalLink";
import { parseUrl } from "../../../utils/UrlUtils";
2019-07-09 18:51:56 +01:00
2021-06-14 23:50:41 +01:00
interface ITermsCheckboxProps {
onChange: (url: string, checked: boolean) => void;
url: string;
checked: boolean;
}
2019-07-09 18:51:56 +01:00
2021-06-14 23:50:41 +01:00
class TermsCheckbox extends React.PureComponent<ITermsCheckboxProps> {
private onChange = (ev: React.FormEvent<HTMLInputElement>): void => {
this.props.onChange(this.props.url, ev.currentTarget.checked);
2021-06-29 13:11:58 +01:00
};
2019-07-09 18:51:56 +01:00
public render(): React.ReactNode {
2019-07-09 18:51:56 +01:00
return <input type="checkbox" onChange={this.onChange} checked={this.props.checked} />;
}
}
2021-06-14 23:50:41 +01:00
interface ITermsDialogProps {
/**
* Array of [Service, policies] pairs, where policies is the response from the
* /terms endpoint for that service
*/
2023-02-13 11:39:16 +00:00
policiesAndServicePairs: ServicePolicyPair[];
2021-06-14 23:50:41 +01:00
/**
* urls that the user has already agreed to
*/
agreedUrls: string[];
2021-06-14 23:50:41 +01:00
/**
* Called with:
* * success {bool} True if the user accepted any douments, false if cancelled
* * agreedUrls {string[]} List of agreed URLs
*/
2021-07-01 23:23:03 +01:00
onFinished: (success: boolean, agreedUrls?: string[]) => void;
2021-06-14 23:50:41 +01:00
}
interface IState {
agreedUrls: any;
}
export default class TermsDialog extends React.PureComponent<ITermsDialogProps, IState> {
2023-02-13 11:39:16 +00:00
public constructor(props: ITermsDialogProps) {
2021-06-14 23:50:41 +01:00
super(props);
2019-07-09 18:51:56 +01:00
this.state = {
// url -> boolean
agreedUrls: {},
};
2019-07-11 10:53:45 +01:00
for (const url of props.agreedUrls) {
this.state.agreedUrls[url] = true;
}
2019-07-09 18:51:56 +01:00
}
2021-06-14 23:50:41 +01:00
private onCancelClick = (): void => {
2019-07-09 18:51:56 +01:00
this.props.onFinished(false);
2021-06-29 13:11:58 +01:00
};
2019-07-09 18:51:56 +01:00
2021-06-14 23:50:41 +01:00
private onNextClick = (): void => {
2019-07-09 18:51:56 +01:00
this.props.onFinished(
true,
Object.keys(this.state.agreedUrls).filter((url) => this.state.agreedUrls[url]),
);
2021-06-29 13:11:58 +01:00
};
2019-07-09 18:51:56 +01:00
2021-06-14 23:50:41 +01:00
private nameForServiceType(serviceType: SERVICE_TYPES, host: string): JSX.Element {
2019-07-09 18:51:56 +01:00
switch (serviceType) {
case SERVICE_TYPES.IS:
return (
<div>
{_t("common|identity_server")}
<br />({host})
</div>
);
case SERVICE_TYPES.IM:
return (
<div>
{_t("common|integration_manager")}
<br />({host})
</div>
);
2019-07-09 18:51:56 +01:00
}
}
2021-06-14 23:50:41 +01:00
private summaryForServiceType(serviceType: SERVICE_TYPES): JSX.Element {
2019-07-09 18:51:56 +01:00
switch (serviceType) {
case SERVICE_TYPES.IS:
2019-07-09 18:51:56 +01:00
return (
<div>
{_t("terms|summary_identity_server_1")}
2019-07-09 18:51:56 +01:00
<br />
{_t("terms|summary_identity_server_2")}
2019-07-09 18:51:56 +01:00
</div>
);
case SERVICE_TYPES.IM:
return <div>{_t("terms|integration_manager")}</div>;
2019-07-09 18:51:56 +01:00
}
}
private onTermsCheckboxChange = (url: string, checked: boolean): void => {
2019-07-10 15:12:05 +01:00
this.setState({
agreedUrls: Object.assign({}, this.state.agreedUrls, { [url]: checked }),
});
2021-06-29 13:11:58 +01:00
};
2019-07-09 18:51:56 +01:00
public render(): React.ReactNode {
const rows: JSX.Element[] = [];
2019-07-10 15:12:05 +01:00
for (const policiesAndService of this.props.policiesAndServicePairs) {
const parsedBaseUrl = parseUrl(policiesAndService.service.baseUrl);
2019-07-09 18:51:56 +01:00
2019-07-22 12:25:12 +01:00
const policyValues = Object.values(policiesAndService.policies);
for (let i = 0; i < policyValues.length; ++i) {
const termDoc = policyValues[i];
2019-07-09 18:51:56 +01:00
const termsLang = pickBestLanguage(Object.keys(termDoc).filter((k) => k !== "version"));
let serviceName: JSX.Element | undefined;
let summary: JSX.Element | undefined;
2019-07-09 18:51:56 +01:00
if (i === 0) {
2021-06-14 23:50:41 +01:00
serviceName = this.nameForServiceType(policiesAndService.service.serviceType, parsedBaseUrl.host);
2019-09-30 17:37:30 +01:00
summary = this.summaryForServiceType(policiesAndService.service.serviceType);
2019-07-09 18:51:56 +01:00
}
rows.push(
<tr key={termDoc[termsLang].url}>
<td className="mx_TermsDialog_service">{serviceName}</td>
<td className="mx_TermsDialog_summary">{summary}</td>
2021-06-14 23:50:41 +01:00
<td>
<ExternalLink rel="noreferrer noopener" target="_blank" href={termDoc[termsLang].url}>
{termDoc[termsLang].name}
</ExternalLink>
2021-06-14 23:50:41 +01:00
</td>
2019-07-09 18:51:56 +01:00
<td>
<TermsCheckbox
url={termDoc[termsLang].url}
2021-06-14 23:50:41 +01:00
onChange={this.onTermsCheckboxChange}
2019-07-09 18:51:56 +01:00
checked={Boolean(this.state.agreedUrls[termDoc[termsLang].url])}
/>
</td>
</tr>,
);
}
}
// if all the documents for at least one service have been checked, we can enable
// the submit button
let enableSubmit = false;
2019-07-10 15:12:05 +01:00
for (const policiesAndService of this.props.policiesAndServicePairs) {
2019-07-09 18:51:56 +01:00
let docsAgreedForService = 0;
2019-07-10 15:12:05 +01:00
for (const terms of Object.values(policiesAndService.policies)) {
2019-07-09 18:51:56 +01:00
let docAgreed = false;
for (const lang of Object.keys(terms)) {
if (lang === "version") continue;
if (this.state.agreedUrls[terms[lang].url]) {
docAgreed = true;
break;
}
}
if (docAgreed) {
++docsAgreedForService;
}
}
2019-07-10 15:12:05 +01:00
if (docsAgreedForService === Object.keys(policiesAndService.policies).length) {
2019-07-09 18:51:56 +01:00
enableSubmit = true;
break;
}
}
return (
<BaseDialog
2019-07-09 18:51:56 +01:00
fixedWidth={false}
2021-06-14 23:50:41 +01:00
onFinished={this.onCancelClick}
title={_t("terms|tos")}
2019-07-09 18:51:56 +01:00
contentId="mx_Dialog_content"
hasCancel={false}
>
<div id="mx_Dialog_content">
<p>{_t("terms|intro")}</p>
2019-07-09 18:51:56 +01:00
<table className="mx_TermsDialog_termsTable">
<tbody>
<tr className="mx_TermsDialog_termsTableHeader">
<th>{_t("terms|column_service")}</th>
<th>{_t("terms|column_summary")}</th>
<th>{_t("terms|column_document")}</th>
<th>{_t("action|accept")}</th>
2019-07-09 18:51:56 +01:00
</tr>
{rows}
2019-07-09 18:51:56 +01:00
</tbody>
</table>
</div>
<DialogButtons
primaryButton={_t("action|next")}
2019-07-09 18:51:56 +01:00
hasCancel={true}
2021-06-14 23:50:41 +01:00
onCancel={this.onCancelClick}
onPrimaryButtonClick={this.onNextClick}
2019-07-09 18:51:56 +01:00
primaryDisabled={!enableSubmit}
/>
</BaseDialog>
);
}
}