Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 7 additions & 2 deletions src/components/structures/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
type SyncStateData,
SyncState,
EventType,
ProfileKeyTimezone,
ProfileKeyMSC4175Timezone,
} from "matrix-js-sdk/src/matrix";
import { type MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import classNames from "classnames";
Expand Down Expand Up @@ -197,10 +199,12 @@ class LoggedInView extends React.Component<IProps, IState> {
}

private onTimezoneUpdate = async (): Promise<void> => {
// TODO: In a future app release, remove support for legacy key.
if (!SettingsStore.getValue("userTimezonePublish")) {
// Ensure it's deleted
try {
await this._matrixClient.deleteExtendedProfileProperty("us.cloke.msc4175.tz");
await this._matrixClient.deleteExtendedProfileProperty(ProfileKeyMSC4175Timezone);
await this._matrixClient.deleteExtendedProfileProperty(ProfileKeyTimezone);
} catch (ex) {
console.warn("Failed to delete timezone from user profile", ex);
}
Expand All @@ -215,7 +219,8 @@ class LoggedInView extends React.Component<IProps, IState> {
return;
}
try {
await this._matrixClient.setExtendedProfileProperty("us.cloke.msc4175.tz", currentTimezone);
await this._matrixClient.setExtendedProfileProperty(ProfileKeyTimezone, currentTimezone);
await this._matrixClient.setExtendedProfileProperty(ProfileKeyMSC4175Timezone, currentTimezone);
} catch (ex) {
console.warn("Failed to update user profile with current timezone", ex);
}
Expand Down
20 changes: 15 additions & 5 deletions src/hooks/useUserTimezone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { useEffect, useState } from "react";
import { type MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
import {
type MatrixClient,
MatrixError,
ProfileKeyMSC4175Timezone,
ProfileKeyTimezone,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";

import { getTwelveHourOptions } from "../DateUtils.ts";
import { useSettingValue } from "./useSettings.ts";

const log = logger.getChild("useUserTimezone");

/**
* Fetch a user's delclared timezone through their profile, and return
* a friendly string of the current time for that user. This will keep
Expand Down Expand Up @@ -52,11 +60,13 @@ export const useUserTimezone = (cli: MatrixClient, userId: string): { timezone:
return;
}
(async () => {
console.log("Trying to fetch TZ");
log.debug("Trying to fetch TZ for", userId);
try {
const tz = await cli.getExtendedProfileProperty(userId, "us.cloke.msc4175.tz");
const userProfile = await cli.getExtendedProfile(userId);
// In a future spec release, remove support for legacy key.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean app release instead of spec release?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's the word I wanted 😆

const tz = userProfile[ProfileKeyTimezone] ?? userProfile[ProfileKeyMSC4175Timezone];
if (typeof tz !== "string") {
// Err, definitely not a tz.
// Definitely not a tz.
throw Error("Timezone value was not a string");
}
// This will validate the timezone for us.
Expand Down Expand Up @@ -85,7 +95,7 @@ export const useUserTimezone = (cli: MatrixClient, userId: string): { timezone:
// No timezone set, ignore.
return;
}
console.error("Could not render current timezone for user", ex);
log.warn(`Could not render current timezone for ${userId}`, ex);
}
})();
}, [supported, userId, cli, showTwelveHour]);
Expand Down
20 changes: 14 additions & 6 deletions test/unit-tests/components/structures/LoggedInView-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
MatrixEvent,
ClientEvent,
PushRuleKind,
ProfileKeyTimezone,
ProfileKeyMSC4175Timezone,
} from "matrix-js-sdk/src/matrix";
import { MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler";
import { logger } from "matrix-js-sdk/src/logger";
Expand Down Expand Up @@ -470,30 +472,36 @@ describe("<LoggedInView />", () => {
it("does not update the timezone when userTimezonePublish is off", async () => {
getComponent();
await SettingsStore.setValue("userTimezonePublish", null, SettingLevel.DEVICE, false);
expect(mockClient.deleteExtendedProfileProperty).toHaveBeenCalledWith("us.cloke.msc4175.tz");
expect(mockClient.deleteExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyTimezone);
expect(mockClient.deleteExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyMSC4175Timezone);
expect(mockClient.setExtendedProfileProperty).not.toHaveBeenCalled();
});
it("should set the user timezone when userTimezonePublish is enabled", async () => {
getComponent();
await SettingsStore.setValue("userTimezonePublish", null, SettingLevel.DEVICE, true);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith("us.cloke.msc4175.tz", userTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyTimezone, userTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyMSC4175Timezone, userTimezone);
});

it("should set the user timezone when the timezone is changed", async () => {
const newTimezone = "Europe/Paris";
getComponent();
await SettingsStore.setValue("userTimezonePublish", null, SettingLevel.DEVICE, true);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith("us.cloke.msc4175.tz", userTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyTimezone, userTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyMSC4175Timezone, userTimezone);
await SettingsStore.setValue("userTimezone", null, SettingLevel.DEVICE, newTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith("us.cloke.msc4175.tz", newTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyTimezone, newTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyMSC4175Timezone, newTimezone);
});

it("should clear the timezone when the publish feature is turned off", async () => {
getComponent();
await SettingsStore.setValue("userTimezonePublish", null, SettingLevel.DEVICE, true);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith("us.cloke.msc4175.tz", userTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyTimezone, userTimezone);
expect(mockClient.setExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyMSC4175Timezone, userTimezone);
await SettingsStore.setValue("userTimezonePublish", null, SettingLevel.DEVICE, false);
expect(mockClient.deleteExtendedProfileProperty).toHaveBeenCalledWith("us.cloke.msc4175.tz");
expect(mockClient.deleteExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyTimezone);
expect(mockClient.deleteExtendedProfileProperty).toHaveBeenCalledWith(ProfileKeyMSC4175Timezone);
});
});

Expand Down
54 changes: 32 additions & 22 deletions test/unit-tests/components/views/right_panel/UserInfo-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ import React from "react";
import { render, screen, act, waitForElementToBeRemoved } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { type Mocked, mocked } from "jest-mock";
import { type Room, User, type MatrixClient, RoomMember, Device } from "matrix-js-sdk/src/matrix";
import {
type Room,
User,
type MatrixClient,
RoomMember,
Device,
ProfileKeyTimezone,
ProfileKeyMSC4175Timezone,
} from "matrix-js-sdk/src/matrix";
import { EventEmitter } from "events";
import {
UserVerificationStatus,
Expand Down Expand Up @@ -120,7 +128,7 @@ beforeEach(() => {
isSynapseAdministrator: jest.fn().mockResolvedValue(false),
doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false),
doesServerSupportExtendedProfiles: jest.fn().mockResolvedValue(false),
getExtendedProfileProperty: jest.fn().mockRejectedValue(new Error("Not supported")),
getExtendedProfile: jest.fn().mockRejectedValue(new Error("Not supported")),
mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"),
removeListener: jest.fn(),
currentState: {
Expand Down Expand Up @@ -199,29 +207,31 @@ describe("<UserInfo />", () => {
expect(screen.getByRole("heading", { name: defaultUserId })).toBeInTheDocument();
});

it("renders user timezone if set", async () => {
// For timezone, force a consistent locale.
jest.spyOn(global.Date.prototype, "toLocaleString").mockImplementation(function (
this: Date,
_locale,
opts,
) {
return origDate.call(this, "en-US", {
...opts,
hourCycle: "h12",
describe.each([[ProfileKeyTimezone], [ProfileKeyMSC4175Timezone]])("timezone rendering (%s)", (profileKey) => {
it("renders user timezone if set", async () => {
// For timezone, force a consistent locale.
jest.spyOn(global.Date.prototype, "toLocaleString").mockImplementation(function (
this: Date,
_locale,
opts,
) {
return origDate.call(this, "en-US", {
...opts,
hourCycle: "h12",
});
});
mockClient.doesServerSupportExtendedProfiles.mockResolvedValue(true);
mockClient.getExtendedProfile.mockResolvedValue({ [profileKey]: "Europe/London" });
renderComponent();
await expect(screen.findByText(/\d\d:\d\d (AM|PM)/)).resolves.toBeInTheDocument();
});
mockClient.doesServerSupportExtendedProfiles.mockResolvedValue(true);
mockClient.getExtendedProfileProperty.mockResolvedValue("Europe/London");
renderComponent();
await expect(screen.findByText(/\d\d:\d\d (AM|PM)/)).resolves.toBeInTheDocument();
});

it("does not renders user timezone if timezone is invalid", async () => {
mockClient.doesServerSupportExtendedProfiles.mockResolvedValue(true);
mockClient.getExtendedProfileProperty.mockResolvedValue("invalid-tz");
renderComponent();
expect(screen.queryByText(/\d\d:\d\d (AM|PM)/)).not.toBeInTheDocument();
it("does not renders user timezone if timezone is invalid", async () => {
mockClient.doesServerSupportExtendedProfiles.mockResolvedValue(true);
mockClient.getExtendedProfile.mockResolvedValue({ [profileKey]: "invalid-tz" });
renderComponent();
expect(screen.queryByText(/\d\d:\d\d (AM|PM)/)).not.toBeInTheDocument();
});
});

it("renders encryption info panel without pending verification", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<UserInfo /> with crypto enabled renders <BasicUserInfo /> 1`] = `
<div>
Expand All @@ -19,7 +19,7 @@ exports[`<UserInfo /> with crypto enabled renders <BasicUserInfo /> 1`] = `
</p>
</div>
<button
aria-labelledby="«r6i»"
aria-labelledby="«r7c»"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
Expand Down Expand Up @@ -306,7 +306,7 @@ exports[`<UserInfo /> with crypto enabled should render a deactivate button for
</p>
</div>
<button
aria-labelledby="«r6s»"
aria-labelledby="«r7m»"
class="_icon-button_1pz9o_8"
data-kind="secondary"
data-testid="base-card-close-button"
Expand Down
Loading