diff --git a/.oxlint/rules/noExtraneousDependencies.mjs b/.oxlint/rules/noExtraneousDependencies.mjs
index 3bae0dbc24..4132bf5a4a 100644
--- a/.oxlint/rules/noExtraneousDependencies.mjs
+++ b/.oxlint/rules/noExtraneousDependencies.mjs
@@ -79,7 +79,7 @@ function getPackageNameFromSource(source) {
return `${scope}/${name}`;
}
const [name] = source.split('/', 1);
- return `${name}`;
+ return name;
}
/**
diff --git a/.oxlintrc.json b/.oxlintrc.json
index 8d08ff7216..89285b2ac4 100644
--- a/.oxlintrc.json
+++ b/.oxlintrc.json
@@ -422,15 +422,15 @@
// [correctness] Disallow void DOM elements (e.g.
,
) from receiving children
"react/void-dom-elements-no-children": "error",
// [correctness] (β
recommended) (π‘ suggestion) (π needs type info)
- "typescript/await-thenable": "off", // FIXME (errors: 126)
+ "typescript/await-thenable": "error",
// [correctness] (β
recommended) (π‘ suggestion) (π needs type info) Disallow using the delete operator on array values
"typescript/no-array-delete": "error",
// [correctness] (β
recommended) (π needs type info) Require .toString() and .toLocaleString() to only be called on objects which provide useful information when stringified
- "typescript/no-base-to-string": "off", // FIXME [reason: FILLMEIN] (errors: 14)
+ "typescript/no-base-to-string": "error",
// [correctness] (β
recommended) Disallow duplicate enum member values
"typescript/no-duplicate-enum-values": "error",
// [correctness] (β
recommended) (π οΈ autofix) (π needs type info) Disallow duplicate constituents of union or intersection types
- "typescript/no-duplicate-type-constituents": "off", // FIXME [reason: FILLMEIN] (errors: 2)
+ "typescript/no-duplicate-type-constituents": "error",
// [correctness] (β
recommended) (π§ planned autofix) Disallow extra non-null assertions
"typescript/no-extra-non-null-assertion": "error",
// [correctness] (β
recommended) (π‘ suggestion) (π needs type info) Require Promise-like statements to be handled appropriately
@@ -440,15 +440,15 @@
// [correctness] (β
recommended) (π needs type info) Disallow the use of eval()-like functions
"typescript/no-implied-eval": "error",
// [correctness] (β
recommended) (π οΈ autofix) (π‘ suggestion) (π needs type info) Disallow the void operator except when used to discard a value
- "typescript/no-meaningless-void-operator": "off", // DECIDEME (errors: 4)
+ "typescript/no-meaningless-void-operator": "error",
// [correctness] (β
recommended) Enforce valid definition of new and constructor
"typescript/no-misused-new": "error",
// [correctness] (β
recommended) (π‘ suggestion) (π needs type info) Disallow using the spread operator when it might cause unexpected behavior
- "typescript/no-misused-spread": "off", // DECIDEME (errors: 12)
+ "typescript/no-misused-spread": "error",
// [correctness] (β
recommended) (π‘ suggestion) Disallow non-null assertions after an optional chain expression
"typescript/no-non-null-asserted-optional-chain": "error",
// [correctness] (β
recommended) (π needs type info) Disallow members of unions and intersections that do nothing or override type information
- "typescript/no-redundant-type-constituents": "off", // DECIDEME (errors: 10)
+ "typescript/no-redundant-type-constituents": "error",
// [correctness] (β
recommended) Disallow aliasing this
"typescript/no-this-alias": "error",
// [correctness] (β
recommended) (π‘ suggestion) Disallow unnecessary assignment of constructor property parameter
@@ -466,9 +466,14 @@
// [correctness] (β
recommended) (π οΈ autofix) Require using namespace keyword over module keyword to declare custom TypeScript modules
"typescript/prefer-namespace-keyword": "error",
// [correctness] (β
recommended) (π needs type info) Require Array#sort and Array#toSorted calls to always provide a compareFunction
- "typescript/require-array-sort-compare": "off", // DECIDEME (errors: 2)
+ "typescript/require-array-sort-compare": "error",
// [correctness] (β
recommended) (π needs type info) Enforce template literal expressions to be of string type
- "typescript/restrict-template-expressions": "off", // DECIDEME (errors: 76)
+ "typescript/restrict-template-expressions": [
+ "error",
+ {
+ "allowNever": true // FIXME
+ }
+ ],
// [correctness] (β
recommended) Disallow certain triple slash directives in favor of ES6-style import declarations
"typescript/triple-slash-reference": "error",
// [correctness] (β
recommended) (π needs type info) Enforce unbound methods are called with their expected scope
@@ -547,31 +552,31 @@
// [nursery] Require return statements to either always or never specify values
"typescript/consistent-return": "off", // [reason: Doesn't seem like it would catch many bugs]
// [nursery] Enforce consistent usage of type exports
- "typescript/consistent-type-exports": "off", // DECIDEME (errors: 2)
+ "typescript/consistent-type-exports": "error",
// [nursery] Enforce dot notation whenever possible
- "typescript/dot-notation": "off", // DECIDEME (errors: 2)
+ "typescript/dot-notation": "error",
// [nursery] Disallow conditionals where the type is always truthy or always falsy
"typescript/no-unnecessary-condition": "off", // FIXME (errors: 1560) [reason: This is a big project, and we should make sure we're not removing conditional checks that are dealing with bad data (improved types)]
// [nursery] Disallow unnecessary namespace qualifiers
- "typescript/no-unnecessary-qualifier": "off", // DECIDEME (errors: 5)
+ "typescript/no-unnecessary-qualifier": "error",
// [nursery] Disallow conversion idioms when they do not change the type or value of the expression
- "typescript/no-unnecessary-type-conversion": "off", // DECIDEME (errors: 127)
+ "typescript/no-unnecessary-type-conversion": "error",
// [nursery] Disallow type parameters that aren't used multiple times
"typescript/no-unnecessary-type-parameters": "off", // TODO: Has issues with disable comments
// [nursery] Disallow default values that will never be used
- "typescript/no-useless-default-assignment": "off", // DECIDEME (errors: 12)
+ "typescript/no-useless-default-assignment": "error",
// [nursery] Enforce the use of Array.prototype.find() over Array.prototype.filter() followed by [0] when looking for a single result
"typescript/prefer-find": "error",
// [nursery] β οΈ π (π‘ suggestion) Enforce using concise optional chain expressions instead of chained logical ands, negated logical ors, or empty objects
"typescript/prefer-optional-chain": "off", // DECIDEME (errors: 251)
// [nursery] Require private members to be marked as readonly if they're never modified outside of the constructor
- "typescript/prefer-readonly": "off", // DECIDEME (errors: 138)
+ "typescript/prefer-readonly": "error",
// [nursery] Require function parameters to be typed as readonly to prevent accidental mutation of inputs
"typescript/prefer-readonly-parameter-types": "off", // DECIDEME (errors: 10694)
// [nursery] Enforce RegExp#exec over String#match if no global flag is provided
"typescript/prefer-regexp-exec": "off", // DECIDEME (errors: 19)
// [nursery] (β
recommended) Enforce using String#startsWith and String#endsWith over other equivalent methods of checking substrings
- "typescript/prefer-string-starts-ends-with": "off", // DECIDEME (errors: 5)
+ "typescript/prefer-string-starts-ends-with": "error",
// [nursery] Enforce ES5 or ES6 class for returning value in render function
"react/require-render-return": "error",
// [nursery] (π needs type info) Disallow passing a value-returning function in a position accepting a void function
@@ -686,43 +691,43 @@
// [pedantic] (π οΈ autofix) (π‘ suggestion) Require expressions of type void to appear in statement position
"typescript/no-confusing-void-expression": "off", // DECIDEME (errors: 621)
// [pedantic] Disallow using code marked as @deprecated
- "typescript/no-deprecated": "off", // DECIDEME (errors: 187)
+ "typescript/no-deprecated": "off", // FIXME (errors: 187)
// [pedantic] Disallow Promises in places not designed to handle them
"typescript/no-misused-promises": ["error", { "checksVoidReturn": false }], // [reason: FILLMEIN] (errors: 352)
// [pedantic] Disallow enums from having both number and string members
"typescript/no-mixed-enums": "error",
// [pedantic] Disallow calling a function with a value with type any
- "typescript/no-unsafe-argument": "off", // DECIDEME (errors: 531)
+ "typescript/no-unsafe-argument": "off", // LATER (errors: 531)
// [pedantic] Disallow assigning a value with type any to variables and properties
- "typescript/no-unsafe-assignment": "off", // DECIDEME (errors: 817)
+ "typescript/no-unsafe-assignment": "off", // LATER (errors: 817)
// [pedantic] Disallow calling a value with type any
- "typescript/no-unsafe-call": "off", // DECIDEME (errors: 364)
+ "typescript/no-unsafe-call": "off", // LATER (errors: 364)
// [pedantic] Disallow using the unsafe built-in Function type
- "typescript/no-unsafe-function-type": "off", // DECIDEME (errors: 1)
+ "typescript/no-unsafe-function-type": "error",
// [pedantic] Disallow member access on a value with type any
- "typescript/no-unsafe-member-access": "off", // DECIDEME (errors: 1087)
+ "typescript/no-unsafe-member-access": "off", // LATER (errors: 1087)
// [pedantic] Disallow returning a value with type any from a function
- "typescript/no-unsafe-return": "off", // DECIDEME (errors: 123)
+ "typescript/no-unsafe-return": "off", // LATER (errors: 123)
// [pedantic] Disallow throwing non-Error values as exceptions
- "typescript/only-throw-error": "off", // DECIDEME (errors: 6)
+ "typescript/only-throw-error": "error",
// [pedantic] (π‘ suggestion) Require each enum member value to be explicitly initialized
"typescript/prefer-enum-initializers": "off", // DECIDEME (errors: 180)
// [pedantic] (π οΈ autofix) Enforce includes method over indexOf method
- "typescript/prefer-includes": "off", // DECIDEME (errors: 3)
+ "typescript/prefer-includes": "error",
// [pedantic] (π οΈ autofix) Enforce using the nullish coalescing operator instead of logical assignments or chaining
"typescript/prefer-nullish-coalescing": "off", // DECIDEME (errors: 1199)
// [pedantic] Require using Error objects as Promise rejection reasons
"typescript/prefer-promise-reject-errors": "off", // DECIDEME (errors: 20)
// [pedantic] (π οΈ autofix) Enforce using @ts-expect-error over @ts-ignore
- "typescript/prefer-ts-expect-error": "off", // DECIDEME (errors: 6)
+ "typescript/prefer-ts-expect-error": "error",
// [pedantic] Enforce that get() types should be assignable to their equivalent set() type
"typescript/related-getter-setter-pairs": "error",
// [pedantic] (π§ planned autofix) Disallow async functions which do not return promises and have no await expression
"typescript/require-await": "off", // DECIDEME (errors: 373)
// [pedantic] Require both operands of addition to be the same type and be bigint, number, or string
- "typescript/restrict-plus-operands": "off", // DECIDEME (errors: 3)
+ "typescript/restrict-plus-operands": "error",
// [pedantic] (π οΈ autofix) (π‘ suggestion) Enforce consistent awaiting of returned promises
- "typescript/return-await": "off", // DECIDEME (errors: 9)
+ "typescript/return-await": "error",
// [pedantic] (π§ planned autofix) Disallow certain types in boolean expressions
"typescript/strict-boolean-expressions": "off", // DECIDEME (errors: 4219)
// [pedantic] (π‘ suggestion) Require switch-case statements to be exhaustive
@@ -1019,7 +1024,7 @@
// [restriction] Disallow require statements except in import statements
"typescript/no-var-requires": "error",
// [restriction] (π οΈ autofix) (π needcs type info) Enforce non-null assertions over explicit type assertions
- "typescript/non-nullable-type-assertion-style": "off", // DECIDEME (errors: 12)
+ "typescript/non-nullable-type-assertion-style": "error",
// [restriction] Require all enum members to be literal values
"typescript/prefer-literal-enum-member": "off", // DECIDEME (errors: 21)
// [restriction] (π οΈ autofix) (π needs type info) Require any function or method that returns a Promise to be marked async
@@ -1312,7 +1317,7 @@
// [style] (π§ planned autofix) Enforce that literals on classes are exposed in a consistent style
"typescript/class-literal-property-style": "error",
// [style] (π οΈ autofix) Enforce specifying generic type arguments on type annotation or constructor name of a constructor call
- "typescript/consistent-generic-constructors": "off", // DECIDEME (errors: 38)
+ "typescript/consistent-generic-constructors": "error",
// [style] (π οΈ autofix) Require or disallow the Record type
"typescript/consistent-indexed-object-style": "off", // DECIDEME (errors: 40)
// [style] (π οΈ autofix) (π‘ suggestion) Enforce consistent usage of type assertions
@@ -1324,15 +1329,15 @@
// [style] (π§ planned autofix) Disallow the declaration of empty interfaces
"typescript/no-empty-interface": "error", // [reason: FILLMEIN]
// [style] (π‘ suggestion) Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean
- "typescript/no-inferrable-types": "off", // DECIDEME (errors: 12)
+ "typescript/no-inferrable-types": "error",
// [style] Require or disallow parameter properties in class constructors
- "typescript/parameter-properties": "off", // DECIDEME (errors: 128)
+ "typescript/parameter-properties": "error",
// [style] (π§ planned autofix) Enforce the use of for-of loop over the standard for loop where possible
"typescript/prefer-for-of": "error",
// [style] (π οΈ autofix) Enforce using function types instead of interfaces with call signatures
- "typescript/prefer-function-type": "off", // DECIDEME (errors: 1)
+ "typescript/prefer-function-type": "error",
// [style] (π οΈ autofix) (π needs type info) Enforce using type parameter when calling Array#reduce instead of using a type assertion
- "typescript/prefer-reduce-type-parameter": "off", // DECIDEME (errors: 4)
+ "typescript/prefer-reduce-type-parameter": "error",
// [style] (π οΈ autofix) (π needs type info) Enforce that this is used when only this type is returned
"typescript/prefer-return-this-type": "error",
// [style] Require that function overload signatures be consecutive
@@ -1526,17 +1531,17 @@
// [suspicious] Enforce style prop value is an object
"react/style-prop-object": "error",
// [suspicious] (π§ planned autofix) Disallow non-null assertion in locations that may be confusing
- "typescript/no-confusing-non-null-assertion": "off", // DECIDEME (errors: 3)
+ "typescript/no-confusing-non-null-assertion": "error",
// [suspicious] (β οΈ π‘ dangerous suggestion) Disallow classes used as namespaces
- "typescript/no-extraneous-class": "off", // DECIDEME (errors: 3)
+ "typescript/no-extraneous-class": "error",
// [suspicious] (π§ planned autofix) (π needs type info) Disallow unnecessary equality comparisons against boolean literals
- "typescript/no-unnecessary-boolean-literal-compare": "off", // DECIDEME (errors: 38)
+ "typescript/no-unnecessary-boolean-literal-compare": "error", // DECIDEME (errors: 38)
// [suspicious] (π§ planned autofix) (π needs type info) Disallow unnecessary template expressions
- "typescript/no-unnecessary-template-expression": "off", // DECIDEME (errors: 20)
+ "typescript/no-unnecessary-template-expression": "error",
// [suspicious] (π οΈ autofix) (π needs type info) Disallow type arguments that are equal to the default
- "typescript/no-unnecessary-type-arguments": "off", // DECIDEME (errors: 24)
+ "typescript/no-unnecessary-type-arguments": "error",
// [suspicious] (π οΈ autofix) (π needs type info) Disallow type assertions that do not change the type of an expression
- "typescript/no-unnecessary-type-assertion": "off", // DECIDEME (errors: 11)
+ "typescript/no-unnecessary-type-assertion": "error",
// [suspicious] (π§ planned autofix) Disallow unnecessary constraints on generic types
"typescript/no-unnecessary-type-constraint": "error",
// [suspicious] (π§ planned autofix) (π needs type info) Disallow comparing an enum value with a non-enum value
@@ -1693,6 +1698,15 @@
"eslint/no-inner-declarations": "off"
}
},
+ {
+ "files": [
+ "ts/test-node/state/ducks/**",
+ "ts/test-electron/state/ducks/**"
+ ],
+ "rules": {
+ "typescript/await-thenable": "off"
+ }
+ },
{
"files": [
".oxlint/**",
diff --git a/.storybook/main.ts b/.storybook/main.ts
index 6a19c3c916..891acbe09c 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -106,7 +106,7 @@ const storybookConfig: StorybookConfig = {
// oxlint-disable-next-line no-param-reassign
webpackConfig.externals = ({ request }, callback) => {
if (
- (/^node:/.test(request) && request !== 'node:buffer') ||
+ (request.startsWith('node:') && request !== 'node:buffer') ||
EXTERNALS.has(request)
) {
// Keep Node.js imports unchanged
diff --git a/app/EmojiService.main.ts b/app/EmojiService.main.ts
index 3f3c02ef4c..e9f9583022 100644
--- a/app/EmojiService.main.ts
+++ b/app/EmojiService.main.ts
@@ -27,6 +27,7 @@ type EmojiEntryType = Readonly<{
type SheetCacheEntry = Map>;
export class EmojiService {
+ readonly #resourceService: OptionalResourceService;
readonly #emojiMap = new Map();
readonly #sheetCache = new LRUCache({
@@ -35,9 +36,11 @@ export class EmojiService {
});
private constructor(
- private readonly resourceService: OptionalResourceService,
+ resourceService: OptionalResourceService,
manifest: ManifestType
) {
+ this.#resourceService = resourceService;
+
protocol.handle('emoji', async req => {
const url = new URL(req.url);
const emoji = url.searchParams.get('emoji');
@@ -74,7 +77,7 @@ export class EmojiService {
let imageMap = this.#sheetCache.get(sheet);
if (!imageMap) {
- const proto = await this.resourceService.getData(
+ const proto = await this.#resourceService.getData(
`emoji-sheet-${sheet}.proto`
);
if (!proto) {
diff --git a/app/OptionalResourceService.main.ts b/app/OptionalResourceService.main.ts
index 79cfcd71cd..3a3181ffd7 100644
--- a/app/OptionalResourceService.main.ts
+++ b/app/OptionalResourceService.main.ts
@@ -31,6 +31,7 @@ const RESOURCES_DICT_PATH = join(
const MAX_CACHE_SIZE = 50 * 1024 * 1024;
export class OptionalResourceService {
+ readonly #resourcesDir: string;
#maybeDeclaration: OptionalResourcesDictType | undefined;
readonly #cache = new LRUCache>({
@@ -41,7 +42,9 @@ export class OptionalResourceService {
readonly #fileQueues = new Map();
- private constructor(private readonly resourcesDir: string) {
+ private constructor(resourcesDir: string) {
+ this.#resourcesDir = resourcesDir;
+
ipcMain.handle('OptionalResourceService:getData', (_event, name) =>
this.getData(name)
);
@@ -61,7 +64,7 @@ export class OptionalResourceService {
return undefined;
}
- const filePath = join(this.resourcesDir, name);
+ const filePath = join(this.#resourcesDir, name);
return this.#queueFileWork(filePath, async () => {
const inMemory = this.#cache.get(name);
if (inMemory) {
@@ -117,7 +120,7 @@ export class OptionalResourceService {
// Clean unknown resources
let subPaths: Array;
try {
- subPaths = await readdir(this.resourcesDir);
+ subPaths = await readdir(this.#resourcesDir);
} catch (error) {
// Directory wasn't created yet
if (error.code === 'ENOENT') {
@@ -132,7 +135,7 @@ export class OptionalResourceService {
return;
}
- const fullPath = join(this.resourcesDir, subPath);
+ const fullPath = join(this.#resourcesDir, subPath);
try {
await unlink(fullPath);
diff --git a/app/PreventDisplaySleepService.std.ts b/app/PreventDisplaySleepService.std.ts
index 05d2698b97..3e205b44b7 100644
--- a/app/PreventDisplaySleepService.std.ts
+++ b/app/PreventDisplaySleepService.std.ts
@@ -7,9 +7,12 @@ import { createLogger } from '../ts/logging/log.std.ts';
const log = createLogger('PreventDisplaySleepService');
export class PreventDisplaySleepService {
+ readonly #powerSaveBlocker: PowerSaveBlocker;
private blockerId: undefined | number;
- constructor(private powerSaveBlocker: PowerSaveBlocker) {}
+ constructor(powerSaveBlocker: PowerSaveBlocker) {
+ this.#powerSaveBlocker = powerSaveBlocker;
+ }
isEnabled(): boolean {
return this.blockerId !== undefined;
@@ -33,14 +36,14 @@ export class PreventDisplaySleepService {
if (this.blockerId !== undefined) {
return;
}
- this.blockerId = this.powerSaveBlocker.start('prevent-display-sleep');
+ this.blockerId = this.#powerSaveBlocker.start('prevent-display-sleep');
}
#disable(): void {
if (this.blockerId === undefined) {
return;
}
- this.powerSaveBlocker.stop(this.blockerId);
+ this.#powerSaveBlocker.stop(this.blockerId);
delete this.blockerId;
}
}
diff --git a/app/SystemTrayService.main.ts b/app/SystemTrayService.main.ts
index 79f811cc59..79650680d6 100644
--- a/app/SystemTrayService.main.ts
+++ b/app/SystemTrayService.main.ts
@@ -33,7 +33,7 @@ export class SystemTrayService {
#isEnabled = false;
#isQuitting = false;
#unreadCount = 0;
- #createTrayInstance: (icon: NativeImage) => Tray;
+ readonly #createTrayInstance: (icon: NativeImage) => Tray;
constructor({ i18n, createTrayInstance }: SystemTrayServiceOptionsType) {
log.info('System tray service: created');
@@ -120,7 +120,7 @@ export class SystemTrayService {
return this.#tray !== undefined;
}
- #render = (): void => {
+ readonly #render = (): void => {
if (this.#isEnabled && this.#browserWindow) {
this.#renderEnabled();
return;
diff --git a/app/SystemTraySettingCache.node.ts b/app/SystemTraySettingCache.node.ts
index 915fa22721..e9d49d78a9 100644
--- a/app/SystemTraySettingCache.node.ts
+++ b/app/SystemTraySettingCache.node.ts
@@ -17,13 +17,18 @@ const log = createLogger('SystemTraySettingCache');
* process.
*/
export class SystemTraySettingCache {
+ readonly #ephemeralConfig: Pick;
+ readonly #argv: Array;
#cachedValue: undefined | SystemTraySetting;
#getPromise: undefined | Promise;
constructor(
- private readonly ephemeralConfig: Pick,
- private readonly argv: Array
- ) {}
+ ephemeralConfig: Pick,
+ argv: Array
+ ) {
+ this.#ephemeralConfig = ephemeralConfig;
+ this.#argv = argv;
+ }
async get(): Promise {
if (this.#cachedValue !== undefined) {
@@ -43,18 +48,18 @@ export class SystemTraySettingCache {
// These command line flags are not officially supported, but many users rely on them.
// Be careful when removing them or making changes.
- if (this.argv.some(arg => arg === '--start-in-tray')) {
+ if (this.#argv.some(arg => arg === '--start-in-tray')) {
result = SystemTraySetting.MinimizeToAndStartInSystemTray;
log.info(
`getSystemTraySetting saw --start-in-tray flag. Returning ${result}`
);
- } else if (this.argv.some(arg => arg === '--use-tray-icon')) {
+ } else if (this.#argv.some(arg => arg === '--use-tray-icon')) {
result = SystemTraySetting.MinimizeToSystemTray;
log.info(
`getSystemTraySetting saw --use-tray-icon flag. Returning ${result}`
);
} else if (isSystemTraySupported(OS)) {
- const value = this.ephemeralConfig.get('system-tray-setting');
+ const value = this.#ephemeralConfig.get('system-tray-setting');
if (value !== undefined) {
log.info('getSystemTraySetting got value', value);
}
@@ -68,7 +73,7 @@ export class SystemTraySettingCache {
}
if (result !== value) {
- this.ephemeralConfig.set('system-tray-setting', result);
+ this.#ephemeralConfig.set('system-tray-setting', result);
}
} else {
result = SystemTraySetting.DoNotUseSystemTray;
diff --git a/app/crashReports.main.ts b/app/crashReports.main.ts
index c8ce9c6d44..13c26f24a7 100644
--- a/app/crashReports.main.ts
+++ b/app/crashReports.main.ts
@@ -186,7 +186,7 @@ export function setup(
}
// Node.js Addons are useful
- if (/\.node$/.test(filename)) {
+ if (filename.endsWith('.node')) {
return true;
}
diff --git a/app/main.main.ts b/app/main.main.ts
index 1c83cfad5a..3be19f604e 100644
--- a/app/main.main.ts
+++ b/app/main.main.ts
@@ -1823,6 +1823,7 @@ async function initializeSQL(
return {
ok: false,
+ // oxlint-disable-next-line typescript/restrict-template-expressions
error: new Error(`initializeSQL: Caught a non-error '${error}'`),
};
} finally {
diff --git a/app/protocol_filter.node.ts b/app/protocol_filter.node.ts
index a84bae520e..3d436e6d26 100644
--- a/app/protocol_filter.node.ts
+++ b/app/protocol_filter.node.ts
@@ -110,7 +110,7 @@ function _createFileHandler({
}
log.info(
- `Warning: denying request to path '${realPath}' (allowedRoots: '${allowedRoots}')`
+ `Warning: denying request to path '${realPath}' (allowedRoots: '${allowedRoots.join(', ')}')`
);
callback({ error: -10 });
} catch (err) {
diff --git a/app/user_config.main.ts b/app/user_config.main.ts
index 0cbab49e6f..f96fe70a42 100644
--- a/app/user_config.main.ts
+++ b/app/user_config.main.ts
@@ -17,6 +17,7 @@ if (config.has('storagePath')) {
} else if (config.has('storageProfile')) {
userData = join(
app.getPath('appData'),
+ // oxlint-disable-next-line typescript/restrict-template-expressions
`Signal-${config.get('storageProfile')}`
);
} else if (OS.isAppImage()) {
diff --git a/build/intl-linter/utils/rule.std.ts b/build/intl-linter/utils/rule.std.ts
index 9975520172..78a3cff667 100644
--- a/build/intl-linter/utils/rule.std.ts
+++ b/build/intl-linter/utils/rule.std.ts
@@ -20,9 +20,7 @@ export type Context = {
): void;
};
-export type RuleFactory = {
- (context: Context): Visitor;
-};
+export type RuleFactory = (context: Context) => Visitor;
export type Rule = {
id: string;
diff --git a/danger/rules/enforceTailwindDepsMatch.mjs b/danger/rules/enforceTailwindDepsMatch.mjs
index 89ae757d72..286a8f29f6 100644
--- a/danger/rules/enforceTailwindDepsMatch.mjs
+++ b/danger/rules/enforceTailwindDepsMatch.mjs
@@ -31,6 +31,11 @@ function isTailwindPackage(pkgName) {
for (const depType of ['dependencies', 'devDependencies']) {
for (const [depName, depSpec] of Object.entries(pkgJson[depType])) {
+ if (typeof depSpec !== 'string') {
+ throw new TypeError(
+ `Expected string value for "${depName}" in package.json#${depType}`
+ );
+ }
if (isTailwindPackage(depName) && depSpec !== expectedVersion) {
fail(
`**Tailwind package versions must all match**\n` +
diff --git a/scripts/check-upgradeable-deps.mjs b/scripts/check-upgradeable-deps.mjs
index a43322872c..5fed64e005 100644
--- a/scripts/check-upgradeable-deps.mjs
+++ b/scripts/check-upgradeable-deps.mjs
@@ -176,7 +176,7 @@ const { approvedDeps } = await enquirer.prompt({
return {
name: deps.name,
- message: `${deps.name.padEnd(longestNameLength)}`,
+ message: deps.name.padEnd(longestNameLength),
hint: `(${color(deps.diff)}: ${deps.resolvedVersion} -> ${color(deps.latestVersion)})`,
};
}),
diff --git a/scripts/generate-acknowledgments.mjs b/scripts/generate-acknowledgments.mjs
index 76c0dd5013..66466ee85e 100644
--- a/scripts/generate-acknowledgments.mjs
+++ b/scripts/generate-acknowledgments.mjs
@@ -13,11 +13,7 @@ import packageJson from '../package.json' with { type: 'json' };
// Enable this flag to throw an error.
const REQUIRE_SIGNAL_LIB_FILES = Boolean(process.env.REQUIRE_SIGNAL_LIB_FILES);
-const {
- dependencies = {},
- devDependencies = {},
- optionalDependencies = {},
-} = packageJson;
+const { dependencies, devDependencies, optionalDependencies } = packageJson;
const SIGNAL_LIBS = [
'@signalapp/libsignal-client',
diff --git a/scripts/get-emoji-locales.mjs b/scripts/get-emoji-locales.mjs
index cd35007681..5edd83880c 100644
--- a/scripts/get-emoji-locales.mjs
+++ b/scripts/get-emoji-locales.mjs
@@ -34,8 +34,8 @@ async function fetchJSON(url) {
const manifest = ManifestSchema.parse(await fetchJSON(MANIFEST_URL));
-manifest.languageToSmartlingLocale['zh_TW'] = 'zh-Hant';
-manifest.languageToSmartlingLocale['sr'] = 'sr';
+manifest.languageToSmartlingLocale.zh_TW = 'zh-Hant';
+manifest.languageToSmartlingLocale.sr = 'sr';
/** @type {Map} */
const extraResources = new Map();
diff --git a/scripts/notarize-universal-dmg.mjs b/scripts/notarize-universal-dmg.mjs
index c4955fb916..ffa54dc710 100644
--- a/scripts/notarize-universal-dmg.mjs
+++ b/scripts/notarize-universal-dmg.mjs
@@ -19,7 +19,9 @@ export async function afterAllArtifactBuild({
platform => platform.name
);
if (platforms.length !== 1) {
- console.log(`notarize: Skipping, too many platforms ${platforms}`);
+ console.log(
+ `notarize: Skipping, too many platforms ${platforms.join(', ')}`
+ );
return;
}
@@ -63,7 +65,9 @@ export async function afterAllArtifactBuild({
/^.*mac-universal.*\.dmg$/.test(artifactPath)
);
if (artifactsToStaple.length !== 1) {
- console.log(`notarize: Skipping, too many dmgs ${artifactsToStaple}`);
+ console.log(
+ `notarize: Skipping, too many dmgs ${artifactsToStaple.join(', ')}`
+ );
return;
}
diff --git a/scripts/prepare_linux_build.mjs b/scripts/prepare_linux_build.mjs
index 17a10cc91f..5434c28312 100644
--- a/scripts/prepare_linux_build.mjs
+++ b/scripts/prepare_linux_build.mjs
@@ -13,7 +13,7 @@ if (
!targets.every(target => TARGETS.has(target.toLowerCase()))
) {
console.error(
- `Invalid linux targets ${targets}. Valid options: ${[...TARGETS]}`
+ `Invalid linux targets ${targets.join(', ')}. Valid options: ${[...TARGETS].join(', ')}`
);
process.exit(1);
}
diff --git a/scripts/sign-macos.mjs b/scripts/sign-macos.mjs
index 3e510f6840..4a3af61ae7 100644
--- a/scripts/sign-macos.mjs
+++ b/scripts/sign-macos.mjs
@@ -37,6 +37,7 @@ export async function sign(configuration) {
});
if (returnCode) {
+ // oxlint-disable-next-line typescript/restrict-template-expressions
throw new Error(`sign-macos: Script returned code ${returnCode}`);
}
}
diff --git a/scripts/sign-windows.mjs b/scripts/sign-windows.mjs
index b2cd1c3865..445b03ff50 100644
--- a/scripts/sign-windows.mjs
+++ b/scripts/sign-windows.mjs
@@ -34,6 +34,7 @@ export async function sign(configuration) {
});
if (returnCode) {
+ // oxlint-disable-next-line typescript/restrict-template-expressions
throw new Error(`sign-windows: Script returned code ${returnCode}`);
}
}
diff --git a/scripts/test-electron.mjs b/scripts/test-electron.mjs
index 511e4982ba..2d21f62ab1 100644
--- a/scripts/test-electron.mjs
+++ b/scripts/test-electron.mjs
@@ -183,7 +183,7 @@ async function launchElectron(worker, attempt) {
]);
} catch (error) {
if (exitSignal && RETRIABLE_SIGNALS.includes(exitSignal)) {
- return launchElectron(worker, attempt + 1);
+ return await launchElectron(worker, attempt + 1);
}
throw error;
} finally {
diff --git a/ts/CI.preload.ts b/ts/CI.preload.ts
index 3a4d962592..70598d58ca 100644
--- a/ts/CI.preload.ts
+++ b/ts/CI.preload.ts
@@ -216,7 +216,7 @@ export function getCI({
const { error } =
await backupsService.stageLocalBackupForImport(snapshotDir);
if (error) {
- throw error;
+ throw new Error(error);
}
}
diff --git a/ts/ConversationController.preload.ts b/ts/ConversationController.preload.ts
index d4a46a1893..3153d42925 100644
--- a/ts/ConversationController.preload.ts
+++ b/ts/ConversationController.preload.ts
@@ -172,9 +172,9 @@ export class ConversationController {
#_initialPromise: undefined | Promise;
#_conversations: Array = [];
- #_conversationOpenStart = new Map();
+ readonly #_conversationOpenStart = new Map();
#_hasQueueEmptied = false;
- #_combineConversationsQueue = new PQueue({ concurrency: 1 });
+ readonly #_combineConversationsQueue = new PQueue({ concurrency: 1 });
#_signalConversationId: undefined | string;
#delayBeforeUpdatingRedux: (() => number) | undefined;
@@ -187,7 +187,7 @@ export class ConversationController {
#_byGroupId: Record = Object.create(null);
#_byId: Record = Object.create(null);
- #debouncedUpdateUnreadCount = debounce(
+ readonly #debouncedUpdateUnreadCount = debounce(
this.updateUnreadCount.bind(this),
SECOND,
{
@@ -197,7 +197,7 @@ export class ConversationController {
}
);
- #convoUpdateBatcher = createBatcher<
+ readonly #convoUpdateBatcher = createBatcher<
| { type: 'change' | 'add'; conversation: ConversationModel }
| { type: 'remove'; id: string }
>({
@@ -955,8 +955,7 @@ export class ConversationController {
} else if (targetConversation && !targetConversation?.get(key)) {
// This is mostly for the situation where PNI was erased when updating e164
log.debug(
- `${logId}: Re-adding ${key} on target conversation - ` +
- `${targetConversation.idForLogging()}`
+ `${logId}: Re-adding ${key} on target conversation - ${targetConversation.idForLogging()}`
);
applyChangeToConversation(targetConversation, pniSignatureVerified, {
[key]: value,
@@ -1413,8 +1412,7 @@ export class ConversationController {
log.warn(
`${logId}: Ensure that all V1 groups have new conversationId instead of old`
);
- const groups =
- await this.getAllGroupsInvolvingServiceId(obsoleteServiceId);
+ const groups = this.getAllGroupsInvolvingServiceId(obsoleteServiceId);
groups.forEach(group => {
const members = group.get('members');
const withoutObsolete = without(members, obsoleteId);
diff --git a/ts/RemoteConfig.dom.ts b/ts/RemoteConfig.dom.ts
index cb83cd928a..d9456d3cb1 100644
--- a/ts/RemoteConfig.dom.ts
+++ b/ts/RemoteConfig.dom.ts
@@ -203,7 +203,7 @@ export const _refreshRemoteConfig = async ({
// new configuration only includes enabled flags we can't distinguish betewen
// a remote flag being deleted or being disabled. We synthesize that for our
// known keys.
- const newConfigValues: Map = new Map(
+ const newConfigValues = new Map(
KnownConfigKeys.map(name => [name, undefined])
);
for (const [name, value] of newConfig) {
diff --git a/ts/SignalProtocolStore.preload.ts b/ts/SignalProtocolStore.preload.ts
index a313a9c685..824c19d531 100644
--- a/ts/SignalProtocolStore.preload.ts
+++ b/ts/SignalProtocolStore.preload.ts
@@ -121,6 +121,7 @@ function validateIdentityKey(attrs: unknown): attrs is IdentityKeyType {
*/
function formatKeys(keys: Array): string {
return formatGroups(
+ // oxlint-disable-next-line typescript/require-array-sort-compare
groupWhile(keys.sort(), (a, b) => a + 1 === b).slice(0, 10),
'-',
', ',
@@ -246,9 +247,9 @@ export class SignalProtocolStore extends EventEmitter {
// Cached values
- #ourIdentityKeys = new Map();
+ readonly #ourIdentityKeys = new Map();
- #ourRegistrationIds = new Map();
+ readonly #ourRegistrationIds = new Map();
#cachedPniSignatureMessage: PniSignatureMessageType | undefined;
identityKeys?: Map<
diff --git a/ts/WebAudioRecorder.std.ts b/ts/WebAudioRecorder.std.ts
index 90dbc0c37d..ea8a8ada2a 100644
--- a/ts/WebAudioRecorder.std.ts
+++ b/ts/WebAudioRecorder.std.ts
@@ -20,12 +20,12 @@ type OptionsType = {
};
export class WebAudioRecorder {
- #buffer: Array;
- #options: OptionsType;
- #context: BaseAudioContext;
- #input: GainNode;
- #onComplete: (recorder: WebAudioRecorder, blob: Blob) => unknown;
- #onError: (recorder: WebAudioRecorder, error: string) => unknown;
+ readonly #buffer: Array;
+ readonly #options: OptionsType;
+ readonly #context: BaseAudioContext;
+ readonly #input: GainNode;
+ readonly #onComplete: (recorder: WebAudioRecorder, blob: Blob) => unknown;
+ readonly #onError: (recorder: WebAudioRecorder, error: string) => unknown;
private processor?: ScriptProcessorNode;
public worker?: Worker;
diff --git a/ts/axo/AxoScrollArea.dom.tsx b/ts/axo/AxoScrollArea.dom.tsx
index a2476e8819..c251a279cc 100644
--- a/ts/axo/AxoScrollArea.dom.tsx
+++ b/ts/axo/AxoScrollArea.dom.tsx
@@ -100,7 +100,7 @@ export namespace AxoScrollArea {
orientation = 'vertical',
maxWidth,
maxHeight,
- scrollbarWidth = 'thin',
+ scrollbarWidth,
scrollbarGutter = 'stable-both-edges',
scrollbarVisibility = 'auto',
scrollBehavior = 'auto',
diff --git a/ts/axo/_internal/assert.std.tsx b/ts/axo/_internal/assert.std.tsx
index 38171ebd0f..b137e22d5a 100644
--- a/ts/axo/_internal/assert.std.tsx
+++ b/ts/axo/_internal/assert.std.tsx
@@ -11,6 +11,7 @@ export function assert(input: T, message?: string): NonNullable {
if (input === false || input == null) {
// oxlint-disable-next-line no-debugger
debugger;
+ // oxlint-disable-next-line typescript/restrict-template-expressions
throw new AssertionError(message ?? `input is ${input}`);
}
return input;
diff --git a/ts/axo/_internal/scrollbars.dom.tsx b/ts/axo/_internal/scrollbars.dom.tsx
index d21afe3347..07765497b3 100644
--- a/ts/axo/_internal/scrollbars.dom.tsx
+++ b/ts/axo/_internal/scrollbars.dom.tsx
@@ -17,11 +17,11 @@ type Listener = () => void;
type Unsubscribe = () => void;
class ScrollbarGuttersObserver {
- #container: HTMLDivElement;
- #scroller: HTMLDivElement;
+ readonly #container: HTMLDivElement;
+ readonly #scroller: HTMLDivElement;
#current: ScrollbarGutters | null;
- #observer: ResizeObserver;
- #listeners = new Set();
+ readonly #observer: ResizeObserver;
+ readonly #listeners = new Set();
constructor(scrollbarWidth: Exclude) {
const container = document.createElement('div');
diff --git a/ts/background.preload.ts b/ts/background.preload.ts
index c2d4a136d6..1d9f9227b3 100644
--- a/ts/background.preload.ts
+++ b/ts/background.preload.ts
@@ -815,6 +815,7 @@ export async function startApp(): Promise {
await convo.shutdownJobQueue();
} catch (err) {
log.error(
+ // oxlint-disable-next-line typescript/restrict-template-expressions
`shutdown: error waiting for conversation ${convo.idForLogging} job queue shutdown`,
Errors.toLogFormat(err)
);
@@ -1375,7 +1376,7 @@ export async function startApp(): Promise {
StorageService.enableStorageService();
if (andSync != null) {
- await StorageService.runStorageServiceSyncJob({
+ StorageService.runStorageServiceSyncJob({
reason: andSync,
});
StorageService.runStorageServiceSyncJob.flush();
@@ -1682,12 +1683,12 @@ export async function startApp(): Promise {
if (!itemStorage.user.getAci()) {
log.error(`${logId}: ACI not captured during registration, unlinking`);
- return unlinkAndDisconnect();
+ return await unlinkAndDisconnect();
}
if (!itemStorage.user.getPni()) {
log.error(`${logId}: PNI not captured during registration, unlinking`);
- return unlinkAndDisconnect();
+ return await unlinkAndDisconnect();
}
// 2. Fetch remote config, before we process the message queue
@@ -1857,7 +1858,7 @@ export async function startApp(): Promise {
}
async function afterEveryLinkedStartupOnNewVersion({
- skipSyncRequests = false,
+ skipSyncRequests,
}: {
skipSyncRequests: boolean;
}) {
@@ -3587,7 +3588,7 @@ export async function startApp(): Promise {
}
}
- await StorageService.runStorageServiceSyncJob({ reason: 'onKeysSync' });
+ StorageService.runStorageServiceSyncJob({ reason: 'onKeysSync' });
}
ev.confirm();
}
diff --git a/ts/badges/badgeImageFileDownloader.preload.ts b/ts/badges/badgeImageFileDownloader.preload.ts
index 7b0bff729b..611b650ffb 100644
--- a/ts/badges/badgeImageFileDownloader.preload.ts
+++ b/ts/badges/badgeImageFileDownloader.preload.ts
@@ -20,7 +20,7 @@ enum BadgeDownloaderState {
class BadgeImageFileDownloader {
#state = BadgeDownloaderState.Idle;
- #queue = new PQueue({ concurrency: 3 });
+ readonly #queue = new PQueue({ concurrency: 3 });
public async checkForFilesToDownload(): Promise {
switch (this.#state) {
diff --git a/ts/calling/VideoSupport.preload.ts b/ts/calling/VideoSupport.preload.ts
index c6948717b0..5251f09f73 100644
--- a/ts/calling/VideoSupport.preload.ts
+++ b/ts/calling/VideoSupport.preload.ts
@@ -52,7 +52,7 @@ export class GumVideoCapturer {
private mediaStream?: MediaStream;
private spawnedSenderRunning = false;
private preferredDeviceId?: string;
- private reportVideoSizeCallback = this.reportVideoSize.bind(this);
+ private readonly reportVideoSizeCallback = this.reportVideoSize.bind(this);
capturing(): boolean {
return this.captureOptions !== undefined;
@@ -418,7 +418,7 @@ export const MAX_VIDEO_CAPTURE_BUFFER_SIZE = MAX_VIDEO_CAPTURE_AREA * 4;
export class CanvasVideoRenderer {
private canvas?: RefObject;
private sizeCallback?: SizeCallbackType;
- private buffer: Uint8Array;
+ private readonly buffer: Uint8Array;
private imageData?: ImageData;
private source?: VideoFrameSource;
private rafId?: ReturnType;
diff --git a/ts/challenge.dom.ts b/ts/challenge.dom.ts
index 6b4f902b64..ed6d3d4533 100644
--- a/ts/challenge.dom.ts
+++ b/ts/challenge.dom.ts
@@ -126,6 +126,8 @@ export function getChallengeURL(type: 'chat' | 'registration'): string {
// `ChallengeHandler` should be in memory at the same time because they could
// overwrite each others storage data.
export class ChallengeHandler {
+ readonly #options: Options;
+
#solving = 0;
#isLoaded = false;
#challengeToken: string | undefined;
@@ -142,7 +144,9 @@ export class ChallengeHandler {
readonly #startTimers = new Map();
readonly #pendingStarts = new Set();
- constructor(private readonly options: Options) {}
+ constructor(options: Options) {
+ this.#options = options;
+ }
public async load(): Promise {
if (this.#isLoaded) {
@@ -151,13 +155,13 @@ export class ChallengeHandler {
this.#isLoaded = true;
const challenges: ReadonlyArray =
- this.options.storage.get(STORAGE_KEY) || [];
+ this.#options.storage.get(STORAGE_KEY) || [];
log.info(`loading ${challenges.length} challenges`);
await Promise.all(
challenges.map(async challenge => {
- const expireAfter = this.options.expireAfter || DEFAULT_EXPIRE_AFTER;
+ const expireAfter = this.#options.expireAfter || DEFAULT_EXPIRE_AFTER;
if (isOlderThan(challenge.createdAt, expireAfter)) {
log.info(
`expired challenge for conversation ${challenge.conversationId}`
@@ -192,7 +196,7 @@ export class ChallengeHandler {
log.info(`online, starting ${pending.length} queues`);
// Start queues for challenges that matured while we were offline
- await this.#startAllQueues();
+ this.#startAllQueues();
}
public maybeSolve({ conversationId, reason }: MaybeSolveOptionsType): void {
@@ -342,7 +346,7 @@ export class ChallengeHandler {
const request: IPCRequest = { seq: this.#seq, reason };
this.#seq += 1;
- this.options.requestChallenge(request);
+ this.#options.requestChallenge(request);
const response = await new Promise((resolve, reject) => {
this.#responseHandlers.set(request.seq, { token, resolve, reject });
@@ -356,7 +360,7 @@ export class ChallengeHandler {
this.#isLoaded,
'ChallengeHandler has to be loaded before persisting new data'
);
- await this.options.storage.put(
+ await this.#options.storage.put(
STORAGE_KEY,
Array.from(this.#registeredConversations.values())
);
@@ -391,16 +395,16 @@ export class ChallengeHandler {
await this.unregister(conversationId, 'startQueue');
if (this.#registeredConversations.size === 0) {
- this.options.setChallengeStatus('idle');
+ this.#options.setChallengeStatus('idle');
}
log.info(`startQueue: starting queue ${conversationId}`);
- this.options.startQueue(conversationId);
+ this.#options.startQueue(conversationId);
}
async #solve({ reason, token }: SolveOptionsType): Promise {
this.#solving += 1;
- this.options.setChallengeStatus('required');
+ this.#options.setChallengeStatus('required');
this.#challengeToken = token;
const captcha = await this.requestCaptcha({ reason, token });
@@ -414,12 +418,12 @@ export class ChallengeHandler {
const lastToken = this.#challengeToken;
this.#challengeToken = undefined;
- this.options.setChallengeStatus('pending');
+ this.#options.setChallengeStatus('pending');
log.info(`challenge(${reason}): sending challenge to server`);
try {
- await this.options.sendChallengeResponse({
+ await this.#options.sendChallengeResponse({
type: 'captcha',
token: lastToken,
captcha,
@@ -454,8 +458,8 @@ export class ChallengeHandler {
// Remove the challenge dialog, and trigger the conversationJobQueue to retry the
// sends, which will likely trigger another captcha
- this.options.setChallengeStatus('idle');
- this.options.onChallengeFailed(retryAfter);
+ this.#options.setChallengeStatus('idle');
+ this.#options.onChallengeFailed(retryAfter);
this.forceWaitOnAll(retryAt);
return;
} finally {
@@ -464,8 +468,8 @@ export class ChallengeHandler {
log.info(`challenge(${reason}): challenge success. force sending`);
- this.options.setChallengeStatus('idle');
- this.options.onChallengeSolved();
+ this.#options.setChallengeStatus('idle');
+ this.#options.onChallengeSolved();
this.#startAllQueues({ force: true });
}
}
diff --git a/ts/components/AvatarLightbox.dom.tsx b/ts/components/AvatarLightbox.dom.tsx
index e01332130a..a9cf707e01 100644
--- a/ts/components/AvatarLightbox.dom.tsx
+++ b/ts/components/AvatarLightbox.dom.tsx
@@ -63,7 +63,7 @@ export function AvatarLightbox({
width: 'auto',
minHeight: '64px',
height: '100%',
- maxHeight: `min(${512}px, 100%)`,
+ maxHeight: `min(512px, 100%)`,
aspectRatio: '1 / 1',
}}
/>
diff --git a/ts/components/CallScreen.dom.stories.tsx b/ts/components/CallScreen.dom.stories.tsx
index 9ab08d48dd..7539040b7e 100644
--- a/ts/components/CallScreen.dom.stories.tsx
+++ b/ts/components/CallScreen.dom.stories.tsx
@@ -753,11 +753,9 @@ function useMakeEveryoneTalk(
Math.random() * call.remoteParticipants.length
);
- const demuxIdToStartSpeaking = (
- call.remoteParticipants[
- idxToStartSpeaking
- ] as GroupCallRemoteParticipantType
- ).demuxId;
+ const demuxIdToStartSpeaking =
+ // oxlint-disable-next-line typescript/no-non-null-assertion
+ call.remoteParticipants[idxToStartSpeaking]!.demuxId;
const remoteAudioLevels = new Map();
@@ -776,9 +774,7 @@ function useMakeEveryoneTalk(
hasRemoteAudio:
idx === idxToStartSpeaking ? true : part.hasRemoteAudio,
speakerTime:
- idx === idxToStartSpeaking
- ? Date.now()
- : (part as GroupCallRemoteParticipantType).speakerTime,
+ idx === idxToStartSpeaking ? Date.now() : part.speakerTime,
};
}),
remoteAudioLevels,
@@ -899,7 +895,8 @@ function useReactionsEmitter({
{
timestamp: timeNow,
demuxId,
- value: sample(emojis) as string,
+ // oxlint-disable-next-line typescript/no-non-null-assertion
+ value: sample(emojis)!,
},
];
diff --git a/ts/components/CallScreen.dom.tsx b/ts/components/CallScreen.dom.tsx
index fdeb0118b8..07bc2fb58a 100644
--- a/ts/components/CallScreen.dom.tsx
+++ b/ts/components/CallScreen.dom.tsx
@@ -161,7 +161,7 @@ export type PropsType = {
export const isInSpeakerView = (
call: Pick | undefined
): boolean => {
- return Boolean(
+ return (
call?.viewMode === CallViewMode.Presentation ||
call?.viewMode === CallViewMode.Speaker
);
diff --git a/ts/components/CallingPendingParticipants.dom.tsx b/ts/components/CallingPendingParticipants.dom.tsx
index 320fe0c320..26922f1959 100644
--- a/ts/components/CallingPendingParticipants.dom.tsx
+++ b/ts/components/CallingPendingParticipants.dom.tsx
@@ -128,7 +128,7 @@ export function CallingPendingParticipants({
}, [serviceIdsStagedForAction, batchUserAction, hideConfirmDialog]);
const renderApprovalButtons = useCallback(
- (participant: ConversationType, isEnabled: boolean = true) => {
+ (participant: ConversationType, isEnabled = true) => {
if (participant.serviceId == null) {
return null;
}
diff --git a/ts/components/CallingRaisedHandsList.dom.tsx b/ts/components/CallingRaisedHandsList.dom.tsx
index cbded176f3..7cf7670e45 100644
--- a/ts/components/CallingRaisedHandsList.dom.tsx
+++ b/ts/components/CallingRaisedHandsList.dom.tsx
@@ -42,7 +42,7 @@ export function CallingRaisedHandsList({
: undefined;
const participants = React.useMemo>(() => {
- const serviceIds: Set = new Set();
+ const serviceIds = new Set();
const conversations: Array = [];
raisedHands.forEach(demuxId => {
const conversation = conversationsByDemuxId.get(demuxId);
diff --git a/ts/components/CompositionArea.dom.stories.tsx b/ts/components/CompositionArea.dom.stories.tsx
index d82973eb3f..7d88029840 100644
--- a/ts/components/CompositionArea.dom.stories.tsx
+++ b/ts/components/CompositionArea.dom.stories.tsx
@@ -50,7 +50,7 @@ const memberColors = new Map(
return null;
}
return [
- admin.member.id?.toString(),
+ admin.member.id,
// oxlint-disable-next-line typescript/no-non-null-assertion
ContactNameColors[i % ContactNameColors.length]!,
];
diff --git a/ts/components/CompositionInput.dom.tsx b/ts/components/CompositionInput.dom.tsx
index f0d85330e1..99709cef89 100644
--- a/ts/components/CompositionInput.dom.tsx
+++ b/ts/components/CompositionInput.dom.tsx
@@ -665,7 +665,7 @@ export function CompositionInput(props: Props): React.ReactElement {
}
if (propsRef.current.onDirtyChange) {
- let isDirty: boolean = false;
+ let isDirty = false;
if (!draftEditMessage) {
isDirty = text.length > 0;
diff --git a/ts/components/CompositionTextArea.dom.tsx b/ts/components/CompositionTextArea.dom.tsx
index 521e868ba4..f7a1be5530 100644
--- a/ts/components/CompositionTextArea.dom.tsx
+++ b/ts/components/CompositionTextArea.dom.tsx
@@ -25,12 +25,12 @@ export type CompositionTextAreaProps = {
maxLength?: number;
placeholder?: string;
whenToShowRemainingCount?: number;
- onScroll?: (ev: React.UIEvent) => void;
+ onScroll?: (ev: React.UIEvent) => void;
onSelectEmoji: (emojiSelection: FunEmojiSelection) => void;
onChange: (
messageText: string,
draftBodyRanges: HydratedBodyRangesType,
- caretLocation?: number | undefined
+ caretLocation?: number
) => void;
emojiSkinToneDefault: EmojiSkinTone;
onEmojiSkinToneDefaultChange: (emojiSkinToneDefault: EmojiSkinTone) => void;
diff --git a/ts/components/ConversationList.dom.tsx b/ts/components/ConversationList.dom.tsx
index f8567bbd79..460da35c3b 100644
--- a/ts/components/ConversationList.dom.tsx
+++ b/ts/components/ConversationList.dom.tsx
@@ -605,6 +605,7 @@ export function ConversationList({
showConversation={showConversation}
/>
);
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
key = `start-new-conversation:${row.phoneNumber}`;
break;
case RowType.UsernameSearchResult:
diff --git a/ts/components/EditHistoryMessagesModal.dom.tsx b/ts/components/EditHistoryMessagesModal.dom.tsx
index 32cf5834da..a825c40e7d 100644
--- a/ts/components/EditHistoryMessagesModal.dom.tsx
+++ b/ts/components/EditHistoryMessagesModal.dom.tsx
@@ -191,12 +191,11 @@ export function EditHistoryMessagesModal({
{pastEdits.map(messageAttributes => {
const syntheticId = `${messageAttributes.id}.${messageAttributes.timestamp}`;
- const shouldShowDateHeader = Boolean(
+ const shouldShowDateHeader =
!previousItem ||
// This comparison avoids strange header behavior for out-of-order messages.
(messageAttributes.timestamp > previousItem.timestamp &&
- !isSameDay(previousItem.timestamp, messageAttributes.timestamp))
- );
+ !isSameDay(previousItem.timestamp, messageAttributes.timestamp));
const dateHeaderElement = shouldShowDateHeader ? (
= React.memo(
imageData?.data.buffer !== frameBuffer.buffer ||
imageData?.data.byteOffset !== frameBuffer.byteOffset
) {
- const view = new Uint8ClampedArray(
+ const view = new Uint8ClampedArray(
frameBuffer.buffer,
frameBuffer.byteOffset,
frameWidth * frameHeight * 4
diff --git a/ts/components/GroupCallRemoteParticipants.dom.tsx b/ts/components/GroupCallRemoteParticipants.dom.tsx
index 01750f9c89..514b64d28e 100644
--- a/ts/components/GroupCallRemoteParticipants.dom.tsx
+++ b/ts/components/GroupCallRemoteParticipants.dom.tsx
@@ -724,7 +724,7 @@ function getGridParticipantsByPage({
...pageLayoutProps,
});
- let nextPage: ParticipantsInPageType | undefined;
+ let nextPage: ParticipantsInPageType | undefined;
if (
nextPageInSortedOrder.numParticipants ===
@@ -806,12 +806,9 @@ function getGridParticipantsByPage({
break;
}
- const nextPageTiles =
- nextPage as ParticipantsInPageType;
-
// Add a previous page tile if needed
if (pages.length > 0) {
- const firstRow = nextPageTiles.rows[0];
+ const firstRow = nextPage.rows[0];
strictAssert(firstRow, 'Missing firstRow');
firstRow.unshift({
isPaginationButton: true,
@@ -837,7 +834,7 @@ function getGridParticipantsByPage({
// Add a next page tile if needed
if (remainingParticipants.length) {
- nextPageTiles.rows.at(-1)?.push({
+ nextPage.rows.at(-1)?.push({
isPaginationButton: true,
paginationButtonType: 'next',
videoAspectRatio: PAGINATION_BUTTON_ASPECT_RATIO,
diff --git a/ts/components/Lightbox.dom.tsx b/ts/components/Lightbox.dom.tsx
index 89601c7ca7..af79b9c92a 100644
--- a/ts/components/Lightbox.dom.tsx
+++ b/ts/components/Lightbox.dom.tsx
@@ -174,7 +174,7 @@ export function Lightbox({
setShouldShowDownloadToast(false);
}, [isDownloading, setShouldShowDownloadToast]);
const onUserInteractionOnVideo = useCallback(
- (event: React.MouseEvent) => {
+ (event: React.MouseEvent) => {
if (downloadToastTimeout.current) {
clearTimeout(downloadToastTimeout.current);
downloadToastTimeout.current = undefined;
@@ -196,9 +196,7 @@ export function Lightbox({
);
const onPrevious = useCallback(
- (
- event: KeyboardEvent | React.MouseEvent
- ) => {
+ (event: KeyboardEvent | React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
@@ -212,9 +210,7 @@ export function Lightbox({
);
const onNext = useCallback(
- (
- event: KeyboardEvent | React.MouseEvent
- ) => {
+ (event: KeyboardEvent | React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
@@ -235,9 +231,7 @@ export function Lightbox({
}, [setVideoTime, videoElement]);
const handleSave = useCallback(
- (
- event: KeyboardEvent | React.MouseEvent
- ) => {
+ (event: KeyboardEvent | React.MouseEvent) => {
if (isViewOnce) {
return;
}
@@ -254,9 +248,7 @@ export function Lightbox({
[isViewOnce, media, saveAttachment, selectedIndex]
);
- const handleForward = (
- event: React.MouseEvent
- ) => {
+ const handleForward = (event: React.MouseEvent) => {
if (isViewOnce) {
return;
}
@@ -524,7 +516,7 @@ export function Lightbox({
);
const zoomButtonHandler = useCallback(
- (ev: React.MouseEvent) => {
+ (ev: React.MouseEvent) => {
ev.preventDefault();
ev.stopPropagation();
@@ -850,10 +842,7 @@ export function Lightbox({
key={item.attachment.thumbnail?.url}
type="button"
onClick={(
- event: React.MouseEvent<
- HTMLButtonElement,
- MouseEvent
- >
+ event: React.MouseEvent
) => {
event.stopPropagation();
event.preventDefault();
diff --git a/ts/components/ListTile.dom.tsx b/ts/components/ListTile.dom.tsx
index 026ee08189..24752834e9 100644
--- a/ts/components/ListTile.dom.tsx
+++ b/ts/components/ListTile.dom.tsx
@@ -14,7 +14,7 @@ export type Props = {
trailing?: string | React.JSX.Element;
moduleClassName?: string;
onClick?: () => void;
- onContextMenu?: (ev: React.MouseEvent) => void;
+ onContextMenu?: (ev: React.MouseEvent) => void;
// show hover highlight,
// defaults to true if onClick is defined
clickable?: boolean;
diff --git a/ts/components/MediaEditor.dom.stories.tsx b/ts/components/MediaEditor.dom.stories.tsx
index fd5c0398a9..6670907aef 100644
--- a/ts/components/MediaEditor.dom.stories.tsx
+++ b/ts/components/MediaEditor.dom.stories.tsx
@@ -22,6 +22,7 @@ export default {
getPreferredBadge: () => undefined,
isHighQuality: false,
i18n,
+ // oxlint-disable-next-line typescript/no-base-to-string
imageToBlurHash: input => Promise.resolve(input.toString()),
imageSrc: IMAGE_2,
isFormattingEnabled: true,
diff --git a/ts/components/Preferences.dom.tsx b/ts/components/Preferences.dom.tsx
index 8a59146388..89a06177e0 100644
--- a/ts/components/Preferences.dom.tsx
+++ b/ts/components/Preferences.dom.tsx
@@ -559,7 +559,7 @@ export function Preferences({
localeOverride,
theme,
themeSetting,
- universalExpireTimer = DurationInSeconds.ZERO,
+ universalExpireTimer,
validateBackup,
whoCanFindMe,
whoCanSeeMe,
@@ -660,7 +660,7 @@ export function Preferences({
const handleContentProtectionChange = useCallback(
(value: boolean) => {
- if (value === true || !isContentProtectionNeeded) {
+ if (value || !isContentProtectionNeeded) {
onContentProtectionChange(value);
} else {
setConfirmContentProtection(true);
@@ -1969,7 +1969,7 @@ export function Preferences({
<>
{
setStagedProfile(profileData => ({
...profileData,
- firstName: String(newFirstName),
+ firstName: newFirstName,
}));
}}
placeholder={i18n('icu:ProfileEditor--first-name')}
diff --git a/ts/components/StoryLinkPreview.dom.tsx b/ts/components/StoryLinkPreview.dom.tsx
index e7a647b876..84eabbe638 100644
--- a/ts/components/StoryLinkPreview.dom.tsx
+++ b/ts/components/StoryLinkPreview.dom.tsx
@@ -28,7 +28,7 @@ export function StoryLinkPreview({
url,
}: Props): React.JSX.Element {
const isImage = isImageAttachment(image);
- const location = domain || getSafeDomain(String(url));
+ const location = domain || getSafeDomain(url);
const isCompact = forceCompactMode || !image;
let content: React.JSX.Element | undefined;
diff --git a/ts/components/TextAttachment.dom.tsx b/ts/components/TextAttachment.dom.tsx
index 3933a0ef5a..aa1d5597ae 100644
--- a/ts/components/TextAttachment.dom.tsx
+++ b/ts/components/TextAttachment.dom.tsx
@@ -297,7 +297,7 @@ export const TextAttachment = forwardRef(
)}
{
setSelectedBackground(backgroundValue);
setIsColorPickerShowing(false);
diff --git a/ts/components/ToastManager.dom.tsx b/ts/components/ToastManager.dom.tsx
index 06b627c4bf..10915ceed7 100644
--- a/ts/components/ToastManager.dom.tsx
+++ b/ts/components/ToastManager.dom.tsx
@@ -280,7 +280,7 @@ export function renderToast({
toastAction={{
label: i18n('icu:conversationArchivedUndo'),
onClick: () => {
- onUndoArchive(String(toast.parameters.conversationId), {
+ onUndoArchive(toast.parameters.conversationId, {
wasPinned: toast.parameters.wasPinned,
});
},
diff --git a/ts/components/conversation/ContactName.dom.tsx b/ts/components/conversation/ContactName.dom.tsx
index aa78f399f9..c14a2299a5 100644
--- a/ts/components/conversation/ContactName.dom.tsx
+++ b/ts/components/conversation/ContactName.dom.tsx
@@ -89,7 +89,7 @@ export function ContactName({
contactNameColor ? getClassName(`--${contactNameColor}`) : null
)}
dir="auto"
- onClick={(event: React.MouseEvent) => {
+ onClick={(event: React.MouseEvent) => {
if (onClick) {
onClick();
event.stopPropagation();
diff --git a/ts/components/conversation/ConversationHeader.dom.tsx b/ts/components/conversation/ConversationHeader.dom.tsx
index 6812546fa8..96540edcec 100644
--- a/ts/components/conversation/ConversationHeader.dom.tsx
+++ b/ts/components/conversation/ConversationHeader.dom.tsx
@@ -642,12 +642,11 @@ function HeaderDropdownMenuContent({
}) {
const muteOptions = getMuteOptions(conversation.muteExpiresAt, i18n);
const isGroup = conversation.type === 'group';
- const disableTimerChanges = Boolean(
+ const disableTimerChanges =
!conversation.canChangeTimer ||
!conversation.acceptedMessageRequest ||
conversation.left ||
- isMissingMandatoryProfileSharing
- );
+ isMissingMandatoryProfileSharing;
const hasGV2AdminEnabled = isGroup && conversation.groupVersion === 2;
const disappearingMessagesValue = useMemo(() => {
diff --git a/ts/components/conversation/GIF.dom.tsx b/ts/components/conversation/GIF.dom.tsx
index 6bb2faad5a..af7ae578a7 100644
--- a/ts/components/conversation/GIF.dom.tsx
+++ b/ts/components/conversation/GIF.dom.tsx
@@ -44,7 +44,7 @@ export type Props = {
cancelDownload(): void;
};
-type MediaEvent = React.SyntheticEvent;
+type MediaEvent = React.SyntheticEvent;
export function GIF(props: Props): React.JSX.Element {
const {
diff --git a/ts/components/conversation/Message.dom.tsx b/ts/components/conversation/Message.dom.tsx
index 49f3655e4c..27e05568fa 100644
--- a/ts/components/conversation/Message.dom.tsx
+++ b/ts/components/conversation/Message.dom.tsx
@@ -493,7 +493,7 @@ const MessageReactions = forwardRef(function MessageReactions(
// Take the first three groups for rendering
const toRender = take(ordered, 3).map(group => {
- const isMe = group.some(re => Boolean(re.from.isMe));
+ const isMe = group.some(re => re.from.isMe);
const count = group.length;
const firstReaction = group[0];
strictAssert(firstReaction, 'Missing firstReaction');
@@ -531,7 +531,7 @@ const MessageReactions = forwardRef(function MessageReactions(
);
const notRenderedIsMe =
someNotRendered &&
- maybeNotRendered.some(res => res.some(re => Boolean(re.from.isMe)));
+ maybeNotRendered.some(res => res.some(re => re.from.isMe));
const popperPlacement = outgoing ? 'bottom-end' : 'bottom-start';
@@ -658,11 +658,12 @@ export class Message extends React.PureComponent {
public reactionsContainerRef: React.RefObject =
React.createRef();
- #hasSelectedTextRef: React.MutableRefObject = {
+ readonly #hasSelectedTextRef: React.MutableRefObject = {
current: false,
};
- #metadataRef: React.RefObject = React.createRef();
+ readonly #metadataRef: React.RefObject =
+ React.createRef();
public expirationCheckInterval: NodeJS.Timeout | undefined;
@@ -1066,7 +1067,7 @@ export class Message extends React.PureComponent {
return true;
}
- #updateMetadataWidth = (newMetadataWidth: number): void => {
+ readonly #updateMetadataWidth = (newMetadataWidth: number): void => {
this.setState(({ metadataWidth }) => ({
// We don't want text to jump around if the metadata shrinks, but we want to make
// sure we have enough room.
@@ -1074,7 +1075,7 @@ export class Message extends React.PureComponent {
}));
};
- #handleSelectionChange = () => {
+ readonly #handleSelectionChange = () => {
const selection = document.getSelection();
if (selection != null && !selection.isCollapsed) {
this.#hasSelectedTextRef.current = true;
@@ -2102,7 +2103,7 @@ export class Message extends React.PureComponent {
);
}
- #doubleCheckMissingQuoteReference = () => {
+ readonly #doubleCheckMissingQuoteReference = () => {
return this.props.doubleCheckMissingQuoteReference(this.props.id);
};
@@ -2976,22 +2977,23 @@ export class Message extends React.PureComponent {
);
}
- #popperPreventOverflowModifier = (): Partial => {
- const { containerElementRef } = this.props;
- return {
- name: 'preventOverflow',
- options: {
- altAxis: true,
- boundary: containerElementRef.current || undefined,
- padding: {
- bottom: 16,
- left: 8,
- right: 8,
- top: 16,
+ readonly #popperPreventOverflowModifier =
+ (): Partial => {
+ const { containerElementRef } = this.props;
+ return {
+ name: 'preventOverflow',
+ options: {
+ altAxis: true,
+ boundary: containerElementRef.current || undefined,
+ padding: {
+ bottom: 16,
+ left: 8,
+ right: 8,
+ top: 16,
+ },
},
- },
+ };
};
- };
public toggleReactionViewer = (onlyRemove = false): void => {
this.setState(oldState => {
diff --git a/ts/components/conversation/MessageMetadata.dom.tsx b/ts/components/conversation/MessageMetadata.dom.tsx
index 8c6f005192..1fc5fcd9fc 100644
--- a/ts/components/conversation/MessageMetadata.dom.tsx
+++ b/ts/components/conversation/MessageMetadata.dom.tsx
@@ -85,9 +85,7 @@ export const MessageMetadata = forwardRef>(
const [confirmationType, setConfirmationType] = useState<
ConfirmationType | undefined
>();
- const withImageNoCaption = Boolean(
- !isSticker && !hasText && isShowingImage
- );
+ const withImageNoCaption = !isSticker && !hasText && isShowingImage;
const metadataDirection = isSticker ? undefined : direction;
let timestampNode: ReactNode;
diff --git a/ts/components/conversation/MessageTextRenderer.dom.tsx b/ts/components/conversation/MessageTextRenderer.dom.tsx
index 260a0ed634..7803a258c0 100644
--- a/ts/components/conversation/MessageTextRenderer.dom.tsx
+++ b/ts/components/conversation/MessageTextRenderer.dom.tsx
@@ -159,9 +159,8 @@ function renderNode({
const key = node.start;
if (node.isSpoiler && node.spoilerChildren?.length) {
- const isSpoilerHidden = Boolean(
- node.isSpoiler && !isSpoilerExpanded[node.spoilerId || 0]
- );
+ const isSpoilerHidden =
+ node.isSpoiler && !isSpoilerExpanded[node.spoilerId || 0];
const content = node.spoilerChildren?.map(spoilerNode =>
renderNode({
direction,
diff --git a/ts/components/conversation/Timeline.dom.tsx b/ts/components/conversation/Timeline.dom.tsx
index 2f5adfc604..897ba84231 100644
--- a/ts/components/conversation/Timeline.dom.tsx
+++ b/ts/components/conversation/Timeline.dom.tsx
@@ -166,7 +166,7 @@ export class Timeline extends React.Component<
readonly #atBottomDetectorRef = React.createRef();
readonly #lastSeenIndicatorRef = React.createRef();
#intersectionObserver?: IntersectionObserver;
- #intersectionRatios: Map = new Map();
+ #intersectionRatios = new Map();
// This is a best guess. It will likely be overridden when the timeline is measured.
#maxVisibleRows = Math.ceil(window.innerHeight / MIN_ROW_HEIGHT);
@@ -184,7 +184,7 @@ export class Timeline extends React.Component<
widthBreakpoint: WidthBreakpoint.Wide,
};
- #onScrollLockChange = (): void => {
+ readonly #onScrollLockChange = (): void => {
const scrollLocked = this.#scrollerLock.isLocked();
this.setState(() => {
// Prevent scroll due to elements shrinking or disappearing (e.g. typing indicators)
@@ -198,9 +198,12 @@ export class Timeline extends React.Component<
});
};
- #scrollerLock = createScrollerLock('Timeline', this.#onScrollLockChange);
+ readonly #scrollerLock = createScrollerLock(
+ 'Timeline',
+ this.#onScrollLockChange
+ );
- #onScroll = (event: UIEvent): void => {
+ readonly #onScroll = (event: UIEvent): void => {
// When content is removed from the viewport, such as typing indicators leaving
// or messages being edited smaller or deleted, scroll events are generated and
// they are marked as user-generated (isTrusted === true). Actual user generated
@@ -246,7 +249,7 @@ export class Timeline extends React.Component<
?.scrollIntoView({ block: 'center' });
}
- #scrollToBottom = (setFocus?: boolean): void => {
+ readonly #scrollToBottom = (setFocus?: boolean): void => {
if (this.#scrollerLock.isLocked()) {
return;
}
@@ -266,12 +269,12 @@ export class Timeline extends React.Component<
}
};
- #onClickScrollDownButton = (): void => {
+ readonly #onClickScrollDownButton = (): void => {
this.#scrollerLock.onUserInterrupt('onClickScrollDownButton');
this.#scrollDown(false);
};
- #scrollDown = (setFocus?: boolean): void => {
+ readonly #scrollDown = (setFocus?: boolean): void => {
if (this.#scrollerLock.isLocked()) {
return;
}
@@ -536,93 +539,97 @@ export class Timeline extends React.Component<
return centerMessageId;
}
- #markNewestBottomVisibleMessageRead = throttle((itemId?: string): void => {
- const { id, items, markMessageRead } = this.props;
- const messageIdToMarkRead =
- itemId ?? this.state.newestBottomVisibleMessageId;
+ readonly #markNewestBottomVisibleMessageRead = throttle(
+ (itemId?: string): void => {
+ const { id, items, markMessageRead } = this.props;
+ const messageIdToMarkRead =
+ itemId ?? this.state.newestBottomVisibleMessageId;
- if (!messageIdToMarkRead) {
- return;
- }
+ if (!messageIdToMarkRead) {
+ return;
+ }
- const lastIndex = items.length - 1;
- const newestBottomVisibleItemIndex = items.findIndex(
- item => item.id === messageIdToMarkRead
- );
+ const lastIndex = items.length - 1;
+ const newestBottomVisibleItemIndex = items.findIndex(
+ item => item.id === messageIdToMarkRead
+ );
- // Mark the newest visible message read if we're at the bottom, or override provided
- if (
- messageIdToMarkRead &&
- (itemId || lastIndex === newestBottomVisibleItemIndex)
- ) {
- const item = items[newestBottomVisibleItemIndex];
- if (!item || item.type === 'none') {
+ // Mark the newest visible message read if we're at the bottom, or override provided
+ if (
+ messageIdToMarkRead &&
+ (itemId || lastIndex === newestBottomVisibleItemIndex)
+ ) {
+ const item = items[newestBottomVisibleItemIndex];
+ if (!item || item.type === 'none') {
+ markMessageRead(id, messageIdToMarkRead);
+ return;
+ }
+ }
+
+ // We can return early if the newest partially-visible item is not a CollapseSet
+ const newestPartiallyVisibleIndex = Math.min(
+ lastIndex,
+ newestBottomVisibleItemIndex + 1
+ );
+ const newestPartiallyVisibleItem = items[newestPartiallyVisibleIndex];
+ if (
+ newestPartiallyVisibleItem &&
+ newestPartiallyVisibleItem.type === 'none'
+ ) {
markMessageRead(id, messageIdToMarkRead);
return;
}
- }
- // We can return early if the newest partially-visible item is not a CollapseSet
- const newestPartiallyVisibleIndex = Math.min(
- lastIndex,
- newestBottomVisibleItemIndex + 1
- );
- const newestPartiallyVisibleItem = items[newestPartiallyVisibleIndex];
- if (
- newestPartiallyVisibleItem &&
- newestPartiallyVisibleItem.type === 'none'
- ) {
- markMessageRead(id, messageIdToMarkRead);
- return;
- }
-
- // Now we need to figure out which of the CollapseSet's inner messages are visible
- const collapseSetEl = this.#messagesRef.current?.querySelector(
- `[data-item-index="${newestPartiallyVisibleIndex}"]`
- );
- const containerWindowRect =
- this.#containerRef.current?.getBoundingClientRect();
- if (!collapseSetEl || !containerWindowRect) {
- markMessageRead(id, messageIdToMarkRead);
- return;
- }
-
- const messageEls = collapseSetEl.querySelectorAll('[data-message-id]');
- const containerWindowBottom =
- containerWindowRect.y + containerWindowRect.height;
-
- let newestFullyVisibleMessage;
- for (let i = messageEls.length - 1; i >= 0; i -= 1) {
- const messageEl = messageEls[i];
- strictAssert(messageEl, 'No messageEl at index i');
-
- // The messages might be rendered, but opacity = 0
- if (!messageEl.checkVisibility({ opacityProperty: true })) {
- break;
+ // Now we need to figure out which of the CollapseSet's inner messages are visible
+ const collapseSetEl = this.#messagesRef.current?.querySelector(
+ `[data-item-index="${newestPartiallyVisibleIndex}"]`
+ );
+ const containerWindowRect =
+ this.#containerRef.current?.getBoundingClientRect();
+ if (!collapseSetEl || !containerWindowRect) {
+ markMessageRead(id, messageIdToMarkRead);
+ return;
}
- // Make sure the messages are scrolled into view
- const messageRect = messageEl.getBoundingClientRect();
- const bottom = messageRect.y + messageRect.height;
+ const messageEls = collapseSetEl.querySelectorAll('[data-message-id]');
+ const containerWindowBottom =
+ containerWindowRect.y + containerWindowRect.height;
- if (bottom <= containerWindowBottom) {
- newestFullyVisibleMessage = messageEl;
- break;
+ let newestFullyVisibleMessage;
+ for (let i = messageEls.length - 1; i >= 0; i -= 1) {
+ const messageEl = messageEls[i];
+ strictAssert(messageEl, 'No messageEl at index i');
+
+ // The messages might be rendered, but opacity = 0
+ if (!messageEl.checkVisibility({ opacityProperty: true })) {
+ break;
+ }
+
+ // Make sure the messages are scrolled into view
+ const messageRect = messageEl.getBoundingClientRect();
+ const bottom = messageRect.y + messageRect.height;
+
+ if (bottom <= containerWindowBottom) {
+ newestFullyVisibleMessage = messageEl;
+ break;
+ }
}
- }
- if (!newestFullyVisibleMessage) {
- markMessageRead(id, messageIdToMarkRead);
- return;
- }
+ if (!newestFullyVisibleMessage) {
+ markMessageRead(id, messageIdToMarkRead);
+ return;
+ }
- const messageId = newestFullyVisibleMessage.getAttribute('data-message-id');
- markMessageRead(id, messageId || messageIdToMarkRead);
- }, 500);
+ const messageId =
+ newestFullyVisibleMessage.getAttribute('data-message-id');
+ markMessageRead(id, messageId || messageIdToMarkRead);
+ },
+ 500
+ );
// When the the window becomes active, or when a fullsceen call is ended, we mark read
// with a delay, to allow users to navigate away quickly without marking messages read
- #markNewestBottomVisibleMessageReadAfterDelay = throttle(
+ readonly #markNewestBottomVisibleMessageReadAfterDelay = throttle(
this.#markNewestBottomVisibleMessageRead,
DELAY_BEFORE_MARKING_READ_AFTER_FOCUS,
{
@@ -854,7 +861,7 @@ export class Timeline extends React.Component<
}
}
- #handleBlur = (event: React.FocusEvent): void => {
+ readonly #handleBlur = (event: React.FocusEvent): void => {
const { clearTargetedMessage } = this.props;
const { currentTarget } = event;
@@ -878,7 +885,9 @@ export class Timeline extends React.Component<
}, 0);
};
- #handleKeyDown = (event: React.KeyboardEvent): void => {
+ readonly #handleKeyDown = (
+ event: React.KeyboardEvent
+ ): void => {
const { targetMessage, targetedMessageId, items, id } = this.props;
const commandKey = get(window, 'platform') === 'darwin' && event.metaKey;
const controlKey = get(window, 'platform') !== 'darwin' && event.ctrlKey;
@@ -1123,11 +1132,10 @@ export class Timeline extends React.Component<
.slice(-SCROLL_DOWN_BUTTON_THRESHOLD)
.find(item => item.id === newestBottomVisibleMessageId));
- const areUnreadBelowCurrentPosition = Boolean(
+ const areUnreadBelowCurrentPosition =
areThereAnyMessages &&
areAnyMessagesUnread &&
- areAnyMessagesBelowCurrentPosition
- );
+ areAnyMessagesBelowCurrentPosition;
const shouldShowScrollDownButtons = Boolean(
areThereAnyMessages &&
(areUnreadBelowCurrentPosition || areAboveScrollDownButtonThreshold)
diff --git a/ts/components/conversation/WaveformScrubber.dom.tsx b/ts/components/conversation/WaveformScrubber.dom.tsx
index fb33c83a75..c126536422 100644
--- a/ts/components/conversation/WaveformScrubber.dom.tsx
+++ b/ts/components/conversation/WaveformScrubber.dom.tsx
@@ -76,11 +76,11 @@ export const WaveformScrubber = React.forwardRef(function WaveformScrubber(
let increment: number;
if (event.key === 'ArrowUp' || event.key === arrow('end')) {
- increment = +SMALL_INCREMENT;
+ increment = SMALL_INCREMENT;
} else if (event.key === 'ArrowDown' || event.key === arrow('start')) {
increment = -SMALL_INCREMENT;
} else if (event.key === 'PageUp') {
- increment = +BIG_INCREMENT;
+ increment = BIG_INCREMENT;
} else if (event.key === 'PageDown') {
increment = -BIG_INCREMENT;
} else {
diff --git a/ts/components/conversation/conversation-details/ConversationDetailsIcon.dom.tsx b/ts/components/conversation/conversation-details/ConversationDetailsIcon.dom.tsx
index ab195f02dc..10ca090263 100644
--- a/ts/components/conversation/conversation-details/ConversationDetailsIcon.dom.tsx
+++ b/ts/components/conversation/conversation-details/ConversationDetailsIcon.dom.tsx
@@ -81,7 +81,7 @@ export function ConversationDetailsIcon({
role="button"
className={bem('button')}
tabIndex={0}
- onClick={(event: React.MouseEvent) => {
+ onClick={(event: React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
onClick();
@@ -106,7 +106,7 @@ export function ConversationDetailsIcon({
className={bem('button')}
disabled={disabled}
type="button"
- onClick={(event: React.MouseEvent) => {
+ onClick={(event: React.MouseEvent) => {
event.preventDefault();
event.stopPropagation();
onClick();
diff --git a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx
index c188b9442b..8e5cc4145c 100644
--- a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx
+++ b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx
@@ -197,7 +197,7 @@ export function ConversationDetailsMembershipList({
/>
);
})}
- {showAllMembers === false && shouldHideRestMembers && (
+ {!showAllMembers && shouldHideRestMembers && (
{
- if (isSaving === false && previousIsSaving !== isSaving && !isDirty) {
+ if (!isSaving && previousIsSaving !== isSaving && !isDirty) {
popPanelForConversation();
}
}, [isDirty, isSaving, popPanelForConversation, previousIsSaving]);
diff --git a/ts/components/conversation/media-gallery/MediaGallery.dom.tsx b/ts/components/conversation/media-gallery/MediaGallery.dom.tsx
index fe7bf1acf6..476ad7c48f 100644
--- a/ts/components/conversation/media-gallery/MediaGallery.dom.tsx
+++ b/ts/components/conversation/media-gallery/MediaGallery.dom.tsx
@@ -257,7 +257,7 @@ export function MediaGallery({
// Reset local state when redux finishes loading
useEffect(() => {
- if (reduxLoading === false) {
+ if (!reduxLoading) {
setLoading(false);
}
}, [reduxLoading]);
diff --git a/ts/components/conversation/poll-message/PollMessageContents.dom.tsx b/ts/components/conversation/poll-message/PollMessageContents.dom.tsx
index 1dbdb4a4a1..1878d3bd90 100644
--- a/ts/components/conversation/poll-message/PollMessageContents.dom.tsx
+++ b/ts/components/conversation/poll-message/PollMessageContents.dom.tsx
@@ -290,7 +290,7 @@ export function PollMessageContents({
- handlePollOptionClicked(index, Boolean(next))
+ handlePollOptionClicked(index, next)
}
isIncoming={isIncoming}
isPending={isVotePending}
diff --git a/ts/components/conversationList/BaseConversationListItem.dom.tsx b/ts/components/conversationList/BaseConversationListItem.dom.tsx
index 595a91d54f..c747e036a5 100644
--- a/ts/components/conversationList/BaseConversationListItem.dom.tsx
+++ b/ts/components/conversationList/BaseConversationListItem.dom.tsx
@@ -128,9 +128,7 @@ export const BaseConversationListItem: FunctionComponent =
const testId = overrideTestId || groupId || serviceId;
const isUnread = isConversationUnread({ markedUnread, unreadCount });
- const isAvatarNoteToSelf = isBoolean(isNoteToSelf)
- ? isNoteToSelf
- : Boolean(isMe);
+ const isAvatarNoteToSelf = isBoolean(isNoteToSelf) ? isNoteToSelf : isMe;
const isCheckbox = isBoolean(checked);
diff --git a/ts/components/conversationList/ContactCheckbox.dom.tsx b/ts/components/conversationList/ContactCheckbox.dom.tsx
index e9acf329bf..41f124f7f7 100644
--- a/ts/components/conversationList/ContactCheckbox.dom.tsx
+++ b/ts/components/conversationList/ContactCheckbox.dom.tsx
@@ -101,7 +101,7 @@ export const ContactCheckbox: FunctionComponent = React.memo(
avatarUrl={avatarUrl}
color={color}
conversationType={type}
- noteToSelf={Boolean(isMe)}
+ noteToSelf={isMe}
i18n={i18n}
phoneNumber={phoneNumber}
profileName={profileName}
diff --git a/ts/components/conversationList/ContactListItem.dom.tsx b/ts/components/conversationList/ContactListItem.dom.tsx
index cb593ec047..569702a159 100644
--- a/ts/components/conversationList/ContactListItem.dom.tsx
+++ b/ts/components/conversationList/ContactListItem.dom.tsx
@@ -262,7 +262,7 @@ export const ContactListItem: FunctionComponent = React.memo(
avatarUrl={avatarUrl}
color={color}
conversationType={type}
- noteToSelf={Boolean(isMe)}
+ noteToSelf={isMe}
i18n={i18n}
phoneNumber={phoneNumber}
profileName={profileName}
diff --git a/ts/components/conversationList/MessageSearchResult.dom.stories.tsx b/ts/components/conversationList/MessageSearchResult.dom.stories.tsx
index 466b416182..fe3a0316a3 100644
--- a/ts/components/conversationList/MessageSearchResult.dom.stories.tsx
+++ b/ts/components/conversationList/MessageSearchResult.dom.stories.tsx
@@ -49,8 +49,10 @@ const useProps = (overrideProps: Partial = {}): PropsType => ({
snippet: overrideProps.snippet || "What's <>going<> on?",
body: overrideProps.body || "What's going on?",
bodyRanges: overrideProps.bodyRanges || [],
- from: overrideProps.from as PropsType['from'],
- to: overrideProps.to as PropsType['to'],
+ // oxlint-disable-next-line typescript/no-non-null-assertion
+ from: overrideProps.from!,
+ // oxlint-disable-next-line typescript/no-non-null-assertion
+ to: overrideProps.to!,
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
isSelected: overrideProps.isSelected || false,
showConversation: action('showConversation'),
diff --git a/ts/components/fun/data/emojis.std.ts b/ts/components/fun/data/emojis.std.ts
index 7c369d6f90..b84a516a02 100644
--- a/ts/components/fun/data/emojis.std.ts
+++ b/ts/components/fun/data/emojis.std.ts
@@ -66,7 +66,7 @@ export const EMOJI_SKIN_TONE_ORDER: ReadonlyArray = [
];
/** @deprecated We should use `EmojiSkinTone` everywhere */
-export const EMOJI_SKIN_TONE_TO_NUMBER: Map = new Map([
+export const EMOJI_SKIN_TONE_TO_NUMBER = new Map([
[EmojiSkinTone.None, 0],
[EmojiSkinTone.Type1, 1],
[EmojiSkinTone.Type2, 2],
@@ -85,7 +85,7 @@ export const KEY_TO_EMOJI_SKIN_TONE = new Map([
]);
/** @deprecated We should use `EmojiSkinTone` everywhere */
-export const EMOJI_SKIN_TONE_TO_KEY: Map = new Map([
+export const EMOJI_SKIN_TONE_TO_KEY = new Map([
[EmojiSkinTone.Type1, '1F3FB'],
[EmojiSkinTone.Type2, '1F3FC'],
[EmojiSkinTone.Type3, '1F3FD'],
@@ -453,7 +453,9 @@ for (const rawEmoji of RAW_EMOJI_DATA) {
const variantKey = one ?? two;
if (variantKey == null) {
const keys = Object.keys(rawEmoji.skin_variations);
- throw new Error(`Missing variant key ${parentKey} -> ${key} (${keys})`);
+ throw new Error(
+ `Missing variant key ${parentKey} -> ${key} (${keys.join(', ')})`
+ );
}
result[skinTone] = variantKey;
EMOJI_INDEX.variantKeyToSkinTone.set(variantKey, skinTone);
diff --git a/ts/components/fun/data/segments.std.ts b/ts/components/fun/data/segments.std.ts
index 0d8b4a6af5..225dd3ad31 100644
--- a/ts/components/fun/data/segments.std.ts
+++ b/ts/components/fun/data/segments.std.ts
@@ -84,6 +84,7 @@ export function _getSegmentRanges(
function assertExpected(actual: T, expected: T, message: string) {
strictAssert(
Object.is(actual, expected),
+ // oxlint-disable-next-line typescript/restrict-template-expressions
`${message}: ${actual} (expected: ${expected})`
);
}
diff --git a/ts/components/fun/keyboard/GridKeyboardDelegate.dom.tsx b/ts/components/fun/keyboard/GridKeyboardDelegate.dom.tsx
index feaced0c92..5ccf2b4492 100644
--- a/ts/components/fun/keyboard/GridKeyboardDelegate.dom.tsx
+++ b/ts/components/fun/keyboard/GridKeyboardDelegate.dom.tsx
@@ -38,8 +38,8 @@ function toState(state: State, cell: Cell | null): State {
}
export class GridKeyboardDelegate extends KeyboardDelegate {
- #virtualizer: Virtualizer;
- #layout: Layout;
+ readonly #virtualizer: Virtualizer;
+ readonly #layout: Layout;
constructor(
virtualizer: Virtualizer,
diff --git a/ts/components/fun/keyboard/WaterfallKeyboardDelegate.dom.tsx b/ts/components/fun/keyboard/WaterfallKeyboardDelegate.dom.tsx
index 5a6c37caf9..11fe80a307 100644
--- a/ts/components/fun/keyboard/WaterfallKeyboardDelegate.dom.tsx
+++ b/ts/components/fun/keyboard/WaterfallKeyboardDelegate.dom.tsx
@@ -48,7 +48,7 @@ function toKey(item: VirtualItem | null): Key | null {
}
export class WaterfallKeyboardDelegate extends KeyboardDelegate {
- #virtualizer: Virtualizer;
+ readonly #virtualizer: Virtualizer;
constructor(virtualizer: Virtualizer) {
super();
diff --git a/ts/components/leftPane/LeftPaneSearchHelper.dom.tsx b/ts/components/leftPane/LeftPaneSearchHelper.dom.tsx
index 152a1c2b16..b587fd05b1 100644
--- a/ts/components/leftPane/LeftPaneSearchHelper.dom.tsx
+++ b/ts/components/leftPane/LeftPaneSearchHelper.dom.tsx
@@ -426,7 +426,7 @@ export class LeftPaneSearchHelper extends LeftPaneHelper results.isLoading);
}
- #onEnterKeyDown = (
+ readonly #onEnterKeyDown = (
clearSearchQuery: () => unknown,
showConversation: ShowConversationType
): void => {
diff --git a/ts/components/stickers/mocks.std.ts b/ts/components/stickers/mocks.std.ts
index 2a95eeeac1..de8f6673b4 100644
--- a/ts/components/stickers/mocks.std.ts
+++ b/ts/components/stickers/mocks.std.ts
@@ -79,9 +79,7 @@ export const packs = [
createPack({ id: 'wide' }, wideSticker),
...Array(20)
.fill(0)
- .map((_, n) =>
- createPack({ id: `pack-${n}` }, sample(choosableStickers) as StickerType)
- ),
+ .map((_, n) => createPack({ id: `pack-${n}` }, sample(choosableStickers))),
];
export const recentStickers = [
diff --git a/ts/groups.preload.ts b/ts/groups.preload.ts
index 3a3e1b23ad..76928504d1 100644
--- a/ts/groups.preload.ts
+++ b/ts/groups.preload.ts
@@ -274,6 +274,7 @@ export async function getPreJoinGroupInfo(
const data = deriveGroupFields(Bytes.fromBase64(masterKeyBase64));
return makeRequestWithCredentials({
+ // oxlint-disable-next-line typescript/restrict-template-expressions
logId: `getPreJoinInfo/groupv2(${data.id})`,
publicParams: Bytes.toBase64(data.publicParams),
secretParams: Bytes.toBase64(data.secretParams),
@@ -1423,7 +1424,7 @@ export function buildPromoteMemberChange({
group,
profileKeyCredentialBase64,
serverPublicParamsBase64,
- isPendingPniAciProfileKey = false,
+ isPendingPniAciProfileKey,
}: BuildPromoteMemberChangeOptionsType): Actions.Params {
if (!group.secretParams) {
throw new Error(
@@ -1550,7 +1551,9 @@ export async function modifyGroupV2({
);
if (logIds.length !== 0) {
- log.info(`modifyGroupV2/${logId}: Fetching profiles for ${logIds}`);
+ log.info(
+ `modifyGroupV2/${logId}: Fetching profiles for ${logIds.join(', ')}`
+ );
}
// oxlint-disable-next-line no-await-in-loop
@@ -1676,7 +1679,7 @@ export async function modifyGroupV2({
if (logIds.length !== 0) {
log.warn(
`modifyGroupV2/${logId}: Profile key credentials were not ` +
- `up-to-date. Updating profiles for ${logIds} and retrying`
+ `up-to-date. Updating profiles for ${logIds.join(', ')} and retrying`
);
}
@@ -1991,7 +1994,7 @@ export async function createGroupV2(
log.warn(
`createGroupV2/${logId}: Profile key credentials were not ` +
- `up-to-date. Updating profiles for ${logIds} and retrying`
+ `up-to-date. Updating profiles for ${logIds.join(', ')} and retrying`
);
return createGroupV2({
@@ -4763,7 +4766,7 @@ function extractDiffs({
const oldMemberLookup = new Map(
(old.membersV2 || []).map(member => [member.aci, member])
);
- const didWeStartInGroup = Boolean(ourAci && oldMemberLookup.has(ourAci));
+ const didWeStartInGroup = oldMemberLookup.has(ourAci);
const oldPendingMemberLookup = new Map<
ServiceIdString,
@@ -5828,6 +5831,7 @@ export async function decryptGroupAvatar(
if (blob.content?.avatar == null) {
throw new Error(
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
`decryptGroupAvatar: Returned blob had incorrect content: ${blob.content}`
);
}
@@ -6448,7 +6452,7 @@ function decryptGroupChange(
return {
added: decrypted,
- joinFromInviteLink: Boolean(addMember.joinFromInviteLink),
+ joinFromInviteLink: addMember.joinFromInviteLink,
};
})
);
@@ -7001,7 +7005,7 @@ function decryptGroupChange(
if (actions.modifyAnnouncementsOnly) {
const { announcementsOnly } = actions.modifyAnnouncementsOnly;
result.modifyAnnouncementsOnly = {
- announcementsOnly: Boolean(announcementsOnly),
+ announcementsOnly,
};
}
diff --git a/ts/growing-file.d.ts b/ts/growing-file.d.ts
index 65ce523516..7c58ab8e68 100644
--- a/ts/growing-file.d.ts
+++ b/ts/growing-file.d.ts
@@ -7,6 +7,7 @@ declare module 'growing-file' {
interval?: number;
};
+ // oxlint-disable-next-line typescript/no-extraneous-class
class GrowingFile {
static open(path: string, options: GrowingFileOptions): Readable;
}
diff --git a/ts/hooks/useIsMounted.std.ts b/ts/hooks/useIsMounted.std.ts
index cdebec5f53..2e289ff2b5 100644
--- a/ts/hooks/useIsMounted.std.ts
+++ b/ts/hooks/useIsMounted.std.ts
@@ -24,5 +24,5 @@ export function useIsMounted(): () => boolean {
};
}, []);
- return useCallback(() => isMounted.current === true, []);
+ return useCallback(() => isMounted.current, []);
}
diff --git a/ts/jobs/AttachmentBackupManager.preload.ts b/ts/jobs/AttachmentBackupManager.preload.ts
index 72503a4d06..c93a3ab519 100644
--- a/ts/jobs/AttachmentBackupManager.preload.ts
+++ b/ts/jobs/AttachmentBackupManager.preload.ts
@@ -508,7 +508,7 @@ async function uploadToTransitTier({
}
// Legacy attachments
- return dependencies.encryptAndUploadAttachment({
+ return await dependencies.encryptAndUploadAttachment({
keys: fromBase64(keys),
needIncrementalMac,
plaintext: { absolutePath },
diff --git a/ts/jobs/AttachmentDownloadManager.preload.ts b/ts/jobs/AttachmentDownloadManager.preload.ts
index 063de9844c..e17bf06e3e 100644
--- a/ts/jobs/AttachmentDownloadManager.preload.ts
+++ b/ts/jobs/AttachmentDownloadManager.preload.ts
@@ -171,9 +171,9 @@ function getJobIdForLogging(job: CoreAttachmentDownloadJobType): string {
}
export class AttachmentDownloadManager extends JobManager {
- #visibleTimelineMessages: Set = new Set();
+ #visibleTimelineMessages = new Set();
- #saveJobsBatcher = createBatcher({
+ readonly #saveJobsBatcher = createBatcher({
name: 'saveAttachmentDownloadJobs',
wait: 150,
maxSize: 1000,
@@ -183,15 +183,15 @@ export class AttachmentDownloadManager extends JobManager Promise;
- #getMessageQueueTime: () => number;
- #hasMediaBackups: () => boolean;
- #statfs: typeof statfs;
- #maxAttachmentSize: number;
- #maxTextAttachmentSize: number;
- #minimumFreeDiskSpace: number;
+ readonly #onLowDiskSpaceBackupImport: (bytesNeeded: number) => Promise;
+ readonly #getMessageQueueTime: () => number;
+ readonly #hasMediaBackups: () => boolean;
+ readonly #statfs: typeof statfs;
+ readonly #maxAttachmentSize: number;
+ readonly #maxTextAttachmentSize: number;
+ readonly #minimumFreeDiskSpace: number;
- #attachmentBackfill = new AttachmentBackfill();
+ readonly #attachmentBackfill = new AttachmentBackfill();
private static _instance: AttachmentDownloadManager | undefined;
override logPrefix = 'AttachmentDownloadManager';
diff --git a/ts/jobs/AttachmentLocalBackupManager.preload.ts b/ts/jobs/AttachmentLocalBackupManager.preload.ts
index ad002e35d0..0f379ac278 100644
--- a/ts/jobs/AttachmentLocalBackupManager.preload.ts
+++ b/ts/jobs/AttachmentLocalBackupManager.preload.ts
@@ -36,7 +36,7 @@ type RunAttachmentBackupJobDependenciesType = {
export function getJobIdForLogging(
job: CoreAttachmentLocalBackupJobType
): string {
- return `${redactGenericText(job.mediaName)}`;
+ return redactGenericText(job.mediaName);
}
export async function runAttachmentBackupJob(
diff --git a/ts/jobs/CallLinkFinalizeDeleteManager.preload.ts b/ts/jobs/CallLinkFinalizeDeleteManager.preload.ts
index 1cbd57eb12..5b2c08d66a 100644
--- a/ts/jobs/CallLinkFinalizeDeleteManager.preload.ts
+++ b/ts/jobs/CallLinkFinalizeDeleteManager.preload.ts
@@ -50,7 +50,7 @@ function getJobId(job: CoreCallLinkDeleteJobType): string {
// synchronously and prior to running this job, so we can show confirmation
// or error to the user.
export class CallLinkFinalizeDeleteManager extends JobManager {
- jobs: Map = new Map();
+ jobs = new Map();
private static _instance: CallLinkFinalizeDeleteManager | undefined;
override logPrefix = 'CallLinkFinalizeDeleteManager';
diff --git a/ts/jobs/Job.std.ts b/ts/jobs/Job.std.ts
index b4b689d8ca..7f4586d1eb 100644
--- a/ts/jobs/Job.std.ts
+++ b/ts/jobs/Job.std.ts
@@ -7,11 +7,23 @@ import type { ParsedJob } from './types.std.ts';
* A single job instance. Shouldn't be instantiated directly, except by `JobQueue`.
*/
export class Job implements ParsedJob {
+ public readonly id: string;
+ public readonly timestamp: number;
+ public readonly queueType: string;
+ public readonly data: T;
+ public readonly completion: Promise;
+
constructor(
- readonly id: string,
- readonly timestamp: number,
- readonly queueType: string,
- readonly data: T,
- readonly completion: Promise
- ) {}
+ id: string,
+ timestamp: number,
+ queueType: string,
+ data: T,
+ completion: Promise
+ ) {
+ this.id = id;
+ this.timestamp = timestamp;
+ this.queueType = queueType;
+ this.data = data;
+ this.completion = completion;
+ }
}
diff --git a/ts/jobs/JobError.std.ts b/ts/jobs/JobError.std.ts
index b722adfa14..1685e01c42 100644
--- a/ts/jobs/JobError.std.ts
+++ b/ts/jobs/JobError.std.ts
@@ -9,8 +9,11 @@ import { reallyJsonStringify } from '../util/reallyJsonStringify.std.ts';
* Should not be instantiated directly, except by `JobQueue`.
*/
export class JobError extends Error {
- constructor(public readonly lastErrorThrownByJob: unknown) {
+ public readonly lastErrorThrownByJob: unknown;
+
+ constructor(lastErrorThrownByJob: unknown) {
super(`Job failed. Last error: ${formatError(lastErrorThrownByJob)}`);
+ this.lastErrorThrownByJob = lastErrorThrownByJob;
}
}
diff --git a/ts/jobs/JobLogger.std.ts b/ts/jobs/JobLogger.std.ts
index b02025a373..5627233dfb 100644
--- a/ts/jobs/JobLogger.std.ts
+++ b/ts/jobs/JobLogger.std.ts
@@ -5,47 +5,49 @@ import type { LoggerType } from '../types/Logging.std.ts';
import type { ParsedJob } from './types.std.ts';
export class JobLogger implements LoggerType {
- #id: string;
- #queueType: string;
+ readonly #id: string;
+ readonly #queueType: string;
+ readonly #logger: LoggerType;
public attempt = -1;
constructor(
job: Readonly, 'id' | 'queueType'>>,
- private logger: LoggerType
+ logger: LoggerType
) {
this.#id = job.id;
this.#queueType = job.queueType;
+ this.#logger = logger;
}
fatal(...args: ReadonlyArray): void {
- this.logger.fatal(this.#prefix(), ...args);
+ this.#logger.fatal(this.#prefix(), ...args);
}
error(...args: ReadonlyArray): void {
- this.logger.error(this.#prefix(), ...args);
+ this.#logger.error(this.#prefix(), ...args);
}
warn(...args: ReadonlyArray): void {
- this.logger.warn(this.#prefix(), ...args);
+ this.#logger.warn(this.#prefix(), ...args);
}
info(...args: ReadonlyArray): void {
- this.logger.info(this.#prefix(), ...args);
+ this.#logger.info(this.#prefix(), ...args);
}
debug(...args: ReadonlyArray): void {
- this.logger.debug(this.#prefix(), ...args);
+ this.#logger.debug(this.#prefix(), ...args);
}
trace(...args: ReadonlyArray): void {
- this.logger.trace(this.#prefix(), ...args);
+ this.#logger.trace(this.#prefix(), ...args);
}
child(name: string): JobLogger {
return new JobLogger(
{ id: this.#id, queueType: this.#queueType },
- this.logger.child(name)
+ this.#logger.child(name)
);
}
diff --git a/ts/jobs/JobManager.std.ts b/ts/jobs/JobManager.std.ts
index fe8f1dd190..fa96bb5b73 100644
--- a/ts/jobs/JobManager.std.ts
+++ b/ts/jobs/JobManager.std.ts
@@ -78,16 +78,25 @@ export type ActiveJobData = {
};
export abstract class JobManager {
- #enabled: boolean = false;
- #activeJobs: Map> = new Map();
- #jobStartPromises: Map> = new Map();
- #jobCompletePromises: Map> = new Map();
+ protected readonly params: JobManagerParamsType;
+ #enabled = false;
+ readonly #activeJobs = new Map>();
+ readonly #jobStartPromises = new Map<
+ string,
+ ExplodePromiseResultType
+ >();
+ readonly #jobCompletePromises = new Map<
+ string,
+ ExplodePromiseResultType
+ >();
#tickTimeout: NodeJS.Timeout | null = null;
#idleCallbacks = new Array<() => void>();
protected logPrefix = 'JobManager';
public tickInterval = DEFAULT_TICK_INTERVAL;
- constructor(readonly params: JobManagerParamsType) {}
+ constructor(params: JobManagerParamsType) {
+ this.params = params;
+ }
async start(): Promise {
log.info(`${this.logPrefix}: starting`);
diff --git a/ts/jobs/JobQueueDatabaseStore.preload.ts b/ts/jobs/JobQueueDatabaseStore.preload.ts
index d949ca577d..53969cdfe7 100644
--- a/ts/jobs/JobQueueDatabaseStore.preload.ts
+++ b/ts/jobs/JobQueueDatabaseStore.preload.ts
@@ -20,11 +20,14 @@ type Database = {
};
export class JobQueueDatabaseStore implements JobQueueStore {
- #activeQueueTypes = new Set();
- #queues = new Map>();
- #initialFetchPromises = new Map>();
+ readonly #db: Database;
+ readonly #activeQueueTypes = new Set();
+ readonly #queues = new Map>();
+ readonly #initialFetchPromises = new Map>();
- constructor(private readonly db: Database) {}
+ constructor(db: Database) {
+ this.#db = db;
+ }
async insert(
job: Readonly,
@@ -42,7 +45,7 @@ export class JobQueueDatabaseStore implements JobQueueStore {
}
if (shouldPersist) {
- await this.db.insertJob(formatJobForInsert(job));
+ await this.#db.insertJob(formatJobForInsert(job));
}
if (initialFetchPromise) {
@@ -51,7 +54,7 @@ export class JobQueueDatabaseStore implements JobQueueStore {
}
async delete(id: string): Promise {
- await this.db.deleteJob(id);
+ await this.#db.deleteJob(id);
}
stream(queueType: string): AsyncIterable {
@@ -90,7 +93,7 @@ export class JobQueueDatabaseStore implements JobQueueStore {
});
this.#initialFetchPromises.set(queueType, initialFetchPromise);
- const result = await this.db.getJobsInQueue(queueType);
+ const result = await this.#db.getJobsInQueue(queueType);
log.info(
`finished fetching existing ${
result.length
diff --git a/ts/jobs/callLinkRefreshJobQueue.preload.ts b/ts/jobs/callLinkRefreshJobQueue.preload.ts
index d5d2e88e06..225cdc1126 100644
--- a/ts/jobs/callLinkRefreshJobQueue.preload.ts
+++ b/ts/jobs/callLinkRefreshJobQueue.preload.ts
@@ -50,7 +50,7 @@ export type CallLinkRefreshJobData = z.infer<
>;
export class CallLinkRefreshJobQueue extends JobQueue {
- #parallelQueue = new PQueue({ concurrency: MAX_PARALLEL_JOBS });
+ readonly #parallelQueue = new PQueue({ concurrency: MAX_PARALLEL_JOBS });
readonly #pendingCallLinks = new Map();
protected override getQueues(): ReadonlySet {
diff --git a/ts/jobs/helpers/attachmentBackfill.preload.ts b/ts/jobs/helpers/attachmentBackfill.preload.ts
index 847c95a588..24f608faa2 100644
--- a/ts/jobs/helpers/attachmentBackfill.preload.ts
+++ b/ts/jobs/helpers/attachmentBackfill.preload.ts
@@ -67,7 +67,7 @@ function isBackfillEnabled(): boolean {
}
export class AttachmentBackfill {
- #pendingRequests = new Map<
+ readonly #pendingRequests = new Map<
ReadonlyMessageAttributesType['id'],
NodeJS.Timeout
>();
diff --git a/ts/jobs/helpers/sendDeleteStoryForEveryone.preload.ts b/ts/jobs/helpers/sendDeleteStoryForEveryone.preload.ts
index b3d0dd6cbb..308775838e 100644
--- a/ts/jobs/helpers/sendDeleteStoryForEveryone.preload.ts
+++ b/ts/jobs/helpers/sendDeleteStoryForEveryone.preload.ts
@@ -255,7 +255,7 @@ export async function sendDeleteStoryForEveryone(
function doesMessageHaveSuccessfulSends(message: MessageModel): boolean {
const map = message.get('deletedForEveryoneSendStatus') ?? {};
- return Object.values(map).some(value => value === true);
+ return Object.values(map).includes(true);
}
async function updateMessageWithSuccessfulSends(
diff --git a/ts/jobs/singleProtoJobQueue.preload.ts b/ts/jobs/singleProtoJobQueue.preload.ts
index 37ad32284f..53a447eb6d 100644
--- a/ts/jobs/singleProtoJobQueue.preload.ts
+++ b/ts/jobs/singleProtoJobQueue.preload.ts
@@ -35,7 +35,7 @@ const MAX_PARALLEL_JOBS = 5;
const MAX_ATTEMPTS = exponentialBackoffMaxAttempts(MAX_RETRY_TIME);
export class SingleProtoJobQueue extends JobQueue {
- #parallelQueue = new PQueue({ concurrency: MAX_PARALLEL_JOBS });
+ readonly #parallelQueue = new PQueue({ concurrency: MAX_PARALLEL_JOBS });
protected override getQueues(): ReadonlySet {
return new Set([this.#parallelQueue]);
diff --git a/ts/linkPreviews/linkPreviewFetch.preload.ts b/ts/linkPreviews/linkPreviewFetch.preload.ts
index 328fb72918..1e09d95057 100644
--- a/ts/linkPreviews/linkPreviewFetch.preload.ts
+++ b/ts/linkPreviews/linkPreviewFetch.preload.ts
@@ -44,7 +44,7 @@ const MIN_HTML_CONTENT_LENGTH = 8;
// Similar to the above. We don't want to show tiny images (even though the more likely
// case is that the Content-Length is 0).
const MIN_IMAGE_CONTENT_LENGTH = 8;
-const VALID_IMAGE_MIME_TYPES: Set = new Set([
+const VALID_IMAGE_MIME_TYPES = new Set([
IMAGE_GIF,
IMAGE_ICO,
IMAGE_JPEG,
diff --git a/ts/logging/log.std.ts b/ts/logging/log.std.ts
index d710f3844a..b6c7e5b230 100644
--- a/ts/logging/log.std.ts
+++ b/ts/logging/log.std.ts
@@ -155,6 +155,7 @@ function debugLog(
// `fatal` has no respective analog in `console`
console[consoleMethod === 'fatal' ? 'error' : consoleMethod](
+ // oxlint-disable-next-line typescript/restrict-template-expressions
`%c${msgPrefix ?? ''}%c${message}`,
`color: ${color}; font-weight: bold`,
'color: inherit; font-weight: inherit',
diff --git a/ts/logging/main_process_logging.main.ts b/ts/logging/main_process_logging.main.ts
index 7b6c2f3fd4..5998435104 100644
--- a/ts/logging/main_process_logging.main.ts
+++ b/ts/logging/main_process_logging.main.ts
@@ -49,7 +49,7 @@ export async function initialize(
if (level >= LogLevel.Error) {
getMainWindow()?.webContents.send(
'logging-error',
- `${msgPrefix ? `${msgPrefix}` : ''}${logLine}`
+ `${msgPrefix ?? ''}${logLine}`
);
}
});
diff --git a/ts/logging/set_up_renderer_logging.preload.ts b/ts/logging/set_up_renderer_logging.preload.ts
index 7f25a8a8a0..0eb2ec5011 100644
--- a/ts/logging/set_up_renderer_logging.preload.ts
+++ b/ts/logging/set_up_renderer_logging.preload.ts
@@ -49,6 +49,7 @@ export function initialize(): void {
window.onerror = (message, source, line, column, error) => {
const errorInfo = Errors.toLogFormat(error);
log.error(
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
`Top-level unhandled error: ${message}, ${errorInfo}`,
Errors.toLocation(source, line, column)
);
@@ -90,6 +91,7 @@ initLogger(
} else if (level === SignalClientLogLevel.Error) {
libSignalLog.error(logString);
} else {
+ // oxlint-disable-next-line typescript/restrict-template-expressions
libSignalLog.error(`${logString} (unknown log level ${level})`);
}
}
diff --git a/ts/logging/uploadDebugLog.node.ts b/ts/logging/uploadDebugLog.node.ts
index b167fab6f7..063b722ae9 100644
--- a/ts/logging/uploadDebugLog.node.ts
+++ b/ts/logging/uploadDebugLog.node.ts
@@ -74,6 +74,7 @@ export const upload = async ({
});
const { fields, url } = parseTokenBody(signedForm.body);
+ // oxlint-disable-next-line typescript/restrict-template-expressions
const uploadKey = `${fields.key}.${extension}`;
const form = new FormData();
diff --git a/ts/main/challengeMain.main.ts b/ts/main/challengeMain.main.ts
index ff491e0c70..ba6aa1fa4f 100644
--- a/ts/main/challengeMain.main.ts
+++ b/ts/main/challengeMain.main.ts
@@ -16,7 +16,7 @@ const log = createLogger('challengeMain');
export class ChallengeMainHandler {
#handlers: Array<(response: ChallengeResponse) => void> = [];
- #hardcodedResult?: ChallengeResponse;
+ readonly #hardcodedResult?: ChallengeResponse;
constructor(hardcodedResult?: string) {
if (hardcodedResult && getEnvironment() === Environment.Development) {
diff --git a/ts/main/powerChannel.main.ts b/ts/main/powerChannel.main.ts
index 646c30edf6..6d9b18e5e1 100644
--- a/ts/main/powerChannel.main.ts
+++ b/ts/main/powerChannel.main.ts
@@ -7,14 +7,14 @@ export type InitializeOptions = {
send(event: string): void;
};
-export class PowerChannel {
- private static isInitialized = false;
+export namespace PowerChannel {
+ let isInitialized = false;
- static initialize({ send }: InitializeOptions): void {
- if (PowerChannel.isInitialized) {
+ export function initialize({ send }: InitializeOptions): void {
+ if (isInitialized) {
throw new Error('PowerChannel already initialized');
}
- PowerChannel.isInitialized = true;
+ isInitialized = true;
powerMonitor.on('suspend', () => {
send('power-channel:suspend');
diff --git a/ts/mediaEditor/MediaEditorFabricCropRect.dom.ts b/ts/mediaEditor/MediaEditorFabricCropRect.dom.ts
index 40b827db05..228bb6899b 100644
--- a/ts/mediaEditor/MediaEditorFabricCropRect.dom.ts
+++ b/ts/mediaEditor/MediaEditorFabricCropRect.dom.ts
@@ -21,7 +21,7 @@ export class MediaEditorFabricCropRect extends fabric.Rect {
this.on('moving', this.#containBounds);
}
- #containBounds = () => {
+ readonly #containBounds = () => {
if (!this.canvas) {
return;
}
diff --git a/ts/messageModifiers/AttachmentDownloads.preload.ts b/ts/messageModifiers/AttachmentDownloads.preload.ts
index 9dc79b69b3..de9815917c 100644
--- a/ts/messageModifiers/AttachmentDownloads.preload.ts
+++ b/ts/messageModifiers/AttachmentDownloads.preload.ts
@@ -70,8 +70,11 @@ export async function markAttachmentAsCorrupted(
}
export class AttachmentNotNeededForMessageError extends Error {
- constructor(public readonly attachment: AttachmentType) {
+ public readonly attachment: AttachmentType;
+
+ constructor(attachment: AttachmentType) {
super('AttachmentNotNeededForMessageError');
+ this.attachment = attachment;
}
}
diff --git a/ts/messageModifiers/MessageReceipts.preload.ts b/ts/messageModifiers/MessageReceipts.preload.ts
index ccc77c250c..a98610dc33 100644
--- a/ts/messageModifiers/MessageReceipts.preload.ts
+++ b/ts/messageModifiers/MessageReceipts.preload.ts
@@ -81,10 +81,10 @@ const processReceiptBatcher = createWaitBatcher({
// Once we find the message, we'll group them by messageId to process
// all receipts for a given message
- const receiptsByMessageId: Map<
+ const receiptsByMessageId = new Map<
string,
Array
- > = new Map();
+ >();
function addReceiptAndTargetMessage(
message: MessageModel,
diff --git a/ts/messageModifiers/ReadSyncs.preload.ts b/ts/messageModifiers/ReadSyncs.preload.ts
index 82303897bf..b6d02bab8f 100644
--- a/ts/messageModifiers/ReadSyncs.preload.ts
+++ b/ts/messageModifiers/ReadSyncs.preload.ts
@@ -63,7 +63,7 @@ async function maybeItIsAReactionReadSync(
const readReaction = await DataWriter.markReactionAsRead(
readSync.senderAci,
- Number(readSync.timestamp)
+ readSync.timestamp
);
if (
diff --git a/ts/messages/MessageSendState.std.ts b/ts/messages/MessageSendState.std.ts
index 439b82ca32..6e901829f2 100644
--- a/ts/messages/MessageSendState.std.ts
+++ b/ts/messages/MessageSendState.std.ts
@@ -249,12 +249,11 @@ const summarizeMessageSendStatuses = memoizee(
statusesWithOnlyOneConversationId: Map;
length: number;
} => {
- const statuses: Set = new Set();
+ const statuses = new Set();
// We keep track of statuses with only one conversationId associated with it
// so that we can ignore a status if it is only for ourConversationId, as needed
- const statusesWithOnlyOneConversationId: Map =
- new Map();
+ const statusesWithOnlyOneConversationId = new Map();
const entries = Object.entries(sendStateByConversationId);
diff --git a/ts/messages/handleDataMessage.preload.ts b/ts/messages/handleDataMessage.preload.ts
index 4fc9ec94ec..1895ab1018 100644
--- a/ts/messages/handleDataMessage.preload.ts
+++ b/ts/messages/handleDataMessage.preload.ts
@@ -255,7 +255,7 @@ export async function handleDataMessage(
initialMessage.groupV2.publicParams
) {
// Repair core GroupV2 data if needed
- await conversation.maybeRepairGroupV2({
+ conversation.maybeRepairGroupV2({
masterKey: initialMessage.groupV2.masterKey,
secretParams: initialMessage.groupV2.secretParams,
publicParams: initialMessage.groupV2.publicParams,
@@ -541,7 +541,7 @@ export async function handleDataMessage(
}
const ourPni = itemStorage.user.getCheckedPni();
- const ourServiceIds: Set = new Set([ourAci, ourPni]);
+ const ourServiceIds = new Set([ourAci, ourPni]);
// oxlint-disable-next-line no-param-reassign
message = window.MessageCache.register(message);
@@ -792,7 +792,7 @@ export async function handleDataMessage(
`${idLog}: gift badge with level ${level} not found on server`
);
} else {
- await window.reduxActions.badges.updateOrCreate([badge]);
+ window.reduxActions.badges.updateOrCreate([badge]);
giftBadge.id = badge.id;
}
}
diff --git a/ts/messages/helpers.std.ts b/ts/messages/helpers.std.ts
index 2c30414a2d..db0e4001f4 100644
--- a/ts/messages/helpers.std.ts
+++ b/ts/messages/helpers.std.ts
@@ -60,7 +60,7 @@ export const shouldTryToCopyFromQuotedMessage = ({
quoteAttachment: ReadonlyDeep | undefined;
}): boolean => {
// If we've tried and can't find the message, try again.
- if (referencedMessageNotFound === true) {
+ if (referencedMessageNotFound) {
return true;
}
diff --git a/ts/model-types.d.ts b/ts/model-types.d.ts
index fd2b771c00..43c4cfa04d 100644
--- a/ts/model-types.d.ts
+++ b/ts/model-types.d.ts
@@ -121,6 +121,7 @@ type StoryReplyContextType = {
export type GroupV1Update = {
avatarUpdated?: boolean;
joined?: ReadonlyArray;
+ // oxlint-disable-next-line typescript/no-redundant-type-constituents
left?: string | 'You';
name?: string;
};
diff --git a/ts/models/conversations.preload.ts b/ts/models/conversations.preload.ts
index fa5a6d8d5b..0619268085 100644
--- a/ts/models/conversations.preload.ts
+++ b/ts/models/conversations.preload.ts
@@ -362,9 +362,9 @@ export class ConversationModel {
#lastIsTyping?: boolean;
#muteTimer?: NodeJS.Timeout;
- #privVerifiedEnum?: typeof signalProtocolStore.VerifiedStatus;
+ readonly #privVerifiedEnum?: typeof signalProtocolStore.VerifiedStatus;
#isShuttingDown = false;
- #savePromises = new Set>();
+ readonly #savePromises = new Set>();
public get id(): string {
return this.#_attributes.id;
@@ -593,7 +593,7 @@ export class ConversationModel {
): Promise {
const idLog = this.idForLogging();
const current = this.get('expireTimer');
- const bothFalsey = Boolean(current) === false && Boolean(seconds) === false;
+ const bothFalsey = !current && !seconds;
if (current === seconds || bothFalsey) {
log.warn(
@@ -4100,8 +4100,8 @@ export class ConversationModel {
const { clearUnreadMetrics } = window.reduxActions.conversations;
clearUnreadMetrics(this.id);
- const enabledProfileSharing = Boolean(!this.get('profileSharing'));
- const unarchivedConversation = Boolean(this.get('isArchived'));
+ const enabledProfileSharing = !this.get('profileSharing');
+ const unarchivedConversation = this.get('isArchived');
log.info(
`beforeMessageSend(${this.idForLogging()}): ` +
@@ -4619,12 +4619,12 @@ export class ConversationModel {
}
setMarkedUnread(markedUnread: boolean): void {
- const previousMarkedUnread = this.get('markedUnread');
+ const previousMarkedUnread = this.get('markedUnread') ?? false;
this.set({ markedUnread });
drop(DataWriter.updateConversation(this.attributes));
- if (Boolean(previousMarkedUnread) !== Boolean(markedUnread)) {
+ if (previousMarkedUnread !== markedUnread) {
this.captureChange('markedUnread');
}
}
@@ -5089,9 +5089,7 @@ export class ConversationModel {
}
const ourGroups =
- await window.ConversationController.getAllGroupsInvolvingServiceId(
- ourAci
- );
+ window.ConversationController.getAllGroupsInvolvingServiceId(ourAci);
return ourGroups
.filter(c => c.hasMember(ourAci) && c.hasMember(theirAci))
.sort(
diff --git a/ts/quill/emoji/completion.dom.tsx b/ts/quill/emoji/completion.dom.tsx
index 61a119534d..8e4c5276db 100644
--- a/ts/quill/emoji/completion.dom.tsx
+++ b/ts/quill/emoji/completion.dom.tsx
@@ -231,9 +231,9 @@ export class EmojiCompletion {
getAttributesForInsert(index: number): Record {
const character = index > 0 ? index - 1 : 0;
const contents = this.quill.getContents(character, 1);
- return contents.ops.reduce(
- (acc, op) => ({ acc, ...op.attributes }),
- {} as Record
+ return contents.ops.reduce>(
+ (acc, op) => ({ ...acc, ...op.attributes }),
+ {}
);
}
diff --git a/ts/quill/memberRepository.std.ts b/ts/quill/memberRepository.std.ts
index b7d38257c8..ea2e46af30 100644
--- a/ts/quill/memberRepository.std.ts
+++ b/ts/quill/memberRepository.std.ts
@@ -73,7 +73,7 @@ export class MemberRepository {
#rawMembers: ReadonlyArray;
#members: ReadonlyArray | null = null;
#isFuseReady = false;
- #fuse = new Fuse([], FUSE_OPTIONS);
+ readonly #fuse = new Fuse([], FUSE_OPTIONS);
constructor(conversations: ReadonlyArray = []) {
this.#rawMembers = conversations;
diff --git a/ts/quill/mentions/completion.dom.tsx b/ts/quill/mentions/completion.dom.tsx
index 6cf6f74598..7877eeb338 100644
--- a/ts/quill/mentions/completion.dom.tsx
+++ b/ts/quill/mentions/completion.dom.tsx
@@ -203,9 +203,9 @@ export class MentionCompletion {
getAttributesForInsert(index: number): Record {
const character = index > 0 ? index - 1 : 0;
const contents = this.quill.getContents(character, 1);
- return contents.ops.reduce(
- (acc, op) => ({ acc, ...op.attributes }),
- {} as Record
+ return contents.ops.reduce>(
+ (acc, op) => ({ ...acc, ...op.attributes }),
+ {}
);
}
diff --git a/ts/quill/signal-clipboard/util.dom.ts b/ts/quill/signal-clipboard/util.dom.ts
index fdeb2d0d0c..430c061297 100644
--- a/ts/quill/signal-clipboard/util.dom.ts
+++ b/ts/quill/signal-clipboard/util.dom.ts
@@ -192,7 +192,8 @@ function getRangeWithContainer(range: Range): Node {
return fragment;
}
- currentNode = startContainer.parentElement as HTMLElement;
+ // oxlint-disable-next-line typescript/no-non-null-assertion
+ currentNode = startContainer.parentElement!;
while (
currentNode &&
CONTAINER_CLASSES.every(item => !currentNode?.classList.contains(item))
diff --git a/ts/quill/util.dom.ts b/ts/quill/util.dom.ts
index ce686eac5e..ce30d1f409 100644
--- a/ts/quill/util.dom.ts
+++ b/ts/quill/util.dom.ts
@@ -195,6 +195,7 @@ export const getTextAndRangesFromOps = (
const preTrimText = ops.reduce((acc, op) => {
// We special-case all-newline ops because Quill doesn't apply styles to them
if (isNewlineOnlyOp(op)) {
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-plus-operands
return acc + op.insert;
}
@@ -459,7 +460,7 @@ export const insertEmojiOps = (
incomingOps: ReadonlyArray,
existingAttributes: AttributeMap
): Array => {
- return incomingOps.reduce((ops, op) => {
+ return incomingOps.reduce>((ops, op) => {
if (typeof op.insert === 'string') {
const text = op.insert;
const { attributes } = op;
@@ -493,5 +494,5 @@ export const insertEmojiOps = (
}
return ops;
- }, [] as Array);
+ }, []);
};
diff --git a/ts/reactions/util.std.ts b/ts/reactions/util.std.ts
index 26a0238670..db53266e19 100644
--- a/ts/reactions/util.std.ts
+++ b/ts/reactions/util.std.ts
@@ -27,8 +27,7 @@ const isOutgoingReactionPending = negate(isOutgoingReactionFullySent);
const isOutgoingReactionCompletelyUnsent = ({
isSentByConversationId = {},
}: Readonly>): boolean => {
- const sendStates = Object.values(isSentByConversationId);
- return sendStates.length > 0 && sendStates.every(state => state === false);
+ return !Object.values(isSentByConversationId).includes(true);
};
export function addOutgoingReaction(
diff --git a/ts/routineProfileRefresh.preload.ts b/ts/routineProfileRefresh.preload.ts
index c2b19d5fb7..2c8faf4245 100644
--- a/ts/routineProfileRefresh.preload.ts
+++ b/ts/routineProfileRefresh.preload.ts
@@ -29,17 +29,19 @@ const MIN_REFRESH_DELAY = MINUTE;
let idCounter = 1;
-export class RoutineProfileRefresher {
- #started = false;
- #id: number;
+type Options = Readonly<{
+ getAllConversations: () => ReadonlyArray;
+ getOurConversationId: () => string | undefined;
+ storage: Pick;
+}>;
- constructor(
- private readonly options: {
- getAllConversations: () => ReadonlyArray;
- getOurConversationId: () => string | undefined;
- storage: Pick;
- }
- ) {
+export class RoutineProfileRefresher {
+ readonly #options: Options;
+ #started = false;
+ readonly #id: number;
+
+ constructor(options: Options) {
+ this.#options = options;
// We keep track of how many of these classes we create, because we suspect that
// there might be too many...
idCounter += 1;
@@ -58,7 +60,8 @@ export class RoutineProfileRefresher {
}
this.#started = true;
- const { storage, getAllConversations, getOurConversationId } = this.options;
+ const { storage, getAllConversations, getOurConversationId } =
+ this.#options;
// oxlint-disable-next-line no-constant-condition
while (true) {
diff --git a/ts/services/ActiveWindowService.std.ts b/ts/services/ActiveWindowService.std.ts
index 38572047fe..40d2e79501 100644
--- a/ts/services/ActiveWindowService.std.ts
+++ b/ts/services/ActiveWindowService.std.ts
@@ -36,7 +36,7 @@ class ActiveWindowService {
#lastActiveEventAt = -Infinity;
#lastActiveNonFocusingEventAt = -Infinity;
#lastBlurredAt = -Infinity;
- #callActiveCallbacks: () => void;
+ readonly #callActiveCallbacks: () => void;
constructor() {
this.#callActiveCallbacks = throttle(() => {
diff --git a/ts/services/BeforeNavigate.std.ts b/ts/services/BeforeNavigate.std.ts
index 22d3ff4e69..240c9cb38a 100644
--- a/ts/services/BeforeNavigate.std.ts
+++ b/ts/services/BeforeNavigate.std.ts
@@ -33,7 +33,7 @@ export type BeforeNavigateEntry = {
};
export class BeforeNavigateService {
- #beforeNavigateCallbacks = new Set();
+ readonly #beforeNavigateCallbacks = new Set();
private findMatchingEntry(
entry: BeforeNavigateEntry
diff --git a/ts/services/MessageCache.preload.ts b/ts/services/MessageCache.preload.ts
index 2515b5997c..fd0f84713b 100644
--- a/ts/services/MessageCache.preload.ts
+++ b/ts/services/MessageCache.preload.ts
@@ -29,7 +29,7 @@ export class MessageCache {
return instance;
}
- #state = {
+ readonly #state = {
messages: new Map(),
messageIdsBySentAt: new Map>(),
lastAccessedAt: new Map(),
@@ -300,7 +300,7 @@ export class MessageCache {
);
}
- #throttledReduxUpdaters = new LRUCache<
+ readonly #throttledReduxUpdaters = new LRUCache<
string,
(attributes: MessageAttributesType) => void
>({
diff --git a/ts/services/ZoomFactorService.main.ts b/ts/services/ZoomFactorService.main.ts
index 1a24ace392..8f8f72fdf4 100644
--- a/ts/services/ZoomFactorService.main.ts
+++ b/ts/services/ZoomFactorService.main.ts
@@ -31,7 +31,7 @@ type ZoomFactorServiceConfig = Readonly<{
}>;
export class ZoomFactorService extends EventEmitter {
- #config: ZoomFactorServiceConfig;
+ readonly #config: ZoomFactorServiceConfig;
#cachedZoomFactor: number | null = null;
#isListeningForZoom = false;
diff --git a/ts/services/audioRecorder.dom.ts b/ts/services/audioRecorder.dom.ts
index cc56ef4775..954e47d701 100644
--- a/ts/services/audioRecorder.dom.ts
+++ b/ts/services/audioRecorder.dom.ts
@@ -55,6 +55,7 @@ export class RecorderClass {
return false;
}
+ // oxlint-disable-next-line typescript/await-thenable
await window.reduxActions.globalModals.ensureSystemMediaPermissions(
'microphone',
'voiceNote'
diff --git a/ts/services/backups/api.preload.ts b/ts/services/backups/api.preload.ts
index 46e316f7b5..74adcf7b8f 100644
--- a/ts/services/backups/api.preload.ts
+++ b/ts/services/backups/api.preload.ts
@@ -54,17 +54,20 @@ export type EphemeralDownloadOptionsType = Readonly<{
DownloadOptionsType;
export class BackupAPI {
- #cachedBackupInfo = new Map<
+ readonly #credentials: BackupCredentials;
+ readonly #cachedBackupInfo = new Map<
BackupCredentialType,
GetBackupInfoResponseType
>();
- constructor(private readonly credentials: BackupCredentials) {}
+ constructor(credentials: BackupCredentials) {
+ this.#credentials = credentials;
+ }
public async refresh(): Promise {
const headers = await Promise.all(
[BackupCredentialType.Messages, BackupCredentialType.Media].map(type =>
- this.credentials.getHeadersForToday(type)
+ this.#credentials.getHeadersForToday(type)
)
);
await Promise.all(headers.map(h => refreshBackup(h)));
@@ -74,7 +77,7 @@ export class BackupAPI {
credentialType: BackupCredentialType
): Promise {
const backupInfo = await getBackupInfo(
- await this.credentials.getHeadersForToday(credentialType)
+ await this.#credentials.getHeadersForToday(credentialType)
);
this.#cachedBackupInfo.set(credentialType, backupInfo);
return backupInfo;
@@ -101,7 +104,7 @@ export class BackupAPI {
public async upload(filePath: string, fileSize: number): Promise {
const form = await getBackupUploadForm(
- await this.credentials.getHeadersForToday(BackupCredentialType.Messages)
+ await this.#credentials.getHeadersForToday(BackupCredentialType.Messages)
);
await uploadFile({
@@ -119,7 +122,7 @@ export class BackupAPI {
const { cdn, backupDir, backupName } = await this.getInfo(
BackupCredentialType.Messages
);
- const { headers } = await this.credentials.getCDNReadCredentials(
+ const { headers } = await this.#credentials.getCDNReadCredentials(
cdn,
BackupCredentialType.Messages
);
@@ -142,7 +145,7 @@ export class BackupAPI {
const { cdn, backupDir, backupName } = await this.#getCachedInfo(
BackupCredentialType.Messages
);
- const { headers } = await this.credentials.getCDNReadCredentials(
+ const { headers } = await this.#credentials.getCDNReadCredentials(
cdn,
BackupCredentialType.Messages
);
@@ -157,7 +160,7 @@ export class BackupAPI {
return { backupExists: true, size, createdAt };
} catch (error) {
if (error instanceof HTTPError && error.code === 401) {
- this.credentials.onCdnCredentialError();
+ this.#credentials.onCdnCredentialError();
} else if (error instanceof HTTPError && error.code === 404) {
return { backupExists: false };
}
@@ -190,7 +193,7 @@ export class BackupAPI {
public async getMediaUploadForm(): Promise {
return getBackupMediaUploadForm(
- await this.credentials.getHeadersForToday(BackupCredentialType.Media)
+ await this.#credentials.getHeadersForToday(BackupCredentialType.Media)
);
}
@@ -198,7 +201,7 @@ export class BackupAPI {
items: ReadonlyArray
): Promise {
return doBackupMediaBatch({
- headers: await this.credentials.getHeadersForToday(
+ headers: await this.#credentials.getHeadersForToday(
BackupCredentialType.Media
),
items,
@@ -213,7 +216,7 @@ export class BackupAPI {
limit: number;
}): Promise {
return backupListMedia({
- headers: await this.credentials.getHeadersForToday(
+ headers: await this.#credentials.getHeadersForToday(
BackupCredentialType.Media
),
cursor,
diff --git a/ts/services/backups/credentials.preload.ts b/ts/services/backups/credentials.preload.ts
index 23f0dd2b7b..5cb883b7db 100644
--- a/ts/services/backups/credentials.preload.ts
+++ b/ts/services/backups/credentials.preload.ts
@@ -65,7 +65,7 @@ const BACKUP_CDN_READ_CREDENTIALS_VALID_DURATION = 12 * HOUR;
export class BackupCredentials {
#activeFetch: Promise> | undefined;
- #scheduler = new CheckScheduler({
+ readonly #scheduler = new CheckScheduler({
name: 'BackupCredentials',
interval: 3 * DAY,
storageKey: 'backupCombinedCredentialsLastRequestTime',
diff --git a/ts/services/backups/errors.std.ts b/ts/services/backups/errors.std.ts
index e12932719b..35cb7a7c4d 100644
--- a/ts/services/backups/errors.std.ts
+++ b/ts/services/backups/errors.std.ts
@@ -3,11 +3,11 @@
import { InstallScreenBackupError } from '../../types/InstallScreen.std.ts';
export class BackupInstallerError extends Error {
- constructor(
- name: string,
- public readonly installerError: InstallScreenBackupError
- ) {
+ public readonly installerError: InstallScreenBackupError;
+
+ constructor(name: string, installerError: InstallScreenBackupError) {
super(name);
+ this.installerError = installerError;
}
}
diff --git a/ts/services/backups/export.preload.ts b/ts/services/backups/export.preload.ts
index 2d1d42d27e..22183574cb 100644
--- a/ts/services/backups/export.preload.ts
+++ b/ts/services/backups/export.preload.ts
@@ -266,9 +266,14 @@ type NonBubbleResultType = Readonly<
}
>;
+type Options = Readonly & {
+ validationRun?: boolean;
+};
+
export class BackupExportStream extends Readable {
+ readonly #options: Options;
// Shared between all methods for consistency.
- #now = Date.now();
+ readonly #now = Date.now();
readonly #backupTimeMs = getSafeLongFromTimestamp(this.#now);
readonly #convoIdToRecipientId = new Map();
@@ -307,14 +312,11 @@ export class BackupExportStream extends Readable {
// Map from custom color uuid to an index in accountSettings.customColors
// array.
- #customColorIdByUuid = new Map();
+ readonly #customColorIdByUuid = new Map();
- constructor(
- private readonly options: Readonly & {
- validationRun?: boolean;
- }
- ) {
+ constructor(options: Options) {
super();
+ this.#options = options;
}
async #cleanupAfterError() {
@@ -345,7 +347,7 @@ export class BackupExportStream extends Readable {
await this.#unsafeRun();
await resumeWriteAccess();
// TODO (DESKTOP-7344): Clear & add backup jobs in a single transaction
- const { type } = this.options;
+ const { type } = this.#options;
switch (type) {
case 'remote':
log.info(
@@ -417,7 +419,7 @@ export class BackupExportStream extends Readable {
debugInfo: null,
};
- if (this.options.type === 'plaintext-export') {
+ if (this.#options.type === 'plaintext-export') {
const { exporter, chunk: initialChunk } = BackupJsonExporter.start(
Backups.BackupInfo.encode(backupInfo),
{ validate: false }
@@ -876,7 +878,7 @@ export class BackupExportStream extends Readable {
this.#stats.messages += 1;
if (
- this.options.validationRun ||
+ this.#options.validationRun ||
this.#stats.messages % FLUSH_EVERY === 0
) {
// flush every chatItem to expose all validation errors
@@ -912,7 +914,7 @@ export class BackupExportStream extends Readable {
#pushFrame(frame: Backups.Frame.Params['item']): void {
const encodedFrame = Backups.Frame.encode({ item: frame });
- if (this.options.type === 'plaintext-export') {
+ if (this.#options.type === 'plaintext-export') {
const delimitedFrame = Buffer.concat(encodeDelimited(encodedFrame));
strictAssert(
this.#jsonExporter != null,
@@ -1586,7 +1588,7 @@ export class BackupExportStream extends Readable {
}
if (message.expireTimer) {
- if (this.options.type === 'plaintext-export') {
+ if (this.#options.type === 'plaintext-export') {
// All disappearing messages are excluded in plaintext export
return undefined;
}
@@ -3326,15 +3328,15 @@ export class BackupExportStream extends Readable {
}): Promise {
const { filePointer, backupJob } = await getFilePointerForAttachment({
attachment,
- backupOptions: this.options,
+ backupOptions: this.#options,
messageReceivedAt,
getBackupCdnInfo,
});
let mediaName: string | undefined;
if (
- this.options.type === 'local-encrypted' ||
- this.options.type === 'plaintext-export'
+ this.#options.type === 'local-encrypted' ||
+ this.#options.type === 'plaintext-export'
) {
if (hasRequiredInformationForLocalBackup(attachment)) {
mediaName = getLocalBackupFileNameForAttachment(attachment);
@@ -3688,7 +3690,7 @@ export class BackupExportStream extends Readable {
// Integration tests use the 'link-and-sync' version of export, which will include
// view-once attachments
const shouldIncludeAttachments =
- this.options.type !== 'plaintext-export' && isTestOrMockEnvironment();
+ this.#options.type !== 'plaintext-export' && isTestOrMockEnvironment();
return {
attachment:
!shouldIncludeAttachments || attachment == null
diff --git a/ts/services/backups/import.preload.ts b/ts/services/backups/import.preload.ts
index 977f237c6c..c17523bb33 100644
--- a/ts/services/backups/import.preload.ts
+++ b/ts/services/backups/import.preload.ts
@@ -256,7 +256,8 @@ function addressToContactAddressType(
}
export class BackupImportStream extends Writable {
- #now = Date.now();
+ readonly #options: BackupImportOptions;
+ readonly #now = Date.now();
#parsedBackupInfo = false;
#logId = 'BackupImportStream(unknown)';
#aboutMe: AboutMe | undefined;
@@ -280,16 +281,17 @@ export class BackupImportStream extends Writable {
#flushMessagesPromise: Promise | undefined;
readonly #stickerPacks = new Array();
#ourConversation?: ConversationAttributesType;
- #pinnedConversations = new Array<[number, string]>();
- #customColorById = new Map();
+ readonly #pinnedConversations = new Array<[number, string]>();
+ readonly #customColorById = new Map();
#releaseNotesRecipientId: bigint | undefined;
#releaseNotesChatId: bigint | undefined;
- #pinnedMessages: Array = [];
- #frameErrorCount: number = 0;
+ readonly #pinnedMessages: Array = [];
+ #frameErrorCount = 0;
#backupTier: BackupLevel | undefined;
- private constructor(private readonly options: BackupImportOptions) {
+ private constructor(options: BackupImportOptions) {
super({ objectMode: true });
+ this.#options = options;
}
public static async create(
@@ -451,8 +453,7 @@ export class BackupImportStream extends Writable {
await convo.updateLastMessage();
} catch (error) {
log.error(
- `${this.#logId}: failed to update conversation's last message` +
- `${Errors.toLogFormat(error)}`
+ `${this.#logId}: failed to update conversation's last message ${Errors.toLogFormat(error)}`
);
}
},
@@ -463,7 +464,7 @@ export class BackupImportStream extends Writable {
await pMap(
allConversations,
async conversation => {
- if (this.options.type === 'cross-client-integration-test') {
+ if (this.#options.type === 'cross-client-integration-test') {
return;
}
if (
@@ -494,7 +495,7 @@ export class BackupImportStream extends Writable {
);
if (
- this.options.type !== 'cross-client-integration-test' &&
+ this.#options.type !== 'cross-client-integration-test' &&
!isTestEnvironment(getEnvironment())
) {
await startBackupMediaDownload();
@@ -599,6 +600,7 @@ export class BackupImportStream extends Writable {
await this.#fromChatFolder(item.chatFolder);
} else {
log.warn(
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
`${this.#logId}: unknown unsupported frame item ${frame.item}`
);
throw new Error('Unknown unsupported frame type');
@@ -606,8 +608,8 @@ export class BackupImportStream extends Writable {
} catch (error) {
this.#frameErrorCount += 1;
log.error(
- `${this.#logId}: failed to process a frame ${frame.item}, ` +
- `${Errors.toLogFormat(error)}`
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
+ `${this.#logId}: failed to process a frame ${frame.item}, ${Errors.toLogFormat(error)}`
);
}
}
@@ -648,8 +650,7 @@ export class BackupImportStream extends Writable {
return await upgradeMessageSchema(attributes);
} catch (error) {
log.error(
- `${this.#logId}: failed to migrate a message ${attributes.sent_at}, ` +
- `${Errors.toLogFormat(error)}`
+ `${this.#logId}: failed to migrate a message ${attributes.sent_at}, ${Errors.toLogFormat(error)}`
);
return attributes;
}
@@ -1130,13 +1131,13 @@ export class BackupImportStream extends Writable {
? Bytes.toBase64(deriveAccessKeyFromProfileKey(contact.profileKey))
: undefined,
sealedSender: SEALED_SENDER.UNKNOWN,
- profileSharing: contact.profileSharing === true,
+ profileSharing: contact.profileSharing,
profileName: dropNull(contact.profileGivenName),
profileFamilyName: dropNull(contact.profileFamilyName),
systemGivenName: dropNull(contact.systemGivenName),
systemFamilyName: dropNull(contact.systemFamilyName),
systemNickname: dropNull(contact.systemNickname),
- hideStory: contact.hideStory === true,
+ hideStory: contact.hideStory,
username: dropNull(contact.username),
expireTimerVersion: 1,
nicknameGivenName: dropNull(contact.nickname?.given),
@@ -1244,12 +1245,11 @@ export class BackupImportStream extends Writable {
groupId,
secretParams: Bytes.toBase64(secretParams),
publicParams: Bytes.toBase64(publicParams),
- profileSharing: group.whitelisted === true,
- messageRequestResponseType:
- group.whitelisted === true
- ? SignalService.SyncMessage.MessageRequestResponse.Type.ACCEPT
- : undefined,
- hideStory: group.hideStory === true,
+ profileSharing: group.whitelisted,
+ messageRequestResponseType: group.whitelisted
+ ? SignalService.SyncMessage.MessageRequestResponse.Type.ACCEPT
+ : undefined,
+ hideStory: group.hideStory,
storySendMode,
avatar: avatarUrl
? {
@@ -1438,7 +1438,7 @@ export class BackupImportStream extends Writable {
result = {
...commonFields,
name: list.name ?? '',
- allowsReplies: list.allowReplies === true,
+ allowsReplies: list.allowReplies,
isBlockList,
members: (list.memberRecipientIds || []).map(recipientId => {
const convo = this.#recipientIdToConvo.get(recipientId);
@@ -1530,7 +1530,7 @@ export class BackupImportStream extends Writable {
conversation.test_chatFrameImportedFromBackup = true;
}
- conversation.isArchived = chat.archived === true;
+ conversation.isArchived = chat.archived;
conversation.isPinned = (chat.pinnedOrder || 0) !== 0;
conversation.expireTimer =
@@ -1550,9 +1550,9 @@ export class BackupImportStream extends Writable {
chat.muteUntilMs
);
}
- conversation.markedUnread = chat.markedUnread === true;
+ conversation.markedUnread = chat.markedUnread;
conversation.dontNotifyForMentionsIfMuted =
- chat.dontNotifyForMentionsIfMuted === true;
+ chat.dontNotifyForMentionsIfMuted;
const chatStyle = this.#fromChatStyle(chat.style);
@@ -1672,7 +1672,7 @@ export class BackupImportStream extends Writable {
type: directionalDetails.outgoing != null ? 'outgoing' : 'incoming',
expirationStartTimestamp,
expireTimer,
- sms: chatItem.sms === true ? true : undefined,
+ sms: chatItem.sms ? true : undefined,
...directionDetails,
};
const additionalMessages: Array = [];
@@ -1867,7 +1867,7 @@ export class BackupImportStream extends Writable {
expiresAt = toNumber(pinExpiry.pinExpiresAtTimestamp);
} else {
strictAssert(
- pinExpiry.pinNeverExpires === true,
+ pinExpiry.pinNeverExpires,
'pinDetails: pinNeverExpires should be true if theres no pinExpiresAtTimestamp'
);
expiresAt = null;
@@ -1999,6 +1999,7 @@ export class BackupImportStream extends Writable {
sendStatus = SendStatus.Skipped;
} else {
log.error(
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
`${timestamp}: Unknown sendStatus received: ${status}, falling back to Pending`
);
// We fallback to pending for unknown send statuses
@@ -2037,7 +2038,7 @@ export class BackupImportStream extends Writable {
incoming.dateServerSent
);
- const unidentifiedDeliveryReceived = incoming.sealedSender === true;
+ const unidentifiedDeliveryReceived = incoming.sealedSender;
if (incoming.read) {
return {
@@ -2176,14 +2177,14 @@ export class BackupImportStream extends Writable {
bodyRanges: this.#fromBodyRanges(data.text),
})),
bodyAttachment: data.longText
- ? convertFilePointerToAttachment(data.longText, this.options)
+ ? convertFilePointerToAttachment(data.longText, this.#options)
: undefined,
attachments: data.attachments?.length
? data.attachments
.map(attachment =>
convertBackupMessageAttachmentToAttachment(
attachment,
- this.options
+ this.#options
)
)
.filter(isNotNil)
@@ -2229,7 +2230,7 @@ export class BackupImportStream extends Writable {
description: dropNull(preview.description),
date: getCheckedTimestampOrUndefinedFromLong(preview.date),
image: preview.image
- ? convertFilePointerToAttachment(preview.image, this.options)
+ ? convertFilePointerToAttachment(preview.image, this.#options)
: undefined,
};
})
@@ -2248,7 +2249,7 @@ export class BackupImportStream extends Writable {
? [
convertBackupMessageAttachmentToAttachment(
attachment,
- this.options
+ this.#options
),
].filter(isNotNil)
: undefined,
@@ -2292,7 +2293,7 @@ export class BackupImportStream extends Writable {
result.body = textReply.text?.body ?? undefined;
result.bodyRanges = this.#fromBodyRanges(textReply.text);
result.bodyAttachment = textReply.longText
- ? convertFilePointerToAttachment(textReply.longText, this.options)
+ ? convertFilePointerToAttachment(textReply.longText, this.#options)
: undefined;
} else if (emoji) {
result.storyReaction = {
@@ -2322,7 +2323,7 @@ export class BackupImportStream extends Writable {
body: textReply.text?.body ?? undefined,
bodyRanges: this.#fromBodyRanges(textReply.text),
bodyAttachment: textReply.longText
- ? convertFilePointerToAttachment(textReply.longText, this.options)
+ ? convertFilePointerToAttachment(textReply.longText, this.#options)
: undefined,
};
}
@@ -2453,7 +2454,7 @@ export class BackupImportStream extends Writable {
? stringToMIMEType(contentType)
: APPLICATION_OCTET_STREAM,
thumbnail: thumbnail?.pointer
- ? convertFilePointerToAttachment(thumbnail.pointer, this.options)
+ ? convertFilePointerToAttachment(thumbnail.pointer, this.#options)
: undefined,
};
}) ?? [],
@@ -2640,7 +2641,7 @@ export class BackupImportStream extends Writable {
? {
avatar: convertFilePointerToAttachment(
avatar,
- this.options
+ this.#options
),
isProfile: false,
}
@@ -2715,7 +2716,7 @@ export class BackupImportStream extends Writable {
packKey: Bytes.toBase64(packKey),
stickerId,
data: data
- ? convertFilePointerToAttachment(data, this.options)
+ ? convertFilePointerToAttachment(data, this.#options)
: undefined,
},
reactions: this.#fromReactions(item.stickerMessage.reactions),
@@ -2788,7 +2789,7 @@ export class BackupImportStream extends Writable {
receiptCredentialPresentation: Bytes.toBase64(
giftBadge.receiptCredentialPresentation
),
- expiration: Number(receipt.getReceiptExpirationTime()) * SECOND,
+ expiration: receipt.getReceiptExpirationTime() * SECOND,
id: undefined,
level: Number(receipt.getReceiptLevel()),
state,
@@ -3948,10 +3949,10 @@ export class BackupImportStream extends Writable {
emoji: dropNull(emoji),
color: dropNull(color) ?? DEFAULT_PROFILE_COLOR,
createdAtMs: getCheckedTimestampOrUndefinedFromLong(createdAtMs) ?? 0,
- allowAllCalls: Boolean(allowAllCalls),
- allowAllMentions: Boolean(allowAllMentions),
+ allowAllCalls,
+ allowAllMentions,
allowedMembers: new Set(allowedMemberConversationIds ?? []),
- scheduleEnabled: Boolean(scheduleEnabled),
+ scheduleEnabled: scheduleEnabled,
scheduleStartTime: dropNull(scheduleStartTime),
scheduleEndTime: dropNull(scheduleEndTime),
scheduleDaysEnabled: parseScheduleDaysEnabled(scheduleDaysEnabled),
@@ -4255,7 +4256,7 @@ export class BackupImportStream extends Writable {
}
#isLocalBackup() {
- return this.options.type === 'local-encrypted';
+ return this.#options.type === 'local-encrypted';
}
#isMediaEnabledBackup() {
diff --git a/ts/services/backups/util/FileStream.node.ts b/ts/services/backups/util/FileStream.node.ts
index ed8c4d61c4..4730b2bd81 100644
--- a/ts/services/backups/util/FileStream.node.ts
+++ b/ts/services/backups/util/FileStream.node.ts
@@ -6,13 +6,15 @@ import { Buffer } from 'node:buffer';
import { InputStream } from '@signalapp/libsignal-client/dist/io.js';
export class FileStream extends InputStream {
+ readonly #filePath: string;
#file: FileHandle | undefined;
#position = 0;
#buffer = Buffer.alloc(16 * 1024);
#initPromise: Promise | undefined;
- constructor(private readonly filePath: string) {
+ constructor(filePath: string) {
super();
+ this.#filePath = filePath;
}
public override async close(): Promise {
@@ -46,7 +48,7 @@ export class FileStream extends InputStream {
return this.#file;
}
- const filePromise = open(this.filePath);
+ const filePromise = open(this.#filePath);
this.#initPromise = filePromise;
this.#file = await filePromise;
return this.#file;
diff --git a/ts/services/calling.preload.ts b/ts/services/calling.preload.ts
index 096a6a2790..808198ebd3 100644
--- a/ts/services/calling.preload.ts
+++ b/ts/services/calling.preload.ts
@@ -201,10 +201,10 @@ const {
cleanExpiredGroupCallRingCancellations,
} = DataWriter;
-const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
+const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD = new Map<
HttpMethod,
'GET' | 'PUT' | 'POST' | 'DELETE'
-> = new Map([
+>([
[HttpMethod.Get, 'GET'],
[HttpMethod.Put, 'PUT'],
[HttpMethod.Post, 'POST'],
@@ -486,12 +486,14 @@ async function ensureSystemPermissions({
hasLocalAudio: boolean;
}): Promise {
if (hasLocalAudio) {
+ // oxlint-disable-next-line typescript/await-thenable
await window.reduxActions.globalModals.ensureSystemMediaPermissions(
'microphone',
'call'
);
}
if (hasLocalVideo) {
+ // oxlint-disable-next-line typescript/await-thenable
await window.reduxActions.globalModals.ensureSystemMediaPermissions(
'camera',
'call'
@@ -590,13 +592,13 @@ export class CallingClass {
#deviceReselectionTimer?: NodeJS.Timeout;
#callsLookup: { [key: string]: Call | GroupCall };
- #cameraEnabled: boolean = false;
+ #cameraEnabled = false;
- #registeredAssets: Map = new Map();
+ readonly #registeredAssets = new Map();
// Send our profile key to other participants in call link calls to ensure they
// can see our profile info. Only send once per aci until the next app start.
- #sendProfileKeysForAdhocCallCache: Set;
+ readonly #sendProfileKeysForAdhocCallCache: Set;
constructor() {
this.#videoCapturer = new GumVideoCapturer();
@@ -2601,6 +2603,7 @@ export class CallingClass {
if (enabled) {
// Make sure we have access to camera
+ // oxlint-disable-next-line typescript/await-thenable
await window.reduxActions.globalModals.ensureSystemMediaPermissions(
'camera',
'call'
@@ -3056,6 +3059,7 @@ export class CallingClass {
}
async enableLocalCamera(mode: CallMode): Promise {
+ // oxlint-disable-next-line typescript/await-thenable
await window.reduxActions.globalModals.ensureSystemMediaPermissions(
'camera',
'call'
@@ -3608,7 +3612,7 @@ export class CallingClass {
ageInSeconds: number,
wasVideoCall: boolean,
receivedAtCounter: number | undefined,
- receivedAtMS: number | undefined = undefined
+ receivedAtMS?: number
) {
const conversation = window.ConversationController.get(remoteUserId);
if (!conversation) {
@@ -3777,7 +3781,7 @@ export class CallingClass {
call.handleRemoteSharingScreen = () => {
reduxInterface.remoteSharingScreenChange({
conversationId,
- isSharingScreen: Boolean(call.remoteSharingScreen),
+ isSharingScreen: call.remoteSharingScreen,
});
};
diff --git a/ts/services/distributionListLoader.preload.ts b/ts/services/distributionListLoader.preload.ts
index 9b92dc54bf..8424d5f4a7 100644
--- a/ts/services/distributionListLoader.preload.ts
+++ b/ts/services/distributionListLoader.preload.ts
@@ -17,10 +17,10 @@ export function getDistributionListsForRedux(): Array ({
- allowsReplies: Boolean(list.allowsReplies),
+ allowsReplies: list.allowsReplies,
deletedAtTimestamp: list.deletedAtTimestamp,
id: list.id,
- isBlockList: Boolean(list.isBlockList),
+ isBlockList: list.isBlockList,
name: list.name,
memberServiceIds: list.members,
}))
diff --git a/ts/services/donations.preload.ts b/ts/services/donations.preload.ts
index 13d7e807aa..aa9061b2b9 100644
--- a/ts/services/donations.preload.ts
+++ b/ts/services/donations.preload.ts
@@ -659,7 +659,7 @@ async function withConcurrencyCheck Promise>(
isDonationStepInProgress = true;
try {
- return fn();
+ return await fn();
} finally {
isDonationStepInProgress = false;
}
@@ -1144,7 +1144,7 @@ export async function _redeemReceipt(
async function failDonation(
errorType: DonationErrorType,
- details: string | undefined = undefined
+ details?: string
): Promise {
const workflow = _getWorkflowFromRedux();
const logId = `failDonation(${workflow?.id ? redactId(workflow.id) : 'NONE'})`;
@@ -1234,6 +1234,7 @@ export function _getWorkflowFromStorage(): DonationWorkflow | undefined {
const result = safeParseUnknown(donationWorkflowSchema, workflowData);
if (!result.success) {
log.error(
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
`${logId}: Workflow from storage was malformed: ${result.error.flatten()}`
);
return undefined;
@@ -1261,6 +1262,7 @@ export async function _saveWorkflowToStorage(
const result = safeParseStrict(donationWorkflowSchema, workflow);
if (!result.success) {
log.error(
+ // oxlint-disable-next-line typescript/no-base-to-string, typescript/restrict-template-expressions
`${logId}: Provided workflow was malformed: ${result.error.flatten()}`
);
throw result.error;
diff --git a/ts/services/expiringMessagesDeletion.preload.ts b/ts/services/expiringMessagesDeletion.preload.ts
index c95a8d95ed..5df82e5b19 100644
--- a/ts/services/expiringMessagesDeletion.preload.ts
+++ b/ts/services/expiringMessagesDeletion.preload.ts
@@ -18,7 +18,10 @@ const log = createLogger('expiringMessagesDeletion');
class ExpiringMessagesDeletionService {
#timeout?: ReturnType