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

Apply strictNullChecks to src/components/views/settings #10724

Merged
merged 26 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3365fba
VoiceUserSettingsTab
Apr 27, 2023
befd834
strictNullChecks in everything except notifications
Apr 27, 2023
a55049a
strictNullChecks in Notifications
Apr 27, 2023
065aa86
test JoinRuleSettings room upgrade
Apr 28, 2023
446b14d
test SecureBackupPanel changes
Apr 28, 2023
91a56ab
broken Notifications tests
Apr 28, 2023
3dc0766
try killing all modals before test
Apr 30, 2023
90a4a43
Merge branch 'develop' into kerry/25230/flaky-securityroomsettingstab…
Apr 30, 2023
c12abc4
Merge branch 'develop' into kerry/25217/snc-views-settings
Apr 30, 2023
c5df9ba
remove annoying test
Apr 30, 2023
7806450
lint
Apr 30, 2023
872ebcc
fix code smell
May 1, 2023
4099965
Revert "remove annoying test"
May 1, 2023
09ca8ab
Merge branch 'kerry/25230/flaky-securityroomsettingstab-test' into ke…
May 1, 2023
4af9364
add test for adding and removing keywords from notifications
May 1, 2023
717b0f2
strict
May 2, 2023
8f1136a
Merge branch 'develop' into kerry/25217/snc-views-settings
May 2, 2023
0fb353c
Merge branch 'develop' into kerry/25217/snc-views-settings
May 3, 2023
e3b67b7
Merge branch 'develop' into kerry/25217/snc-views-settings
May 4, 2023
e64c996
Merge branch 'develop' into kerry/25217/snc-views-settings
May 4, 2023
299f71c
tests
May 4, 2023
6442f61
test setting keywords when disabled
May 5, 2023
7aaca9b
securebackuppanel test reset
May 5, 2023
efc33d8
Merge branch 'develop' into kerry/25217/snc-views-settings
May 5, 2023
4bef48d
strict for SecureBackupPanel-test
May 5, 2023
d729674
test setting input device in voice settings
May 5, 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
7 changes: 6 additions & 1 deletion src/components/views/elements/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ export const Tag: React.FC<IProps> = ({ icon, label, onDeleteClick, disabled = f
{icon?.()}
{label}
{onDeleteClick && (
<AccessibleButton className="mx_Tag_delete" onClick={onDeleteClick} disabled={disabled}>
<AccessibleButton
aria-label="Remove"
className="mx_Tag_delete"
onClick={onDeleteClick}
disabled={disabled}
>
<CancelRounded />
</AccessibleButton>
)}
Expand Down
16 changes: 11 additions & 5 deletions src/components/views/settings/JoinRuleSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { Action } from "../../../dispatcher/actions";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions";

interface IProps {
export interface JoinRuleSettingsProps {
room: Room;
promptUpgrade?: boolean;
closeSettingsFn(): void;
Expand All @@ -45,7 +45,7 @@ interface IProps {
aliasWarning?: ReactNode;
}

const JoinRuleSettings: React.FC<IProps> = ({
const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
room,
promptUpgrade,
aliasWarning,
Expand Down Expand Up @@ -287,7 +287,10 @@ const JoinRuleSettings: React.FC<IProps> = ({
fn(_t("Upgrading room"), 0, total);
} else if (!progress.roomSynced) {
fn(_t("Loading new room"), 1, total);
} else if (progress.inviteUsersProgress < progress.inviteUsersTotal) {
} else if (
progress.inviteUsersProgress !== undefined &&
progress.inviteUsersProgress < progress.inviteUsersTotal
) {
fn(
_t("Sending invites... (%(progress)s out of %(count)s)", {
progress: progress.inviteUsersProgress,
Expand All @@ -296,13 +299,16 @@ const JoinRuleSettings: React.FC<IProps> = ({
2 + progress.inviteUsersProgress,
total,
);
} else if (progress.updateSpacesProgress < progress.updateSpacesTotal) {
} else if (
progress.updateSpacesProgress !== undefined &&
progress.updateSpacesProgress < progress.updateSpacesTotal
) {
fn(
_t("Updating spaces... (%(progress)s out of %(count)s)", {
progress: progress.updateSpacesProgress,
count: progress.updateSpacesTotal,
}),
2 + progress.inviteUsersProgress + progress.updateSpacesProgress,
2 + (progress.inviteUsersProgress ?? 0) + progress.updateSpacesProgress,
total,
);
}
Expand Down
56 changes: 35 additions & 21 deletions src/components/views/settings/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ const maximumVectorState = (
if (!definition.syncedRuleIds?.length) {
return undefined;
}
const vectorState = definition.syncedRuleIds.reduce<VectorState | undefined>((maxVectorState, ruleId) => {
const vectorState = definition.syncedRuleIds.reduce<VectorState>((maxVectorState, ruleId) => {
// already set to maximum
if (maxVectorState === VectorState.Loud) {
return maxVectorState;
Expand All @@ -177,12 +177,15 @@ const maximumVectorState = (
const syncedRuleVectorState = definition.ruleToVectorState(syncedRule);
// if syncedRule is 'louder' than current maximum
// set maximum to louder vectorState
if (OrderedVectorStates.indexOf(syncedRuleVectorState) > OrderedVectorStates.indexOf(maxVectorState)) {
if (
syncedRuleVectorState &&
OrderedVectorStates.indexOf(syncedRuleVectorState) > OrderedVectorStates.indexOf(maxVectorState)
) {
return syncedRuleVectorState;
}
}
return maxVectorState;
}, definition.ruleToVectorState(rule));
}, definition.ruleToVectorState(rule)!);

return vectorState;
};
Expand Down Expand Up @@ -281,7 +284,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
}

private async refreshRules(): Promise<Partial<IState>> {
const ruleSets = await MatrixClientPeg.get().getPushRules();
const ruleSets = await MatrixClientPeg.get().getPushRules()!;
const categories: Record<string, RuleClass> = {
[RuleId.Master]: RuleClass.Master,

Expand Down Expand Up @@ -316,7 +319,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
// noinspection JSUnfilteredForInLoop
const kind = k as PushRuleKind;

for (const r of ruleSets.global[kind]) {
for (const r of ruleSets.global[kind]!) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add an explanation why we are sure that ruleSets.global[kind] is defined

const rule: IAnnotatedPushRule = Object.assign(r, { kind });
const category = categories[rule.rule_id] ?? RuleClass.Other;

Expand Down Expand Up @@ -344,7 +347,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
preparedNewState.vectorPushRules[category] = [];
for (const rule of defaultRules[category]) {
const definition: VectorPushRuleDefinition = VectorPushRulesDefinitions[rule.rule_id];
const vectorState = definition.ruleToVectorState(rule);
const vectorState = definition.ruleToVectorState(rule)!;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add an explanation why we are sure that vectorState is defined

preparedNewState.vectorPushRules[category]!.push({
ruleId: rule.rule_id,
rule,
Expand Down Expand Up @@ -441,8 +444,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
} else {
const pusher = this.state.pushers?.find((p) => p.kind === "email" && p.pushkey === email);
if (pusher) {
pusher.kind = null; // flag for delete
await MatrixClientPeg.get().setPusher(pusher);
await MatrixClientPeg.get().removePusher(pusher.pushkey, pusher.app_id);
}
}

Expand Down Expand Up @@ -539,34 +541,37 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
}
};

private async setKeywords(keywords: string[], originalRules: IAnnotatedPushRule[]): Promise<void> {
private async setKeywords(
unsafeKeywords: (string | undefined)[],
originalRules: IAnnotatedPushRule[],
): Promise<void> {
try {
// De-duplicate and remove empties
keywords = filterBoolean(Array.from(new Set(keywords)));
const oldKeywords = filterBoolean(Array.from(new Set(originalRules.map((r) => r.pattern))));
const keywords = filterBoolean<string>(Array.from(new Set(unsafeKeywords)));
const oldKeywords = filterBoolean<string>(Array.from(new Set(originalRules.map((r) => r.pattern))));

// Note: Technically because of the UI interaction (at the time of writing), the diff
// will only ever be +/-1 so we don't really have to worry about efficiently handling
// tons of keyword changes.

const diff = arrayDiff(oldKeywords, keywords);
const diff = arrayDiff<string>(oldKeywords, keywords);

for (const word of diff.removed) {
for (const rule of originalRules.filter((r) => r.pattern === word)) {
await MatrixClientPeg.get().deletePushRule("global", rule.kind, rule.rule_id);
}
}

let ruleVectorState = this.state.vectorKeywordRuleInfo?.vectorState;
let ruleVectorState = this.state.vectorKeywordRuleInfo!.vectorState;
if (ruleVectorState === VectorState.Off) {
// When the current global keywords rule is OFF, we need to look at
// the flavor of existing rules to apply the same actions
// when creating the new rule.
if (originalRules.length) {
ruleVectorState = PushRuleVectorState.contentRuleVectorStateKind(originalRules[0]) ?? undefined;
} else {
ruleVectorState = VectorState.On; // default
}
const existingRuleVectorState = originalRules.length
? PushRuleVectorState.contentRuleVectorStateKind(originalRules[0])
: undefined;
// set to same state as existing rule, or default to On
ruleVectorState = existingRuleVectorState ?? VectorState.On; //default
}
const kind = PushRuleKind.ContentSpecific;
for (const word of diff.added) {
Expand All @@ -588,6 +593,10 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
}

private onKeywordAdd = (keyword: string): void => {
// should not encounter this
if (!this.state.vectorKeywordRuleInfo) {
throw new Error("Notification data is incomplete.");
}
const originalRules = objectClone(this.state.vectorKeywordRuleInfo.rules);

// We add the keyword immediately as a sort of local echo effect
Expand All @@ -606,14 +615,18 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
},
async (): Promise<void> => {
await this.setKeywords(
this.state.vectorKeywordRuleInfo.rules.map((r) => r.pattern),
this.state.vectorKeywordRuleInfo!.rules.map((r) => r.pattern),
originalRules,
);
},
);
};

private onKeywordRemove = (keyword: string): void => {
// should not encounter this
if (!this.state.vectorKeywordRuleInfo) {
throw new Error("Notification data is incomplete.");
}
const originalRules = objectClone(this.state.vectorKeywordRuleInfo.rules);

// We remove the keyword immediately as a sort of local echo effect
Expand All @@ -627,7 +640,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
},
async (): Promise<void> => {
await this.setKeywords(
this.state.vectorKeywordRuleInfo.rules.map((r) => r.pattern),
this.state.vectorKeywordRuleInfo!.rules.map((r) => r.pattern),
originalRules,
);
},
Expand Down Expand Up @@ -749,9 +762,10 @@ export default class Notifications extends React.PureComponent<IProps, IState> {

let keywordComposer: JSX.Element | undefined;
if (category === RuleClass.VectorMentions) {
const tags = filterBoolean<string>(this.state.vectorKeywordRuleInfo?.rules.map((r) => r.pattern) || []);
keywordComposer = (
<TagComposer
tags={this.state.vectorKeywordRuleInfo?.rules.map((r) => r.pattern)}
tags={tags}
onAdd={this.onKeywordAdd}
onRemove={this.onKeywordRemove}
disabled={this.state.phase === Phase.Persisting}
Expand Down
4 changes: 3 additions & 1 deletion src/components/views/settings/ProfileSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ export default class ProfileSettings extends React.Component<{}, IState> {

private removeAvatar = (): void => {
// clear file upload field so same file can be selected
this.avatarUpload.current.value = "";
if (this.avatarUpload.current) {
this.avatarUpload.current.value = "";
}
this.setState({
avatarUrl: undefined,
avatarFile: null,
Expand Down
16 changes: 8 additions & 8 deletions src/components/views/settings/SecureBackupPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
private async checkKeyBackupStatus(): Promise<void> {
this.getUpdatedDiagnostics();
try {
const { backupInfo, trustInfo } = await MatrixClientPeg.get().checkKeyBackup();
const keyBackupResult = await MatrixClientPeg.get().checkKeyBackup();
this.setState({
loading: false,
error: null,
backupInfo,
backupSigStatus: trustInfo,
backupInfo: keyBackupResult?.backupInfo ?? null,
backupSigStatus: keyBackupResult?.trustInfo ?? null,
});
} catch (e) {
logger.log("Unable to fetch check backup status", e);
Expand All @@ -123,7 +123,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
this.getUpdatedDiagnostics();
try {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo!);
const backupSigStatus = backupInfo ? await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo) : null;
if (this.unmounted) return;
this.setState({
loading: false,
Expand Down Expand Up @@ -192,7 +192,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (!proceed) return;
this.setState({ loading: true });
MatrixClientPeg.get()
.deleteKeyBackupVersion(this.state.backupInfo.version)
.deleteKeyBackupVersion(this.state.backupInfo!.version!)
.then(() => {
this.loadBackupStatus();
});
Expand Down Expand Up @@ -285,7 +285,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
);
}

let backupSigStatuses: React.ReactNode = backupSigStatus?.sigs.map((sig, i) => {
let backupSigStatuses: React.ReactNode | undefined = backupSigStatus?.sigs?.map((sig, i) => {
const deviceName = sig.device ? sig.device.getDisplayName() || sig.device.deviceId : null;
const validity = (sub: string): JSX.Element => (
<span className={sig.valid ? "mx_SecureBackupPanel_sigValid" : "mx_SecureBackupPanel_sigInvalid"}>
Expand Down Expand Up @@ -354,7 +354,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
{},
{ validity, verify, device },
);
} else if (sig.valid && !sig.deviceTrust.isVerified()) {
} else if (sig.valid && !sig.deviceTrust?.isVerified()) {
sigStatus = _t(
"Backup has a <validity>valid</validity> signature from " +
"<verify>unverified</verify> session <device></device>",
Expand All @@ -368,7 +368,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
{},
{ validity, verify, device },
);
} else if (!sig.valid && !sig.deviceTrust.isVerified()) {
} else if (!sig.valid && !sig.deviceTrust?.isVerified()) {
sigStatus = _t(
"Backup has an <validity>invalid</validity> signature from " +
"<verify>unverified</verify> session <device></device>",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {

private setDevice = (deviceId: string, kind: MediaDeviceKindEnum): void => {
MediaDeviceHandler.instance.setDevice(deviceId, kind);
this.setState<null>({ [kind]: deviceId });
this.setState<any>({ [kind]: deviceId });
};

private changeWebRtcMethod = (p2p: boolean): void => {
Expand Down
Loading