This repository has been archived by the owner on Sep 11, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 831
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
13 changed files
with
454 additions
and
269 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
-1.54 KB
(97%)
playwright/snapshots/settings/general-user-settings-tab.spec.ts/general-linux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
src/components/views/settings/UserPersonalInfoSettings.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
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 { ThreepidMedium } from "matrix-js-sdk/src/matrix"; | ||
import { Alert } from "@vector-im/compound-web"; | ||
|
||
import AccountEmailAddresses from "./account/EmailAddresses"; | ||
import AccountPhoneNumbers from "./account/PhoneNumbers"; | ||
import { _t } from "../../../languageHandler"; | ||
import InlineSpinner from "../elements/InlineSpinner"; | ||
import SettingsSubsection from "./shared/SettingsSubsection"; | ||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext"; | ||
import { ThirdPartyIdentifier } from "../../../AddThreepid"; | ||
import SettingsStore from "../../../settings/SettingsStore"; | ||
import { UIFeature } from "../../../settings/UIFeature"; | ||
|
||
type LoadingState = "loading" | "loaded" | "error"; | ||
|
||
interface ThreepidSectionWrapperProps { | ||
error: string; | ||
loadingState: LoadingState; | ||
children: React.ReactNode; | ||
} | ||
|
||
const ThreepidSectionWrapper: React.FC<ThreepidSectionWrapperProps> = ({ error, loadingState, children }) => { | ||
if (loadingState === "loading") { | ||
return <InlineSpinner />; | ||
} else if (loadingState === "error") { | ||
return ( | ||
<Alert type="critical" title={_t("common|error")}> | ||
{error} | ||
</Alert> | ||
); | ||
} else { | ||
return <>{children}</>; | ||
} | ||
}; | ||
|
||
interface UserPersonalInfoSettingsProps { | ||
canMake3pidChanges: boolean; | ||
} | ||
|
||
/** | ||
* Settings controls allowing the user to set personal information like email addresses. | ||
*/ | ||
export const UserPersonalInfoSettings: React.FC<UserPersonalInfoSettingsProps> = ({ canMake3pidChanges }) => { | ||
const [emails, setEmails] = useState<ThirdPartyIdentifier[] | undefined>(); | ||
const [phoneNumbers, setPhoneNumbers] = useState<ThirdPartyIdentifier[] | undefined>(); | ||
const [loadingState, setLoadingState] = useState<"loading" | "loaded" | "error">("loading"); | ||
|
||
const client = useMatrixClientContext(); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
try { | ||
const threepids = await client.getThreePids(); | ||
setEmails(threepids.threepids.filter((a) => a.medium === ThreepidMedium.Email)); | ||
setPhoneNumbers(threepids.threepids.filter((a) => a.medium === ThreepidMedium.Phone)); | ||
setLoadingState("loaded"); | ||
} catch (e) { | ||
setLoadingState("error"); | ||
} | ||
})(); | ||
}, [client]); | ||
|
||
const onEmailsChange = useCallback((emails: ThirdPartyIdentifier[]) => { | ||
setEmails(emails); | ||
}, []); | ||
|
||
const onMsisdnsChange = useCallback((msisdns: ThirdPartyIdentifier[]) => { | ||
setPhoneNumbers(msisdns); | ||
}, []); | ||
|
||
if (!SettingsStore.getValue(UIFeature.ThirdPartyID)) return null; | ||
|
||
return ( | ||
<div> | ||
<h2>{_t("settings|general|personal_info")}</h2> | ||
<SettingsSubsection | ||
heading={_t("settings|general|emails_heading")} | ||
stretchContent | ||
data-testid="mx_AccountEmailAddresses" | ||
> | ||
<ThreepidSectionWrapper | ||
error={_t("settings|general|unable_to_load_emails")} | ||
loadingState={loadingState} | ||
> | ||
<AccountEmailAddresses | ||
emails={emails!} | ||
onEmailsChange={onEmailsChange} | ||
disabled={!canMake3pidChanges} | ||
/> | ||
</ThreepidSectionWrapper> | ||
</SettingsSubsection> | ||
|
||
<SettingsSubsection | ||
heading={_t("settings|general|msisdns_heading")} | ||
stretchContent | ||
data-testid="mx_AccountPhoneNumbers" | ||
> | ||
<ThreepidSectionWrapper | ||
error={_t("settings|general|unable_to_load_msisdns")} | ||
loadingState={loadingState} | ||
> | ||
<AccountPhoneNumbers | ||
msisdns={phoneNumbers!} | ||
onMsisdnsChange={onMsisdnsChange} | ||
disabled={!canMake3pidChanges} | ||
/> | ||
</ThreepidSectionWrapper> | ||
</SettingsSubsection> | ||
</div> | ||
); | ||
}; | ||
|
||
export default UserPersonalInfoSettings; |
190 changes: 190 additions & 0 deletions
190
src/components/views/settings/discovery/DiscoverySettings.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
Oops, something went wrong.