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

Commit

Permalink
Fix all obvious instances of SdkConfig case conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
turt2live committed Mar 16, 2022
1 parent da217d2 commit 4b320d0
Show file tree
Hide file tree
Showing 48 changed files with 240 additions and 155 deletions.
9 changes: 5 additions & 4 deletions src/@types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ export type RecursivePartial<T> = {
T[P];
};

// Condensed but also expanded version of https://stackoverflow.com/a/60206860
export type KeysOfStrictType<Input, SearchType> = {
[P in keyof Input]: Input[P] extends SearchType
? (SearchType extends Input[P] ? P : never)
// Inspired by https://stackoverflow.com/a/60206860
export type KeysWithObjectShape<Input> = {
[P in keyof Input]: Input[P] extends object
// Arrays are counted as objects - exclude them
? (Input[P] extends Array<unknown> ? never : P)
: never;
}[keyof Input];
38 changes: 29 additions & 9 deletions src/Analytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ limitations under the License.

import React from 'react';
import { logger } from "matrix-js-sdk/src/logger";
import { Optional } from "matrix-events-sdk";

import { getCurrentLanguage, _t, _td, IVariables } from './languageHandler';
import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig';
import Modal from './Modal';
import * as sdk from './index';
import { SnakedObject } from "./utils/SnakedObject";
import { IConfigOptions } from "./IConfigOptions";

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

public canEnable() {
const config = SdkConfig.get();
return navigator.doNotTrack !== "1" && config && config.piwik && config.piwik.url && config.piwik.siteId;
const piwikConfig = SdkConfig.get("piwik");
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
if (typeof piwikConfig === 'object') {
piwik = new SnakedObject(piwikConfig);
}
return navigator.doNotTrack !== "1" && piwik?.get("site_id");
}

/**
Expand All @@ -204,12 +211,16 @@ export class Analytics {
public async enable() {
if (!this.disabled) return;
if (!this.canEnable()) return;
const config = SdkConfig.get();
const piwikConfig = SdkConfig.get("piwik");
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
if (typeof piwikConfig === 'object') {
piwik = new SnakedObject(piwikConfig);
}

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

const config = SdkConfig.get();
if (!config.piwik) return;
const piwikConfig = SdkConfig.get("piwik");
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
if (typeof piwikConfig === 'object') {
piwik = new SnakedObject(piwikConfig);
}
if (!piwik) return;

const whitelistedHSUrls = config.piwik.whitelistedHSUrls || [];
const whitelistedHSUrls = piwik.get("whitelisted_hs_urls", "whitelistedHSUrls") || [];

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

// FIXME: Using an import will result in test failures
const cookiePolicyUrl = SdkConfig.get().piwik?.policyUrl;
const piwikConfig = SdkConfig.get("piwik");
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
if (typeof piwikConfig === 'object') {
piwik = new SnakedObject(piwikConfig);
}
const cookiePolicyUrl = piwik?.get("policy_url");
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
const cookiePolicyLink = _t(
"Our complete cookie policy can be found <CookiePolicyLink>here</CookiePolicyLink>.",
Expand Down
3 changes: 2 additions & 1 deletion src/BasePlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { hideToast as hideUpdateToast } from "./toasts/UpdateToast";
import { MatrixClientPeg } from "./MatrixClientPeg";
import { idbLoad, idbSave, idbDelete } from "./utils/StorageManager";
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
import { IConfigOptions } from "./IConfigOptions";

export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
Expand Down Expand Up @@ -62,7 +63,7 @@ export default abstract class BasePlatform {
this.startUpdateCheck = this.startUpdateCheck.bind(this);
}

abstract getConfig(): Promise<{}>;
abstract getConfig(): Promise<IConfigOptions>;

abstract getDefaultDeviceDisplayName(): string;

Expand Down
2 changes: 1 addition & 1 deletion src/CallHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export default class CallHandler extends EventEmitter {
}

private shouldObeyAssertedfIdentity(): boolean {
return SdkConfig.get()['voip']?.obeyAssertedIdentity;
return SdkConfig.getObject("voip")?.get("obey_asserted_identity");
}

public getSupportsPstnProtocol(): boolean {
Expand Down
51 changes: 46 additions & 5 deletions src/IConfigOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.

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

import { ValidatedServerConfig } from "./utils/AutoDiscoveryUtils";

// Convention decision: All config options are lower_snake_case
// We use an isolated file for the interface so we can mess around with the eslint options.

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

// see element-web config.md for non-developer docs
export interface IConfigOptions {
valToProveLintRuleWorks?: never;

// dev note: while true that this is arbitrary JSON, it's valuable to enforce that all
// config options are documented for "find all usages" sort of searching.
// [key: string]: any;
Expand All @@ -42,14 +42,20 @@ export interface IConfigOptions {

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

// This is intended to be overridden by app startup and not specified by the user
// This is also why it's allowed to have an interface that isn't snake_case
validated_server_config?: ValidatedServerConfig;

fallback_hs_url?: string;

disable_custom_urls?: boolean;
disable_guests?: boolean;
disable_login_language_selector?: boolean;
disable_3pid_login?: boolean;

brand?: string;
brand: string;
branding?: {
welcome_background_url?: string;
welcome_background_url?: string | string[]; // chosen at random if array
auth_header_logo_url?: string;
auth_footer_links?: {text: string, url: string}[];
};
Expand Down Expand Up @@ -99,10 +105,14 @@ export interface IConfigOptions {
environment?: string; // "production", etc
};

widget_build_url?: string; // url called to replace jitsi/call widget creation
audio_stream_url?: string;
jitsi?: {
preferred_domain: string;
};
jitsi_widget?: {
skip_built_in_welcome_screen?: boolean;
};
voip?: {
obey_asserted_identity?: boolean; // MSC3086
};
Expand All @@ -128,7 +138,7 @@ export interface IConfigOptions {
// piwik (matomo) is deprecated in favour of posthog
piwik?: false | {
url: string; // piwik instance
site_id: string | number; // TODO: @@TR Typed correctly?
site_id: string;
policy_url: string; // cookie policy
whitelisted_hs_urls: string[];
};
Expand All @@ -137,6 +147,37 @@ export interface IConfigOptions {
api_host: string; // hostname
};
analytics_owner?: string; // defaults to `brand`

// Server hosting upsell options
hosting_signup_link?: string; // slightly different from `host_signup`
host_signup?: {
brand?: string; // acts as the enabled flag too (truthy == show)

// Required-ness denotes when `brand` is truthy
cookie_policy_url: string;
privacy_policy_url: string;
terms_of_service_url: string;
url: string;
domains?: string[];
};

enable_presence_by_hs_url?: Record<string, boolean>; // <HomeserverName, Enabled>

terms_and_conditions_links?: { url: string, text: string }[];

latex_maths_delims?: {
inline?: {
left?: string;
right?: string;
};
display?: {
left?: string;
right?: string;
};
};

sync_timeline_limit?: number;
dangerously_allow_unsafe_and_insecure_passwords?: boolean; // developer option
}

export interface ISsoRedirectOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/KeyBindingsDefaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ const callBindings = (): KeyBinding[] => {
};

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

return getBindingsByCategory(CategoryName.LABS);
};
Expand Down
2 changes: 1 addition & 1 deletion src/Livestream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import SdkConfig from "./SdkConfig";
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";

export function getConfigLivestreamUrl() {
return SdkConfig.get()["audioStreamUrl"];
return SdkConfig.get("audio_stream_url");
}

// Dummy rtmp URL used to signal that we want a special audio-only stream
Expand Down
6 changes: 3 additions & 3 deletions src/PosthogAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ export class PosthogAnalytics {
}

constructor(private readonly posthog: PostHog) {
const posthogConfig = SdkConfig.get()["posthog"];
const posthogConfig = SdkConfig.getObject("posthog");
if (posthogConfig) {
this.posthog.init(posthogConfig.projectApiKey, {
api_host: posthogConfig.apiHost,
this.posthog.init(posthogConfig.get("project_api_key"), {
api_host: posthogConfig.get("api_host"),
autocapture: false,
mask_all_text: true,
mask_all_element_attributes: true,
Expand Down
20 changes: 14 additions & 6 deletions src/SdkConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Optional } from "matrix-events-sdk";

import { SnakedObject } from "./utils/SnakedObject";
import { IConfigOptions, ISsoRedirectOptions } from "./IConfigOptions";
import { KeysOfStrictType } from "./@types/common";
import { KeysWithObjectShape } from "./@types/common";

// see element-web config.md for docs, or the IConfigOptions interface for dev docs
export const DEFAULTS: Partial<IConfigOptions> = {
Expand All @@ -30,7 +30,12 @@ export const DEFAULTS: Partial<IConfigOptions> = {
jitsi: {
preferred_domain: "meet.element.io",
},
desktop_builds: {

// @ts-ignore - we deliberately use the camelCase version here so we trigger
// the fallback behaviour. If we used the snake_case version then we'd break
// everyone's config which has the camelCase property because our default would
// be preferred over their config.
desktopBuilds: {
available: true,
logo: require("../res/img/element-desktop-logo.svg").default,
url: "https://element.io/get-started",
Expand All @@ -54,11 +59,14 @@ export default class SdkConfig {
public static get<K extends keyof IConfigOptions = never>(
key?: K, altCaseName?: string,
): IConfigOptions | IConfigOptions[K] {
if (key === undefined) return SdkConfig.instance || {};
if (key === undefined) {
// safe to cast as a fallback - we want to break the runtime contract in this case
return SdkConfig.instance || <IConfigOptions>{};
}
return SdkConfig.fallback.get(key, altCaseName);
}

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

public static unset() {
SdkConfig.setInstance({});
SdkConfig.setInstance(<IConfigOptions>{}); // safe to cast - defaults will be applied
}

public static add(cfg: IConfigOptions) {
public static add(cfg: Partial<IConfigOptions>) {
const liveConfig = SdkConfig.get();
const newConfig = Object.assign({}, liveConfig, cfg);
SdkConfig.put(newConfig);
Expand Down
7 changes: 2 additions & 5 deletions src/components/structures/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,8 @@ const HomePage: React.FC<IProps> = ({ justRegistered = false }) => {
if (justRegistered) {
introSection = <UserWelcomeTop />;
} else {
const brandingConfig = config.branding;
let logoUrl = "themes/element/img/logos/element-logo.svg";
if (brandingConfig && brandingConfig.authHeaderLogoUrl) {
logoUrl = brandingConfig.authHeaderLogoUrl;
}
const brandingConfig = SdkConfig.getObject("branding");
const logoUrl = brandingConfig?.get("auth_header_logo_url") ?? "themes/element/img/logos/element-logo.svg";

introSection = <React.Fragment>
<img src={logoUrl} alt={config.brand} />
Expand Down
6 changes: 3 additions & 3 deletions src/components/structures/HostSignupAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export default class HostSignupAction extends React.PureComponent<IProps, IState
};

public render(): React.ReactNode {
const hostSignupConfig = SdkConfig.get().hostSignup;
if (!hostSignupConfig?.brand) {
const hostSignupConfig = SdkConfig.getObject("host_signup");
if (!hostSignupConfig?.get("brand")) {
return null;
}

Expand All @@ -51,7 +51,7 @@ export default class HostSignupAction extends React.PureComponent<IProps, IState
label={_t(
"Upgrade to %(hostSignupBrand)s",
{
hostSignupBrand: hostSignupConfig.brand,
hostSignupBrand: hostSignupConfig.get("brand"),
},
)}
onClick={this.openDialog}
Expand Down
8 changes: 2 additions & 6 deletions src/components/structures/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import RightPanelStore from '../../stores/right-panel/RightPanelStore';
import { TimelineRenderingType } from "../../contexts/RoomContext";
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
import { SwitchSpacePayload } from "../../dispatcher/payloads/SwitchSpacePayload";
import { IConfigOptions } from "../../IConfigOptions";

// We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity.
Expand Down Expand Up @@ -102,12 +103,7 @@ interface IProps {
roomOobData?: IOOBData;
currentRoomId: string;
collapseLhs: boolean;
config: {
piwik: {
policyUrl: string;
};
[key: string]: any;
};
config: IConfigOptions;
currentUserId?: string;
currentGroupId?: string;
currentGroupIsNew?: boolean;
Expand Down
Loading

0 comments on commit 4b320d0

Please sign in to comment.