Split out email & phone number settings to separate components & move discovery to privacy tab (#12670)
* WIP update of threepid settings section * Remove email / phone number section from original place and don't show the new one if 3pids are disabled * Update snapshots * Pull identity server / 3pid binding settings out to separate component and put it in the security & privacy section which is its new home * Update snapshot * Move relevant part of test & update screenshots / snapshots * Remove unnecessary dependency * Add test for discovery settings * Add spacing in terms agreement
This commit is contained in:
190
src/components/views/settings/discovery/DiscoverySettings.tsx
Normal file
190
src/components/views/settings/discovery/DiscoverySettings.tsx
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { SERVICE_TYPES, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { Alert } from "@vector-im/compound-web";
|
||||
|
||||
import DiscoveryEmailAddresses from "../discovery/EmailAddresses";
|
||||
import DiscoveryPhoneNumbers from "../discovery/PhoneNumbers";
|
||||
import { getThreepidsWithBindStatus } from "../../../../boundThreepids";
|
||||
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
|
||||
import { ThirdPartyIdentifier } from "../../../../AddThreepid";
|
||||
import SettingsStore from "../../../../settings/SettingsStore";
|
||||
import { UIFeature } from "../../../../settings/UIFeature";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import SetIdServer from "../SetIdServer";
|
||||
import SettingsSubsection from "../shared/SettingsSubsection";
|
||||
import InlineTermsAgreement from "../../terms/InlineTermsAgreement";
|
||||
import { Service, ServicePolicyPair, startTermsFlow } from "../../../../Terms";
|
||||
import IdentityAuthClient from "../../../../IdentityAuthClient";
|
||||
import { abbreviateUrl } from "../../../../utils/UrlUtils";
|
||||
import { useDispatcher } from "../../../../hooks/useDispatcher";
|
||||
import defaultDispatcher from "../../../../dispatcher/dispatcher";
|
||||
import { ActionPayload } from "../../../../dispatcher/payloads";
|
||||
|
||||
type RequiredPolicyInfo =
|
||||
| {
|
||||
// This object is passed along to a component for handling
|
||||
policiesAndServices: null; // From the startTermsFlow callback
|
||||
agreedUrls: null; // From the startTermsFlow callback
|
||||
resolve: null; // Promise resolve function for startTermsFlow callback
|
||||
}
|
||||
| {
|
||||
policiesAndServices: ServicePolicyPair[];
|
||||
agreedUrls: string[];
|
||||
resolve: (values: string[]) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings controlling how a user's email addreses and phone numbers can be used to discover them
|
||||
*/
|
||||
export const DiscoverySettings: React.FC = () => {
|
||||
const client = useMatrixClientContext();
|
||||
|
||||
const [emails, setEmails] = useState<ThirdPartyIdentifier[]>([]);
|
||||
const [phoneNumbers, setPhoneNumbers] = useState<ThirdPartyIdentifier[]>([]);
|
||||
const [loadingState, setLoadingState] = useState<"loading" | "loaded" | "error">("loading");
|
||||
const [idServerName, setIdServerName] = useState<string | undefined>(abbreviateUrl(client.getIdentityServerUrl()));
|
||||
const [canMake3pidChanges, setCanMake3pidChanges] = useState<boolean>(false);
|
||||
|
||||
const [requiredPolicyInfo, setRequiredPolicyInfo] = useState<RequiredPolicyInfo>({
|
||||
// This object is passed along to a component for handling
|
||||
policiesAndServices: null, // From the startTermsFlow callback
|
||||
agreedUrls: null, // From the startTermsFlow callback
|
||||
resolve: null, // Promise resolve function for startTermsFlow callback
|
||||
});
|
||||
const [hasTerms, setHasTerms] = useState<boolean>(false);
|
||||
|
||||
const getThreepidState = useCallback(async () => {
|
||||
const threepids = await getThreepidsWithBindStatus(client);
|
||||
setEmails(threepids.filter((a) => a.medium === ThreepidMedium.Email));
|
||||
setPhoneNumbers(threepids.filter((a) => a.medium === ThreepidMedium.Phone));
|
||||
}, [client]);
|
||||
|
||||
useDispatcher(
|
||||
defaultDispatcher,
|
||||
useCallback(
|
||||
(payload: ActionPayload) => {
|
||||
if (payload.action === "id_server_changed") {
|
||||
setIdServerName(abbreviateUrl(client.getIdentityServerUrl()));
|
||||
|
||||
getThreepidState().then();
|
||||
}
|
||||
},
|
||||
[client, getThreepidState],
|
||||
),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
await getThreepidState();
|
||||
|
||||
const capabilities = await client.getCapabilities();
|
||||
setCanMake3pidChanges(
|
||||
!capabilities["m.3pid_changes"] || capabilities["m.3pid_changes"].enabled === true,
|
||||
);
|
||||
|
||||
// By starting the terms flow we get the logic for checking which terms the user has signed
|
||||
// for free. So we might as well use that for our own purposes.
|
||||
const idServerUrl = client.getIdentityServerUrl();
|
||||
if (!idServerUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const authClient = new IdentityAuthClient();
|
||||
try {
|
||||
const idAccessToken = await authClient.getAccessToken({ check: false });
|
||||
await startTermsFlow(
|
||||
client,
|
||||
[new Service(SERVICE_TYPES.IS, idServerUrl, idAccessToken!)],
|
||||
(policiesAndServices, agreedUrls, extraClassNames) => {
|
||||
return new Promise((resolve) => {
|
||||
setIdServerName(abbreviateUrl(idServerUrl));
|
||||
setHasTerms(true);
|
||||
setRequiredPolicyInfo({
|
||||
policiesAndServices,
|
||||
agreedUrls,
|
||||
resolve,
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
// User accepted all terms
|
||||
setHasTerms(false);
|
||||
} catch (e) {
|
||||
logger.warn(
|
||||
`Unable to reach identity server at ${idServerUrl} to check ` + `for terms in Settings`,
|
||||
);
|
||||
logger.warn(e);
|
||||
}
|
||||
|
||||
setLoadingState("loaded");
|
||||
} catch (e) {
|
||||
setLoadingState("error");
|
||||
}
|
||||
})();
|
||||
}, [client, getThreepidState]);
|
||||
|
||||
if (!SettingsStore.getValue(UIFeature.ThirdPartyID)) return null;
|
||||
|
||||
if (hasTerms && requiredPolicyInfo.policiesAndServices) {
|
||||
const intro = (
|
||||
<Alert type="info" title={_t("settings|general|discovery_needs_terms_title")}>
|
||||
{_t("settings|general|discovery_needs_terms", { serverName: idServerName })}
|
||||
</Alert>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<InlineTermsAgreement
|
||||
policiesAndServicePairs={requiredPolicyInfo.policiesAndServices}
|
||||
agreedUrls={requiredPolicyInfo.agreedUrls}
|
||||
onFinished={requiredPolicyInfo.resolve}
|
||||
introElement={intro}
|
||||
/>
|
||||
{/* has its own heading as it includes the current identity server */}
|
||||
<SetIdServer missingTerms={true} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const threepidSection = idServerName ? (
|
||||
<>
|
||||
<DiscoveryEmailAddresses
|
||||
emails={emails}
|
||||
isLoading={loadingState === "loading"}
|
||||
disabled={!canMake3pidChanges}
|
||||
/>
|
||||
<DiscoveryPhoneNumbers
|
||||
msisdns={phoneNumbers}
|
||||
isLoading={loadingState === "loading"}
|
||||
disabled={!canMake3pidChanges}
|
||||
/>
|
||||
</>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<SettingsSubsection heading={_t("settings|discovery|title")} data-testid="discoverySection">
|
||||
{threepidSection}
|
||||
{/* has its own heading as it includes the current identity server */}
|
||||
<SetIdServer missingTerms={false} />
|
||||
</SettingsSubsection>
|
||||
);
|
||||
};
|
||||
|
||||
export default DiscoverySettings;
|
||||
Reference in New Issue
Block a user