From 05b1826bbee02553071688898123c454f1745d53 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 6 Sep 2024 14:02:23 +0100 Subject: [PATCH 01/18] Add timezone to right panel profile. --- src/components/views/right_panel/UserInfo.tsx | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 9cc50fa9287..8019bda1f1f 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -34,7 +34,7 @@ import { KnownMembership } from "matrix-js-sdk/src/types"; import { UserVerificationStatus, VerificationRequest } from "matrix-js-sdk/src/crypto-api"; import { logger } from "matrix-js-sdk/src/logger"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; -import { Heading, MenuItem, Text } from "@vector-im/compound-web"; +import { Heading, MenuItem, Text, Tooltip } from "@vector-im/compound-web"; import ChatIcon from "@vector-im/compound-design-tokens/assets/web/icons/chat"; import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check"; import ShareIcon from "@vector-im/compound-design-tokens/assets/web/icons/share"; @@ -1702,6 +1702,30 @@ export const UserInfoHeader: React.FC<{ ); } + + // TODO: Check for support for this property. + const [timezone, setTimezone] = useState<{friendly: string, tz: string}>(); + useEffect(() => { + cli.getExtendedProfileProperty(member.userId, 'us.cloke.msc4175.tz').then((value) => { + if (typeof value !== "string") { + // Err, definitely not a tz. + return; + } + try { + setTimezone( + { + friendly: new Date().toLocaleString(undefined, { timeZone: value, hour12: true, hour: "2-digit", minute: "2-digit", timeZoneName: "shortOffset"}), + tz: value + } + ); + } catch (ex) { + console.error('Could not render current time for user', ex); + } + }).catch((ex) => { + console.error('Unable to load user timezone', ex); + }); + }, [member.userId]); + const e2eIcon = e2eStatus ? : null; const userIdentifier = UserIdentifierCustomisations.getDisplayUserIdentifier?.(member.userId, { roomId, @@ -1734,7 +1758,7 @@ export const UserInfoHeader: React.FC<{ {e2eIcon} - {presenceLabel} + {presenceLabel} {timezone && {timezone.friendly}} userIdentifier} border={false}> {userIdentifier} From c103109e9d7958ef68e607aecad841de44a5dfbd Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 6 Sep 2024 14:02:32 +0100 Subject: [PATCH 02/18] Add setting to publish timezone --- .../settings/UserPersonalInfoSettings.tsx | 20 +++++++++++++++++++ .../tabs/user/PreferencesUserSettingsTab.tsx | 2 +- src/settings/Settings.tsx | 6 ++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/UserPersonalInfoSettings.tsx b/src/components/views/settings/UserPersonalInfoSettings.tsx index 63925424aa1..721d7227b0c 100644 --- a/src/components/views/settings/UserPersonalInfoSettings.tsx +++ b/src/components/views/settings/UserPersonalInfoSettings.tsx @@ -130,6 +130,26 @@ export const UserPersonalInfoSettings: React.FC = /> + + + + + + ); }; diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index d74ea346620..0fc8690fe22 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -143,7 +143,7 @@ export default class PreferencesUserSettingsTab extends React.Component Date: Fri, 6 Sep 2024 14:02:43 +0100 Subject: [PATCH 03/18] Add string for timezone publish --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e1e44efc61d..77767c1d09c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2721,6 +2721,7 @@ "keyboard_view_shortcuts_button": "To view all keyboard shortcuts, click here.", "media_heading": "Images, GIFs and videos", "presence_description": "Share your activity and status with others.", + "publish_timezone": "Publish timezone on public profile", "rm_lifetime": "Read Marker lifetime (ms)", "rm_lifetime_offscreen": "Read Marker off-screen lifetime (ms)", "room_directory_heading": "Room directory", From e78dfc2bad7165ccaf1eb659ab98845cba8c605c Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 6 Sep 2024 14:02:56 +0100 Subject: [PATCH 04/18] Automatically update timezone when setting changes. --- src/components/structures/LoggedInView.tsx | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 755d2c11568..4e9ada99b26 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -139,6 +139,7 @@ class LoggedInView extends React.Component { protected layoutWatcherRef?: string; protected compactLayoutWatcherRef?: string; protected backgroundImageWatcherRef?: string; + protected timezoneProfileUpdateRef?: string[]; protected resizer?: Resizer; public constructor(props: IProps) { @@ -190,6 +191,21 @@ class LoggedInView extends React.Component { this.refreshBackgroundImage, ); + + this.timezoneProfileUpdateRef = [ + SettingsStore.watchSetting( + "userTimezonePublish", + null, + this.onTimezoneUpdate, + ), + SettingsStore.watchSetting( + "userTimezone", + null, + this.onTimezoneUpdate, + ), + + ]; + this.resizer = this.createResizer(); this.resizer.attach(); @@ -198,6 +214,28 @@ class LoggedInView extends React.Component { this.refreshBackgroundImage(); } + private onTimezoneUpdate = async (): Promise => { + console.log('Triggering timezoen update', SettingsStore); + if (!SettingsStore.getValue("userTimezonePublish")) { + // Ensure it's deleted + try { + await this._matrixClient.deleteExtendedProfileProperty("us.cloke.msc4175.tz"); + } catch (ex) { + console.warn("Failed to delete timezone from user profile", ex); + } + return; + } + const currentTimezone = SettingsStore.getValue("userTimezone"); + if (!currentTimezone || typeof currentTimezone !== "string") { + return; + } + try { + await this._matrixClient.setExtendedProfileProperty("us.cloke.msc4175.tz", currentTimezone) + } catch (ex) { + console.warn("Failed to update user profile with current timezone", ex); + } + }; + public componentWillUnmount(): void { document.removeEventListener("keydown", this.onNativeKeyDown, false); LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.onCallState); @@ -208,6 +246,7 @@ class LoggedInView extends React.Component { if (this.layoutWatcherRef) SettingsStore.unwatchSetting(this.layoutWatcherRef); if (this.compactLayoutWatcherRef) SettingsStore.unwatchSetting(this.compactLayoutWatcherRef); if (this.backgroundImageWatcherRef) SettingsStore.unwatchSetting(this.backgroundImageWatcherRef); + this.timezoneProfileUpdateRef?.forEach(s => SettingsStore.unwatchSetting(s)); this.resizer?.detach(); } From 6989162252470128bc7e11733c0f05ecff19e592 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Sun, 8 Sep 2024 10:55:29 +0100 Subject: [PATCH 05/18] Refactor to using a hook And automatically refresh the timezone every minute. --- src/components/views/right_panel/UserInfo.tsx | 28 +------ src/hooks/useUserTimezone.ts | 82 +++++++++++++++++++ 2 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 src/hooks/useUserTimezone.ts diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 8019bda1f1f..60af901975e 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -29,6 +29,7 @@ import { User, Device, EventType, + MatrixError, } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { UserVerificationStatus, VerificationRequest } from "matrix-js-sdk/src/crypto-api"; @@ -93,7 +94,7 @@ import { SdkContextClass } from "../../../contexts/SDKContext"; import { asyncSome } from "../../../utils/arrays"; import { Flex } from "../../utils/Flex"; import CopyableText from "../elements/CopyableText"; - +import { useUserTimezone } from "../../../hooks/useUserTimezone"; export interface IDevice extends Device { ambiguous?: boolean; } @@ -1703,28 +1704,7 @@ export const UserInfoHeader: React.FC<{ } - // TODO: Check for support for this property. - const [timezone, setTimezone] = useState<{friendly: string, tz: string}>(); - useEffect(() => { - cli.getExtendedProfileProperty(member.userId, 'us.cloke.msc4175.tz').then((value) => { - if (typeof value !== "string") { - // Err, definitely not a tz. - return; - } - try { - setTimezone( - { - friendly: new Date().toLocaleString(undefined, { timeZone: value, hour12: true, hour: "2-digit", minute: "2-digit", timeZoneName: "shortOffset"}), - tz: value - } - ); - } catch (ex) { - console.error('Could not render current time for user', ex); - } - }).catch((ex) => { - console.error('Unable to load user timezone', ex); - }); - }, [member.userId]); + const timezoneInfo = useUserTimezone(member.userId); const e2eIcon = e2eStatus ? : null; const userIdentifier = UserIdentifierCustomisations.getDisplayUserIdentifier?.(member.userId, { @@ -1758,7 +1738,7 @@ export const UserInfoHeader: React.FC<{ {e2eIcon} - {presenceLabel} {timezone && {timezone.friendly}} + {presenceLabel} {timezoneInfo && {timezoneInfo.friendly}} userIdentifier} border={false}> {userIdentifier} diff --git a/src/hooks/useUserTimezone.ts b/src/hooks/useUserTimezone.ts new file mode 100644 index 00000000000..d47888315a8 --- /dev/null +++ b/src/hooks/useUserTimezone.ts @@ -0,0 +1,82 @@ +import { useEffect, useState } from "react"; +import { MatrixClientPeg } from "../MatrixClientPeg"; +import { MatrixError } from "matrix-js-sdk/src/matrix"; + +/** + * Fetch a user's delclared timezone through their profile, and return + * a friendly string of the current time for that user. This will keep + * in sync with the current time, and will be refreshed once a minute. + * + * @param userId The userID to fetch the timezone for. + * @returns A timezone name and friendly string for the user's timezone, or + * null if the user has no timezone or the timezone was not recognised + * by the browser. + */ +export const useUserTimezone = (userId: string): { timezone: string, friendly: string }|null => { + const [timezone, setTimezone] = useState(); + const [updateInterval, setUpdateInterval] = useState(); + const [friendly, setFriendly] = useState(); + const [supported, setSupported] = useState(); + const cli = MatrixClientPeg.safeGet(); + + useEffect(() => { + if (supported !== undefined) { + return; + } + cli.doesServerSupportExtendedProfiles().then(setSupported).catch((ex) => { + console.warn("Unable to determine if extended profiles are supported", ex); + }); + }, [supported]); + + useEffect(() => { + return () => { + if (updateInterval) { + clearInterval(updateInterval); + } + } + }, [updateInterval]); + + useEffect(() => { + if (supported !== true) { + return; + } + (async () => { + try { + const tz = await cli.getExtendedProfileProperty(userId, 'us.cloke.msc4175.tz'); + if (typeof tz !== "string") { + // Err, definitely not a tz. + throw Error('Timezone value was not a string'); + } + // This will validate the timezone for us. + Intl.DateTimeFormat(undefined, {timeZone: tz}); + + const updateTime = () => { + const currentTime = new Date(); + const friendly = currentTime.toLocaleString(undefined, { timeZone: tz, hour12: true, hour: "2-digit", minute: "2-digit", timeZoneName: "shortOffset"}); + setTimezone(tz); + setFriendly(friendly); + setUpdateInterval(setTimeout(updateTime, (60 - currentTime.getSeconds()) * 1000)); + } + updateTime(); + } catch (ex) { + setTimezone(undefined); + setFriendly(undefined); + setUpdateInterval(undefined); + if (ex instanceof MatrixError && ex.errcode === "M_NOT_FOUND") { + // No timezone set, ignore. + return; + } + console.error('Could not render current timezone for user', ex); + } + })(); + }, [supported, userId]); + + if (!timezone || !friendly) { + return null; + } + + return { + friendly, + timezone + }; +} \ No newline at end of file From 34d317645a0c6530a62e537134e283dbb13f617c Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Sun, 8 Sep 2024 11:07:16 +0100 Subject: [PATCH 06/18] Check for feature support for extended profiles. --- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.tsx | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 77767c1d09c..d394b93b5ed 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1426,6 +1426,7 @@ "element_call_video_rooms": "Element Call video rooms", "experimental_description": "Feeling experimental? Try out our latest ideas in development. These features are not finalised; they may be unstable, may change, or may be dropped altogether. Learn more.", "experimental_section": "Early previews", + "extended_profiles_msc_support": "Requires your server to support MSC4133", "feature_disable_call_per_sender_encryption": "Disable per-sender encryption for Element Call", "feature_wysiwyg_composer_description": "Use rich text instead of Markdown in the message composer.", "group_calls": "New group call experience", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 34ce9405256..876b88f743f 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -43,6 +43,7 @@ import ServerSupportUnstableFeatureController from "./controllers/ServerSupportU import { WatchManager } from "./WatchManager"; import { CustomTheme } from "../theme"; import AnalyticsController from "./controllers/AnalyticsController"; +import { UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/client" export const defaultWatchManager = new WatchManager(); @@ -654,11 +655,17 @@ export const SETTINGS: { [setting: string]: ISetting } = { displayName: _td("settings|preferences|user_timezone"), default: "", }, - // TODO: How to make this visible based on server feature levels? "userTimezonePublish": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td("settings|preferences|publish_timezone"), default: false, + controller: new ServerSupportUnstableFeatureController( + "userTimezonePublish", + defaultWatchManager, + [[UNSTABLE_MSC4133_EXTENDED_PROFILES]], + undefined, + _td("labs|extended_profiles_msc_support"), + ), }, "autoplayGifs": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 8e39e0639f5c2ea3badeeb85ace7310c32738eb3 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:17:20 +0100 Subject: [PATCH 07/18] lint --- res/css/views/right_panel/_UserInfo.pcss | 6 ++ src/components/structures/LoggedInView.tsx | 20 ++----- src/components/views/right_panel/UserInfo.tsx | 11 +++- .../settings/UserPersonalInfoSettings.tsx | 6 +- src/hooks/useUserTimezone.ts | 59 +++++++++++++------ src/settings/Settings.tsx | 2 +- 6 files changed, 63 insertions(+), 41 deletions(-) diff --git a/res/css/views/right_panel/_UserInfo.pcss b/res/css/views/right_panel/_UserInfo.pcss index 186eb78a32f..b0824f704f6 100644 --- a/res/css/views/right_panel/_UserInfo.pcss +++ b/res/css/views/right_panel/_UserInfo.pcss @@ -132,6 +132,12 @@ limitations under the License. } } + .mx_UserInfo_timezone { + .mx_UserInfo_profileStatus { + margin: var(--cpd-space-1x) 0; + } + } + .mx_PresenceLabel { font: var(--cpd-font-body-sm-regular); opacity: 1; diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 4e9ada99b26..1a92748375a 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -191,19 +191,9 @@ class LoggedInView extends React.Component { this.refreshBackgroundImage, ); - this.timezoneProfileUpdateRef = [ - SettingsStore.watchSetting( - "userTimezonePublish", - null, - this.onTimezoneUpdate, - ), - SettingsStore.watchSetting( - "userTimezone", - null, - this.onTimezoneUpdate, - ), - + SettingsStore.watchSetting("userTimezonePublish", null, this.onTimezoneUpdate), + SettingsStore.watchSetting("userTimezone", null, this.onTimezoneUpdate), ]; this.resizer = this.createResizer(); @@ -215,7 +205,7 @@ class LoggedInView extends React.Component { } private onTimezoneUpdate = async (): Promise => { - console.log('Triggering timezoen update', SettingsStore); + console.log("Triggering timezoen update", SettingsStore); if (!SettingsStore.getValue("userTimezonePublish")) { // Ensure it's deleted try { @@ -230,7 +220,7 @@ class LoggedInView extends React.Component { return; } try { - await this._matrixClient.setExtendedProfileProperty("us.cloke.msc4175.tz", currentTimezone) + await this._matrixClient.setExtendedProfileProperty("us.cloke.msc4175.tz", currentTimezone); } catch (ex) { console.warn("Failed to update user profile with current timezone", ex); } @@ -246,7 +236,7 @@ class LoggedInView extends React.Component { if (this.layoutWatcherRef) SettingsStore.unwatchSetting(this.layoutWatcherRef); if (this.compactLayoutWatcherRef) SettingsStore.unwatchSetting(this.compactLayoutWatcherRef); if (this.backgroundImageWatcherRef) SettingsStore.unwatchSetting(this.backgroundImageWatcherRef); - this.timezoneProfileUpdateRef?.forEach(s => SettingsStore.unwatchSetting(s)); + this.timezoneProfileUpdateRef?.forEach((s) => SettingsStore.unwatchSetting(s)); this.resizer?.detach(); } diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 60af901975e..4b707f589d1 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -29,7 +29,6 @@ import { User, Device, EventType, - MatrixError, } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import { UserVerificationStatus, VerificationRequest } from "matrix-js-sdk/src/crypto-api"; @@ -1703,7 +1702,6 @@ export const UserInfoHeader: React.FC<{ ); } - const timezoneInfo = useUserTimezone(member.userId); const e2eIcon = e2eStatus ? : null; @@ -1738,7 +1736,14 @@ export const UserInfoHeader: React.FC<{ {e2eIcon} - {presenceLabel} {timezoneInfo && {timezoneInfo.friendly}} + {presenceLabel} + + + + {timezoneInfo?.friendly ?? ""} + + + userIdentifier} border={false}> {userIdentifier} diff --git a/src/components/views/settings/UserPersonalInfoSettings.tsx b/src/components/views/settings/UserPersonalInfoSettings.tsx index 721d7227b0c..a4e3c1e4d06 100644 --- a/src/components/views/settings/UserPersonalInfoSettings.tsx +++ b/src/components/views/settings/UserPersonalInfoSettings.tsx @@ -131,11 +131,7 @@ export const UserPersonalInfoSettings: React.FC = - + { +export const useUserTimezone = (userId: string): { timezone: string; friendly: string } | null => { const [timezone, setTimezone] = useState(); const [updateInterval, setUpdateInterval] = useState(); const [friendly, setFriendly] = useState(); @@ -23,17 +39,19 @@ export const useUserTimezone = (userId: string): { timezone: string, friendly: s if (supported !== undefined) { return; } - cli.doesServerSupportExtendedProfiles().then(setSupported).catch((ex) => { - console.warn("Unable to determine if extended profiles are supported", ex); - }); - }, [supported]); + cli.doesServerSupportExtendedProfiles() + .then(setSupported) + .catch((ex) => { + console.warn("Unable to determine if extended profiles are supported", ex); + }); + }, [supported, cli]); useEffect(() => { return () => { if (updateInterval) { clearInterval(updateInterval); } - } + }; }, [updateInterval]); useEffect(() => { @@ -42,21 +60,28 @@ export const useUserTimezone = (userId: string): { timezone: string, friendly: s } (async () => { try { - const tz = await cli.getExtendedProfileProperty(userId, 'us.cloke.msc4175.tz'); + const tz = await cli.getExtendedProfileProperty(userId, "us.cloke.msc4175.tz"); if (typeof tz !== "string") { // Err, definitely not a tz. - throw Error('Timezone value was not a string'); + throw Error("Timezone value was not a string"); } // This will validate the timezone for us. - Intl.DateTimeFormat(undefined, {timeZone: tz}); + // eslint-disable-next-line new-cap + Intl.DateTimeFormat(undefined, { timeZone: tz }); - const updateTime = () => { + const updateTime = (): void => { const currentTime = new Date(); - const friendly = currentTime.toLocaleString(undefined, { timeZone: tz, hour12: true, hour: "2-digit", minute: "2-digit", timeZoneName: "shortOffset"}); + const friendly = currentTime.toLocaleString(undefined, { + timeZone: tz, + hour12: true, + hour: "2-digit", + minute: "2-digit", + timeZoneName: "shortOffset", + }); setTimezone(tz); setFriendly(friendly); setUpdateInterval(setTimeout(updateTime, (60 - currentTime.getSeconds()) * 1000)); - } + }; updateTime(); } catch (ex) { setTimezone(undefined); @@ -66,10 +91,10 @@ export const useUserTimezone = (userId: string): { timezone: string, friendly: s // No timezone set, ignore. return; } - console.error('Could not render current timezone for user', ex); + console.error("Could not render current timezone for user", ex); } })(); - }, [supported, userId]); + }, [supported, userId, cli]); if (!timezone || !friendly) { return null; @@ -77,6 +102,6 @@ export const useUserTimezone = (userId: string): { timezone: string, friendly: s return { friendly, - timezone + timezone, }; -} \ No newline at end of file +}; diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 876b88f743f..c0d61c1506a 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -16,6 +16,7 @@ limitations under the License. */ import React, { ReactNode } from "react"; +import { UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/matrix"; import { _t, _td, TranslationKey } from "../languageHandler"; import { @@ -43,7 +44,6 @@ import ServerSupportUnstableFeatureController from "./controllers/ServerSupportU import { WatchManager } from "./WatchManager"; import { CustomTheme } from "../theme"; import AnalyticsController from "./controllers/AnalyticsController"; -import { UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/client" export const defaultWatchManager = new WatchManager(); From 6fc5450a3a92476c6c18cbf79dab3951cad5aa33 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:20:33 +0100 Subject: [PATCH 08/18] Add timezone --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d394b93b5ed..024c14f657a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2579,6 +2579,7 @@ "remove_email_prompt": "Remove %(email)s?", "remove_msisdn_prompt": "Remove %(phone)s?", "spell_check_locale_placeholder": "Choose a locale", + "timezone": "settings|general|timezone", "unable_to_load_emails": "Unable to load email addresses", "unable_to_load_msisdns": "Unable to load phone numbers", "username": "Username" From 96d7fda74718aa5c0143e9901d2010462e6effbf Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:21:27 +0100 Subject: [PATCH 09/18] Remove unintentional changes --- .../views/settings/UserPersonalInfoSettings.tsx | 16 ---------------- src/i18n/strings/en_EN.json | 1 - 2 files changed, 17 deletions(-) diff --git a/src/components/views/settings/UserPersonalInfoSettings.tsx b/src/components/views/settings/UserPersonalInfoSettings.tsx index a4e3c1e4d06..63925424aa1 100644 --- a/src/components/views/settings/UserPersonalInfoSettings.tsx +++ b/src/components/views/settings/UserPersonalInfoSettings.tsx @@ -130,22 +130,6 @@ export const UserPersonalInfoSettings: React.FC = /> - - - - - - ); }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 024c14f657a..d394b93b5ed 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2579,7 +2579,6 @@ "remove_email_prompt": "Remove %(email)s?", "remove_msisdn_prompt": "Remove %(phone)s?", "spell_check_locale_placeholder": "Choose a locale", - "timezone": "settings|general|timezone", "unable_to_load_emails": "Unable to load email addresses", "unable_to_load_msisdns": "Unable to load phone numbers", "username": "Username" From 8ec6294a7e92a0818675e956eb7e9a031111badb Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:29:30 +0100 Subject: [PATCH 10/18] Use browser default timezone. --- src/components/structures/LoggedInView.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 1a92748375a..a77b9a31855 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -205,7 +205,6 @@ class LoggedInView extends React.Component { } private onTimezoneUpdate = async (): Promise => { - console.log("Triggering timezoen update", SettingsStore); if (!SettingsStore.getValue("userTimezonePublish")) { // Ensure it's deleted try { @@ -215,7 +214,9 @@ class LoggedInView extends React.Component { } return; } - const currentTimezone = SettingsStore.getValue("userTimezone"); + const currentTimezone = SettingsStore.getValue("userTimezone") + // If the timezone is empty, then use the browser timezone. + || Intl.DateTimeFormat().resolvedOptions().timeZone; if (!currentTimezone || typeof currentTimezone !== "string") { return; } From 45285f26ff58e8c9b2c7a8f2aac67c5573cfb410 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:30:42 +0100 Subject: [PATCH 11/18] lint --- src/components/structures/LoggedInView.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index a77b9a31855..3ead748da50 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -214,9 +214,10 @@ class LoggedInView extends React.Component { } return; } - const currentTimezone = SettingsStore.getValue("userTimezone") - // If the timezone is empty, then use the browser timezone. - || Intl.DateTimeFormat().resolvedOptions().timeZone; + const currentTimezone = + SettingsStore.getValue("userTimezone") || + // If the timezone is empty, then use the browser timezone. + Intl.DateTimeFormat().resolvedOptions().timeZone; if (!currentTimezone || typeof currentTimezone !== "string") { return; } From 5ae6e2e0e1ce9e7d46c14987ebbe90544a60f1a7 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:36:32 +0100 Subject: [PATCH 12/18] tweaks --- res/css/views/right_panel/_UserInfo.pcss | 4 +--- src/components/structures/LoggedInView.tsx | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/res/css/views/right_panel/_UserInfo.pcss b/res/css/views/right_panel/_UserInfo.pcss index b0824f704f6..3a5dfe3b053 100644 --- a/res/css/views/right_panel/_UserInfo.pcss +++ b/res/css/views/right_panel/_UserInfo.pcss @@ -133,9 +133,7 @@ limitations under the License. } .mx_UserInfo_timezone { - .mx_UserInfo_profileStatus { - margin: var(--cpd-space-1x) 0; - } + margin: var(--cpd-space-1x) 0; } .mx_PresenceLabel { diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 3ead748da50..cba172a0715 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -217,6 +217,7 @@ class LoggedInView extends React.Component { const currentTimezone = SettingsStore.getValue("userTimezone") || // If the timezone is empty, then use the browser timezone. + // eslint-disable-next-line new-cap Intl.DateTimeFormat().resolvedOptions().timeZone; if (!currentTimezone || typeof currentTimezone !== "string") { return; From b12e4cd5aadee491c302d103ad1fc69a7032bd23 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 9 Sep 2024 17:49:38 +0100 Subject: [PATCH 13/18] Set timezone publish at the device level to prevent all devices writing to the timezone field. --- .../views/settings/tabs/user/PreferencesUserSettingsTab.tsx | 3 ++- src/settings/Settings.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index 0fc8690fe22..39ebb092ec6 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -143,7 +143,7 @@ export default class PreferencesUserSettingsTab extends React.Component {this.renderGroup(PreferencesUserSettingsTab.TIME_SETTINGS)} + Date: Tue, 10 Sep 2024 10:10:20 +0100 Subject: [PATCH 14/18] Update hook to use external client. --- src/components/views/right_panel/UserInfo.tsx | 3 ++- src/hooks/useUserTimezone.ts | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 4b707f589d1..c68e39b8dc4 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -1702,7 +1702,7 @@ export const UserInfoHeader: React.FC<{ ); } - const timezoneInfo = useUserTimezone(member.userId); + const timezoneInfo = useUserTimezone(cli, member.userId); const e2eIcon = e2eStatus ? : null; const userIdentifier = UserIdentifierCustomisations.getDisplayUserIdentifier?.(member.userId, { @@ -1710,6 +1710,7 @@ export const UserInfoHeader: React.FC<{ withDisplayName: true, }); const displayName = (member as RoomMember).rawDisplayName; + console.log("booop", timezoneInfo); return (
diff --git a/src/hooks/useUserTimezone.ts b/src/hooks/useUserTimezone.ts index bcae43d6d86..11198be1faa 100644 --- a/src/hooks/useUserTimezone.ts +++ b/src/hooks/useUserTimezone.ts @@ -14,29 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. */ import { useEffect, useState } from "react"; -import { MatrixError } from "matrix-js-sdk/src/matrix"; - -import { MatrixClientPeg } from "../MatrixClientPeg"; +import { MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix"; /** * Fetch a user's delclared timezone through their profile, and return * a friendly string of the current time for that user. This will keep * in sync with the current time, and will be refreshed once a minute. * + * @param cli The Matrix Client instance. * @param userId The userID to fetch the timezone for. * @returns A timezone name and friendly string for the user's timezone, or * null if the user has no timezone or the timezone was not recognised * by the browser. */ -export const useUserTimezone = (userId: string): { timezone: string; friendly: string } | null => { +export const useUserTimezone = (cli: MatrixClient, userId: string): { timezone: string; friendly: string } | null => { const [timezone, setTimezone] = useState(); const [updateInterval, setUpdateInterval] = useState(); const [friendly, setFriendly] = useState(); const [supported, setSupported] = useState(); - const cli = MatrixClientPeg.safeGet(); useEffect(() => { - if (supported !== undefined) { + if (!cli || supported !== undefined) { return; } cli.doesServerSupportExtendedProfiles() @@ -59,6 +57,7 @@ export const useUserTimezone = (userId: string): { timezone: string; friendly: s return; } (async () => { + console.log("Trying to fetch TZ"); try { const tz = await cli.getExtendedProfileProperty(userId, "us.cloke.msc4175.tz"); if (typeof tz !== "string") { From 4e11e7d07871e93be95dabd26ae292fd6369d49a Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 10 Sep 2024 10:10:26 +0100 Subject: [PATCH 15/18] Add test for user timezone. --- test/components/views/right_panel/UserInfo-test.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx index a12ce75b2ea..891d60b5012 100644 --- a/test/components/views/right_panel/UserInfo-test.tsx +++ b/test/components/views/right_panel/UserInfo-test.tsx @@ -158,6 +158,8 @@ beforeEach(() => { isSynapseAdministrator: jest.fn().mockResolvedValue(false), isRoomEncrypted: jest.fn().mockReturnValue(false), doesServerSupportUnstableFeature: jest.fn().mockReturnValue(false), + doesServerSupportExtendedProfiles: jest.fn().mockResolvedValue(false), + getExtendedProfileProperty: jest.fn().mockRejectedValue(new Error("Not supported")), mxcUrlToHttp: jest.fn().mockReturnValue("mock-mxcUrlToHttp"), removeListener: jest.fn(), currentState: { @@ -237,6 +239,13 @@ describe("", () => { expect(screen.getByRole("heading", { name: defaultUserId })).toBeInTheDocument(); }); + it("renders user timezone if set", async () => { + mockClient.doesServerSupportExtendedProfiles.mockResolvedValue(true); + mockClient.getExtendedProfileProperty.mockResolvedValue("Europe/London"); + renderComponent(); + await expect(screen.findByText(/\d\d:\d\d (am|pm)/)).resolves.toBeInTheDocument(); + }); + it("renders encryption info panel without pending verification", () => { renderComponent({ phase: RightPanelPhases.EncryptionPanel }); expect(screen.getByRole("heading", { name: /encryption/i })).toBeInTheDocument(); From b176ca8b9ce8af18b6250f4967c09202be41834f Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Tue, 10 Sep 2024 10:10:47 +0100 Subject: [PATCH 16/18] Update snapshot for preferences tab. --- .../PreferencesUserSettingsTab-test.tsx.snap | 143 +++++++++++------- 1 file changed, 85 insertions(+), 58 deletions(-) diff --git a/test/components/views/settings/tabs/user/__snapshots__/PreferencesUserSettingsTab-test.tsx.snap b/test/components/views/settings/tabs/user/__snapshots__/PreferencesUserSettingsTab-test.tsx.snap index 74b5375ebc1..ddbbe2e000a 100644 --- a/test/components/views/settings/tabs/user/__snapshots__/PreferencesUserSettingsTab-test.tsx.snap +++ b/test/components/views/settings/tabs/user/__snapshots__/PreferencesUserSettingsTab-test.tsx.snap @@ -307,6 +307,33 @@ exports[`PreferencesUserSettingsTab should render 1`] = ` />
+
+ +
+
+
+