Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Support Matrix 1.1 (drop legacy r0 versions) #9819

Merged
merged 28 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
640a020
Intercept "hs too old" error and replace with a user-friendly thing
turt2live Dec 21, 2022
2e899ef
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
t3chguy Jul 28, 2023
75bf644
Update tests
t3chguy Jul 28, 2023
183a80a
Add legacy server toast
t3chguy Jul 28, 2023
ac6cec9
i18n
t3chguy Jul 28, 2023
9276bfa
Iterate
t3chguy Jul 28, 2023
c3f39bd
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
t3chguy Aug 1, 2023
69af23c
Fix tests
t3chguy Aug 1, 2023
2c8adbd
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
t3chguy Aug 8, 2023
238b30a
Remove doesServerSupportSeparateAddAndBind as we now require v1.1
t3chguy Aug 8, 2023
a9189a4
Add test
t3chguy Aug 8, 2023
030c604
Merge branch 'develop' into travis/mx-v1.1/api
t3chguy Aug 9, 2023
10c1fa8
Merge branch 'develop' into travis/mx-v1.1/api
t3chguy Aug 10, 2023
067c737
Fix general settings tab
t3chguy Aug 10, 2023
c9db1e5
Merge remote-tracking branch 'origin/travis/mx-v1.1/api' into travis/…
t3chguy Aug 10, 2023
5111690
Improve coverage
t3chguy Aug 10, 2023
8ec0059
Stop using r0 API endpoints
t3chguy Aug 10, 2023
adbd0f6
Remove more <v1.1 support
t3chguy Aug 10, 2023
8c47f83
iterate
t3chguy Aug 10, 2023
91738a4
i18n
t3chguy Aug 10, 2023
98ec8a4
Improve coverage
t3chguy Aug 10, 2023
4b5b041
Delint
t3chguy Aug 10, 2023
3afe577
Improve test coverage
t3chguy Aug 10, 2023
6ddec82
Improve coverage
t3chguy Aug 10, 2023
3535656
Fix test fixture
t3chguy Aug 10, 2023
4ed6f7e
Iterate
t3chguy Aug 10, 2023
7d75543
Fix object mangling
t3chguy Aug 10, 2023
af6a688
Merge branch 'develop' into travis/mx-v1.1/api
t3chguy Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cypress/e2e/timeline/timeline.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,14 +704,14 @@ describe("Timeline", () => {
});

it("should render url previews", () => {
cy.intercept("**/_matrix/media/r0/thumbnail/matrix.org/2022-08-16_yaiSVSRIsNFfxDnV?*", {
cy.intercept("**/_matrix/media/v3/thumbnail/matrix.org/2022-08-16_yaiSVSRIsNFfxDnV?*", {
statusCode: 200,
fixture: "riot.png",
headers: {
"Content-Type": "image/png",
},
}).as("mxc");
cy.intercept("**/_matrix/media/r0/preview_url?url=https%3A%2F%2Fcall.element.io%2F&ts=*", {
cy.intercept("**/_matrix/media/v3/preview_url?url=https%3A%2F%2Fcall.element.io%2F&ts=*", {
statusCode: 200,
body: {
"og:title": "Element Call",
Expand Down
297 changes: 131 additions & 166 deletions src/AddThreepid.ts

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions src/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { decryptAES, encryptAES, IEncryptedPayload } from "matrix-js-sdk/src/cry
import { QueryDict } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { SSOAction } from "matrix-js-sdk/src/@types/auth";
import { MINIMUM_MATRIX_VERSION } from "matrix-js-sdk/src/version-support";

import { IMatrixClientCreds, MatrixClientPeg } from "./MatrixClientPeg";
import SecurityCustomisations from "./customisations/Security";
Expand Down Expand Up @@ -67,6 +68,7 @@ import { SdkContextClass } from "./contexts/SDKContext";
import { messageForLoginError } from "./utils/ErrorUtils";
import { completeOidcLogin } from "./utils/oidc/authorize";
import { persistOidcAuthenticatedSettings } from "./utils/oidc/persistOidcSettings";
import GenericToast from "./components/views/toasts/GenericToast";

const HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url";
Expand Down Expand Up @@ -585,13 +587,43 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }):
},
false,
);
checkServerVersions();
return true;
} else {
logger.log("No previous session found.");
return false;
}
}

async function checkServerVersions(): Promise<void> {
MatrixClientPeg.get()
?.getVersions()
.then((response) => {
if (!response.versions.includes(MINIMUM_MATRIX_VERSION)) {
const toastKey = "LEGACY_SERVER";
ToastStore.sharedInstance().addOrReplaceToast({
key: toastKey,
title: _t("Your server is unsupported"),
props: {
description: _t(
"This server is using an older version of Matrix. Upgrade to Matrix %(version)s to use %(brand)s without errors.",
{
version: MINIMUM_MATRIX_VERSION,
brand: SdkConfig.get().brand,
},
),
acceptLabel: _t("OK"),
onAccept: () => {
ToastStore.sharedInstance().dismissToast(toastKey);
},
},
component: GenericToast,
priority: 98,
});
}
});
}

async function handleLoadSessionFailure(e: unknown): Promise<boolean> {
logger.error("Unable to load session", e);

Expand Down
39 changes: 0 additions & 39 deletions src/components/views/settings/discovery/EmailAddresses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,6 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
}

private async changeBinding({ bind, label, errorTitle }: Binding): Promise<void> {
if (!(await MatrixClientPeg.safeGet().doesServerSupportSeparateAddAndBind())) {
return this.changeBindingTangledAddBind({ bind, label, errorTitle });
}

const { medium, address } = this.props.email;

try {
Expand Down Expand Up @@ -113,41 +109,6 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
}
}

private async changeBindingTangledAddBind({ bind, label, errorTitle }: Binding): Promise<void> {
const { medium, address } = this.props.email;

const task = new AddThreepid(MatrixClientPeg.safeGet());
this.setState({
verifying: true,
continueDisabled: true,
addTask: task,
});

try {
await MatrixClientPeg.safeGet().deleteThreePid(medium, address);
if (bind) {
await task.bindEmailAddress(address);
} else {
await task.addEmailAddress(address);
}
this.setState({
continueDisabled: false,
bound: bind,
});
} catch (err) {
logger.error(`changeBindingTangledAddBind: Unable to ${label} email address ${address}`, err);
this.setState({
verifying: false,
continueDisabled: false,
addTask: null,
});
Modal.createDialog(ErrorDialog, {
title: errorTitle,
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
}

private onRevokeClick = (e: ButtonEvent): void => {
e.stopPropagation();
e.preventDefault();
Expand Down
45 changes: 0 additions & 45 deletions src/components/views/settings/discovery/PhoneNumbers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
}

private async changeBinding({ bind, label, errorTitle }: Binding): Promise<void> {
if (!(await MatrixClientPeg.safeGet().doesServerSupportSeparateAddAndBind())) {
return this.changeBindingTangledAddBind({ bind, label, errorTitle });
}

const { medium, address } = this.props.msisdn;

try {
Expand Down Expand Up @@ -114,47 +110,6 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
}
}

private async changeBindingTangledAddBind({ bind, label, errorTitle }: Binding): Promise<void> {
const { medium, address } = this.props.msisdn;

const task = new AddThreepid(MatrixClientPeg.safeGet());
this.setState({
verifying: true,
continueDisabled: true,
addTask: task,
});

try {
await MatrixClientPeg.safeGet().deleteThreePid(medium, address);
// XXX: Sydent will accept a number without country code if you add
// a leading plus sign to a number in E.164 format (which the 3PID
// address is), but this goes against the spec.
// See https://github.com/matrix-org/matrix-doc/issues/2222
if (bind) {
// @ts-ignore
await task.bindMsisdn(null, `+${address}`);
} else {
// @ts-ignore
await task.addMsisdn(null, `+${address}`);
}
this.setState({
continueDisabled: false,
bound: bind,
});
} catch (err) {
logger.error(`changeBindingTangledAddBind: Unable to ${label} phone number ${address}`, err);
this.setState({
verifying: false,
continueDisabled: false,
addTask: null,
});
Modal.createDialog(ErrorDialog, {
title: errorTitle,
description: extractErrorMessageFromError(err, _t("Operation failed")),
});
}
}

private onRevokeClick = (e: ButtonEvent): void => {
e.stopPropagation();
e.preventDefault();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { Service, ServicePolicyPair, startTermsFlow } from "../../../../../Terms
import IdentityAuthClient from "../../../../../IdentityAuthClient";
import { abbreviateUrl } from "../../../../../utils/UrlUtils";
import { getThreepidsWithBindStatus } from "../../../../../boundThreepids";
import Spinner from "../../../elements/Spinner";
import { SettingLevel } from "../../../../../settings/SettingLevel";
import { UIFeature } from "../../../../../settings/UIFeature";
import { ActionPayload } from "../../../../../dispatcher/payloads";
Expand Down Expand Up @@ -71,7 +70,6 @@ interface IState {
spellCheckEnabled?: boolean;
spellCheckLanguages: string[];
haveIdServer: boolean;
serverSupportsSeparateAddAndBind?: boolean;
idServerHasUnsignedTerms: boolean;
requiredPolicyInfo:
| {
Expand Down Expand Up @@ -167,8 +165,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
private async getCapabilities(): Promise<void> {
const cli = this.context;

const serverSupportsSeparateAddAndBind = await cli.doesServerSupportSeparateAddAndBind();

const capabilities = await cli.getCapabilities(); // this is cached
const changePasswordCap = capabilities["m.change_password"];

Expand All @@ -180,7 +176,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
const delegatedAuthConfig = M_AUTHENTICATION.findIn<IDelegatedAuthConfig | undefined>(cli.getClientWellKnown());
const externalAccountManagementUrl = delegatedAuthConfig?.account;

this.setState({ serverSupportsSeparateAddAndBind, canChangePassword, externalAccountManagementUrl });
this.setState({ canChangePassword, externalAccountManagementUrl });
}

private async getThreepidState(): Promise<void> {
Expand Down Expand Up @@ -333,10 +329,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
// validate 3PID ownership even if we're just adding to the homeserver only.
// For newer homeservers with separate 3PID add and bind methods (MSC2290),
// there is no such concern, so we can always show the HS account 3PIDs.
if (
SettingsStore.getValue(UIFeature.ThirdPartyID) &&
(this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true)
) {
if (SettingsStore.getValue(UIFeature.ThirdPartyID) && this.state.haveIdServer) {
const emails = this.state.loading3pids ? (
<InlineSpinner />
) : (
Expand Down Expand Up @@ -366,8 +359,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
</SettingsSubsection>
</>
);
} else if (this.state.serverSupportsSeparateAddAndBind === null) {
threepidSection = <Spinner />;
}

let passwordChangeSection: ReactNode = null;
Expand Down
3 changes: 3 additions & 0 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.",
"We couldn't log you in": "We couldn't log you in",
"Try again": "Try again",
"Your server is unsupported": "Your server is unsupported",
"This server is using an older version of Matrix. Upgrade to Matrix %(version)s to use %(brand)s without errors.": "This server is using an older version of Matrix. Upgrade to Matrix %(version)s to use %(brand)s without errors.",
"User is not logged in": "User is not logged in",
"Database unexpectedly closed": "Database unexpectedly closed",
"This may be caused by having the app open in multiple tabs or due to clearing browser data.": "This may be caused by having the app open in multiple tabs or due to clearing browser data.",
Expand Down Expand Up @@ -690,6 +692,7 @@
"No homeserver URL provided": "No homeserver URL provided",
"Unexpected error resolving homeserver configuration": "Unexpected error resolving homeserver configuration",
"Unexpected error resolving identity server configuration": "Unexpected error resolving identity server configuration",
"Your homeserver is too old and does not support the minimum API version required. Please contact your server owner, or upgrade your server.": "Your homeserver is too old and does not support the minimum API version required. Please contact your server owner, or upgrade your server.",
"This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.",
"This homeserver has been blocked by its administrator.": "This homeserver has been blocked by its administrator.",
"This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.",
Expand Down
5 changes: 5 additions & 0 deletions src/utils/AutoDiscoveryUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ export default class AutoDiscoveryUtils {
if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error as string) !== -1) {
throw new UserFriendlyError(String(hsResult.error));
}
if (hsResult.error === AutoDiscovery.ERROR_HOMESERVER_TOO_OLD) {
throw new UserFriendlyError(
"Your homeserver is too old and does not support the minimum API version required. Please contact your server owner, or upgrade your server.",
);
}
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
} // else the error is not related to syntax - continue anyways.
}
Expand Down
1 change: 1 addition & 0 deletions test/Lifecycle-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe("Lifecycle", () => {
store: {
destroy: jest.fn(),
},
getVersions: jest.fn().mockResolvedValue({ versions: ["v1.1"] }),
});

beforeEach(() => {
Expand Down
3 changes: 2 additions & 1 deletion test/components/structures/MatrixChat-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ describe("<MatrixChat />", () => {
// reused in createClient mock below
const getMockClientMethods = () => ({
...mockClientMethodsUser(userId),
getVersions: jest.fn().mockResolvedValue({ versions: ["v1.1"] }),
startClient: jest.fn(),
stopClient: jest.fn(),
setCanResetTimelineCallback: jest.fn(),
Expand Down Expand Up @@ -180,7 +181,7 @@ describe("<MatrixChat />", () => {
mockClient = getMockClientWithEventEmitter(getMockClientMethods());
fetchMock.get("https://test.com/_matrix/client/versions", {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
localStorageSetSpy = jest.spyOn(localStorage.__proto__, "setItem");
localStorageGetSpy = jest.spyOn(localStorage.__proto__, "getItem").mockReturnValue(undefined);
Expand Down
6 changes: 3 additions & 3 deletions test/components/structures/auth/Login-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe("Login", function () {
fetchMock.resetHistory();
fetchMock.get("https://matrix.org/_matrix/client/versions", {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
platform = mockPlatformPeg({
startSingleSignOn: jest.fn(),
Expand Down Expand Up @@ -209,7 +209,7 @@ describe("Login", function () {

fetchMock.get("https://server2/_matrix/client/versions", {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
rerender(getRawComponent("https://server2"));
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
Expand Down Expand Up @@ -359,7 +359,7 @@ describe("Login", function () {
// but server2 is
fetchMock.get("https://server2/_matrix/client/versions", {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
const { rerender } = render(getRawComponent());
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
Expand Down
5 changes: 3 additions & 2 deletions test/components/structures/auth/Registration-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe("Registration", function () {
const mockClient = mocked({
registerRequest,
loginFlows: jest.fn(),
getVersions: jest.fn().mockResolvedValue({ versions: ["v1.1"] }),
} as unknown as MatrixClient);

beforeEach(function () {
Expand All @@ -59,7 +60,7 @@ describe("Registration", function () {
});
fetchMock.get("https://matrix.org/_matrix/client/versions", {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
mockPlatformPeg({
startSingleSignOn: jest.fn(),
Expand Down Expand Up @@ -125,7 +126,7 @@ describe("Registration", function () {

fetchMock.get("https://server2/_matrix/client/versions", {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
rerender(getRawComponent("https://server2"));
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
Expand Down
8 changes: 4 additions & 4 deletions test/components/views/dialogs/ServerPickerDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe("<ServerPickerDialog />", () => {
it("should allow user to revert from a custom server to the default", async () => {
fetchMock.get(`https://custom.org/_matrix/client/versions`, {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});

const onFinished = jest.fn();
Expand Down Expand Up @@ -147,7 +147,7 @@ describe("<ServerPickerDialog />", () => {
const homeserver = "https://myhomeserver.site";
fetchMock.get(`${homeserver}/_matrix/client/versions`, {
unstable_features: {},
versions: [],
versions: ["v1.1"],
});
const onFinished = jest.fn();
getComponent({ onFinished });
Expand Down Expand Up @@ -200,7 +200,7 @@ describe("<ServerPickerDialog />", () => {

fetchMock.getOnce(wellKnownUrl, validWellKnown);
fetchMock.getOnce(versionsUrl, {
versions: [],
versions: ["v1.1"],
});
fetchMock.getOnce(isWellKnownUrl, {});
const onFinished = jest.fn();
Expand Down Expand Up @@ -236,7 +236,7 @@ describe("<ServerPickerDialog />", () => {
const wellKnownUrl = `https://${homeserver}/.well-known/matrix/client`;
fetchMock.get(wellKnownUrl, { status: 404 });
// but is otherwise live (happy versions response)
fetchMock.get(`https://${homeserver}/_matrix/client/versions`, { versions: ["1"] });
fetchMock.get(`https://${homeserver}/_matrix/client/versions`, { versions: ["v1.1"] });
const onFinished = jest.fn();
getComponent({ onFinished });

Expand Down
Loading
Loading