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

Commit bb327f7

Browse files
committed
Fix all obvious instances of SdkConfig case conflicts
1 parent da217d2 commit bb327f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+235
-154
lines changed

src/@types/common.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ export type RecursivePartial<T> = {
4242
T[P];
4343
};
4444

45-
// Condensed but also expanded version of https://stackoverflow.com/a/60206860
46-
export type KeysOfStrictType<Input, SearchType> = {
47-
[P in keyof Input]: Input[P] extends SearchType
48-
? (SearchType extends Input[P] ? P : never)
45+
// Inspired by https://stackoverflow.com/a/60206860
46+
export type KeysWithObjectShape<Input> = {
47+
[P in keyof Input]: Input[P] extends object
48+
// Arrays are counted as objects - exclude them
49+
? (Input[P] extends Array<unknown> ? never : P)
4950
: never;
5051
}[keyof Input];

src/Analytics.tsx

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@ limitations under the License.
1717

1818
import React from 'react';
1919
import { logger } from "matrix-js-sdk/src/logger";
20+
import { Optional } from "matrix-events-sdk";
2021

2122
import { getCurrentLanguage, _t, _td, IVariables } from './languageHandler';
2223
import PlatformPeg from './PlatformPeg';
2324
import SdkConfig from './SdkConfig';
2425
import Modal from './Modal';
2526
import * as sdk from './index';
27+
import { SnakedObject } from "./utils/SnakedObject";
28+
import { IConfigOptions } from "./IConfigOptions";
2629

2730
const hashRegex = /#\/(groups?|room|user|settings|register|login|forgot_password|home|directory)/;
2831
const hashVarRegex = /#\/(group|room|user)\/.*$/;
@@ -193,8 +196,12 @@ export class Analytics {
193196
}
194197

195198
public canEnable() {
196-
const config = SdkConfig.get();
197-
return navigator.doNotTrack !== "1" && config && config.piwik && config.piwik.url && config.piwik.siteId;
199+
const piwikConfig = SdkConfig.get("piwik");
200+
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
201+
if (typeof piwikConfig === 'object') {
202+
piwik = new SnakedObject(piwikConfig);
203+
}
204+
return navigator.doNotTrack !== "1" && piwik?.get("site_id");
198205
}
199206

200207
/**
@@ -204,12 +211,16 @@ export class Analytics {
204211
public async enable() {
205212
if (!this.disabled) return;
206213
if (!this.canEnable()) return;
207-
const config = SdkConfig.get();
214+
const piwikConfig = SdkConfig.get("piwik");
215+
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
216+
if (typeof piwikConfig === 'object') {
217+
piwik = new SnakedObject(piwikConfig);
218+
}
208219

209-
this.baseUrl = new URL("piwik.php", config.piwik.url);
220+
this.baseUrl = new URL("piwik.php", piwik.get("url"));
210221
// set constants
211222
this.baseUrl.searchParams.set("rec", "1"); // rec is required for tracking
212-
this.baseUrl.searchParams.set("idsite", config.piwik.siteId); // rec is required for tracking
223+
this.baseUrl.searchParams.set("idsite", piwik.get("site_id")); // idsite is required for tracking
213224
this.baseUrl.searchParams.set("apiv", "1"); // API version to use
214225
this.baseUrl.searchParams.set("send_image", "0"); // we want a 204, not a tiny GIF
215226
// set user parameters
@@ -347,10 +358,14 @@ export class Analytics {
347358
public setLoggedIn(isGuest: boolean, homeserverUrl: string) {
348359
if (this.disabled) return;
349360

350-
const config = SdkConfig.get();
351-
if (!config.piwik) return;
361+
const piwikConfig = SdkConfig.get("piwik");
362+
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
363+
if (typeof piwikConfig === 'object') {
364+
piwik = new SnakedObject(piwikConfig);
365+
}
366+
if (!piwik) return;
352367

353-
const whitelistedHSUrls = config.piwik.whitelistedHSUrls || [];
368+
const whitelistedHSUrls = piwik.get("whitelisted_hs_urls", "whitelistedHSUrls") || [];
354369

355370
this.setVisitVariable('User Type', isGuest ? 'Guest' : 'Logged In');
356371
this.setVisitVariable('Homeserver URL', whitelistRedact(whitelistedHSUrls, homeserverUrl));
@@ -391,7 +406,12 @@ export class Analytics {
391406
];
392407

393408
// FIXME: Using an import will result in test failures
394-
const cookiePolicyUrl = SdkConfig.get().piwik?.policyUrl;
409+
const piwikConfig = SdkConfig.get("piwik");
410+
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
411+
if (typeof piwikConfig === 'object') {
412+
piwik = new SnakedObject(piwikConfig);
413+
}
414+
const cookiePolicyUrl = piwik?.get("policy_url");
395415
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
396416
const cookiePolicyLink = _t(
397417
"Our complete cookie policy can be found <CookiePolicyLink>here</CookiePolicyLink>.",

src/CallHandler.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export default class CallHandler extends EventEmitter {
259259
}
260260

261261
private shouldObeyAssertedfIdentity(): boolean {
262-
return SdkConfig.get()['voip']?.obeyAssertedIdentity;
262+
return SdkConfig.getObject("voip")?.get("obey_asserted_identity");
263263
}
264264

265265
public getSupportsPstnProtocol(): boolean {

src/IConfigOptions.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717

1818
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
1919

20+
import { ValidatedServerConfig } from "./utils/AutoDiscoveryUtils";
21+
2022
// Convention decision: All config options are lower_snake_case
2123
// We use an isolated file for the interface so we can mess around with the eslint options.
2224

@@ -25,8 +27,6 @@ import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
2527

2628
// see element-web config.md for non-developer docs
2729
export interface IConfigOptions {
28-
valToProveLintRuleWorks?: never;
29-
3030
// dev note: while true that this is arbitrary JSON, it's valuable to enforce that all
3131
// config options are documented for "find all usages" sort of searching.
3232
// [key: string]: any;
@@ -42,14 +42,20 @@ export interface IConfigOptions {
4242

4343
default_is_url?: string; // used in combination with default_hs_url, but for the identity server
4444

45+
// This is intended to be overridden by app startup and not specified by the user
46+
// This is also why it's allowed to have an interface that isn't snake_case
47+
validated_server_config?: ValidatedServerConfig;
48+
49+
fallback_hs_url?: string;
50+
4551
disable_custom_urls?: boolean;
4652
disable_guests?: boolean;
4753
disable_login_language_selector?: boolean;
4854
disable_3pid_login?: boolean;
4955

50-
brand?: string;
56+
brand: string;
5157
branding?: {
52-
welcome_background_url?: string;
58+
welcome_background_url?: string | string[]; // chosen at random if array
5359
auth_header_logo_url?: string;
5460
auth_footer_links?: {text: string, url: string}[];
5561
};
@@ -99,6 +105,7 @@ export interface IConfigOptions {
99105
environment?: string; // "production", etc
100106
};
101107

108+
widget_build_url?: string; // url called to replace jitsi/call widget creation
102109
audio_stream_url?: string;
103110
jitsi?: {
104111
preferred_domain: string;
@@ -128,7 +135,7 @@ export interface IConfigOptions {
128135
// piwik (matomo) is deprecated in favour of posthog
129136
piwik?: false | {
130137
url: string; // piwik instance
131-
site_id: string | number; // TODO: @@TR Typed correctly?
138+
site_id: string;
132139
policy_url: string; // cookie policy
133140
whitelisted_hs_urls: string[];
134141
};
@@ -137,6 +144,37 @@ export interface IConfigOptions {
137144
api_host: string; // hostname
138145
};
139146
analytics_owner?: string; // defaults to `brand`
147+
148+
// Server hosting upsell options
149+
hosting_signup_link?: string; // slightly different from `host_signup`
150+
host_signup?: {
151+
brand?: string; // acts as the enabled flag too (truthy == show)
152+
153+
// Required-ness denotes when `brand` is truthy
154+
cookie_policy_url: string;
155+
privacy_policy_url: string;
156+
terms_of_service_url: string;
157+
url: string;
158+
domains?: string[];
159+
};
160+
161+
enable_presence_by_hs_url?: Record<string, boolean>; // <HomeserverName, Enabled>
162+
163+
terms_and_conditions_links?: { url: string, text: string }[];
164+
165+
latex_maths_delims?: {
166+
inline?: {
167+
left?: string;
168+
right?: string;
169+
};
170+
display?: {
171+
left?: string;
172+
right?: string;
173+
};
174+
};
175+
176+
sync_timeline_limit?: number;
177+
dangerously_allow_unsafe_and_insecure_passwords?: boolean; // developer option
140178
}
141179

142180
export interface ISsoRedirectOptions {

src/KeyBindingsDefaults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ const callBindings = (): KeyBinding[] => {
161161
};
162162

163163
const labsBindings = (): KeyBinding[] => {
164-
if (!SdkConfig.get()['showLabsSettings']) return [];
164+
if (!SdkConfig.get("show_labs_settings")) return [];
165165

166166
return getBindingsByCategory(CategoryName.LABS);
167167
};

src/Livestream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import SdkConfig from "./SdkConfig";
2121
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
2222

2323
export function getConfigLivestreamUrl() {
24-
return SdkConfig.get()["audioStreamUrl"];
24+
return SdkConfig.get("audio_stream_url");
2525
}
2626

2727
// Dummy rtmp URL used to signal that we want a special audio-only stream

src/PosthogAnalytics.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,10 @@ export class PosthogAnalytics {
126126
}
127127

128128
constructor(private readonly posthog: PostHog) {
129-
const posthogConfig = SdkConfig.get()["posthog"];
129+
const posthogConfig = SdkConfig.getObject("posthog");
130130
if (posthogConfig) {
131-
this.posthog.init(posthogConfig.projectApiKey, {
132-
api_host: posthogConfig.apiHost,
131+
this.posthog.init(posthogConfig.get("project_api_key"), {
132+
api_host: posthogConfig.get("api_host"),
133133
autocapture: false,
134134
mask_all_text: true,
135135
mask_all_element_attributes: true,

src/SdkConfig.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { Optional } from "matrix-events-sdk";
1919

2020
import { SnakedObject } from "./utils/SnakedObject";
2121
import { IConfigOptions, ISsoRedirectOptions } from "./IConfigOptions";
22-
import { KeysOfStrictType } from "./@types/common";
22+
import { KeysWithObjectShape } from "./@types/common";
2323

2424
// see element-web config.md for docs, or the IConfigOptions interface for dev docs
2525
export const DEFAULTS: Partial<IConfigOptions> = {
@@ -30,7 +30,12 @@ export const DEFAULTS: Partial<IConfigOptions> = {
3030
jitsi: {
3131
preferred_domain: "meet.element.io",
3232
},
33-
desktop_builds: {
33+
34+
// @ts-ignore - we deliberately use the camelCase version here so we trigger
35+
// the fallback behaviour. If we used the snake_case version then we'd break
36+
// everyone's config which has the camelCase property because our default would
37+
// be preferred over their config.
38+
desktopBuilds: {
3439
available: true,
3540
logo: require("../res/img/element-desktop-logo.svg").default,
3641
url: "https://element.io/get-started",
@@ -54,11 +59,14 @@ export default class SdkConfig {
5459
public static get<K extends keyof IConfigOptions = never>(
5560
key?: K, altCaseName?: string,
5661
): IConfigOptions | IConfigOptions[K] {
57-
if (key === undefined) return SdkConfig.instance || {};
62+
if (key === undefined) {
63+
// safe to cast as a fallback - we want to break the runtime contract in this case
64+
return SdkConfig.instance || <IConfigOptions>{};
65+
}
5866
return SdkConfig.fallback.get(key, altCaseName);
5967
}
6068

61-
public static getObject<K extends KeysOfStrictType<IConfigOptions, object>>(
69+
public static getObject<K extends KeysWithObjectShape<IConfigOptions>>(
6270
key: K, altCaseName?: string,
6371
): Optional<SnakedObject<IConfigOptions[K]>> {
6472
const val = SdkConfig.get(key, altCaseName);
@@ -81,10 +89,10 @@ export default class SdkConfig {
8189
}
8290

8391
public static unset() {
84-
SdkConfig.setInstance({});
92+
SdkConfig.setInstance(<IConfigOptions>{}); // safe to cast - defaults will be applied
8593
}
8694

87-
public static add(cfg: IConfigOptions) {
95+
public static add(cfg: Partial<IConfigOptions>) {
8896
const liveConfig = SdkConfig.get();
8997
const newConfig = Object.assign({}, liveConfig, cfg);
9098
SdkConfig.put(newConfig);

src/components/structures/HomePage.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,8 @@ const HomePage: React.FC<IProps> = ({ justRegistered = false }) => {
103103
if (justRegistered) {
104104
introSection = <UserWelcomeTop />;
105105
} else {
106-
const brandingConfig = config.branding;
107-
let logoUrl = "themes/element/img/logos/element-logo.svg";
108-
if (brandingConfig && brandingConfig.authHeaderLogoUrl) {
109-
logoUrl = brandingConfig.authHeaderLogoUrl;
110-
}
106+
const brandingConfig = SdkConfig.getObject("branding");
107+
const logoUrl = brandingConfig?.get("auth_header_logo_url") ?? "themes/element/img/logos/element-logo.svg";
111108

112109
introSection = <React.Fragment>
113110
<img src={logoUrl} alt={config.brand} />

src/components/structures/HostSignupAction.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export default class HostSignupAction extends React.PureComponent<IProps, IState
3939
};
4040

4141
public render(): React.ReactNode {
42-
const hostSignupConfig = SdkConfig.get().hostSignup;
43-
if (!hostSignupConfig?.brand) {
42+
const hostSignupConfig = SdkConfig.getObject("host_signup");
43+
if (!hostSignupConfig?.get("brand")) {
4444
return null;
4545
}
4646

@@ -51,7 +51,7 @@ export default class HostSignupAction extends React.PureComponent<IProps, IState
5151
label={_t(
5252
"Upgrade to %(hostSignupBrand)s",
5353
{
54-
hostSignupBrand: hostSignupConfig.brand,
54+
hostSignupBrand: hostSignupConfig.get("brand"),
5555
},
5656
)}
5757
onClick={this.openDialog}

0 commit comments

Comments
 (0)