Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable no-unsafe-call eslint rule #42743

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8f019fd
enable no-unsafe-call rule
bgawkuc May 22, 2024
df002bc
fix all text input ref errors
bgawkuc May 27, 2024
03e4302
wip
bgawkuc May 28, 2024
6cd6194
move isAnimatedTextInputRef function to common file
bgawkuc May 28, 2024
ae1804a
simplify changes
bgawkuc May 28, 2024
b82c7ed
Merge branch 'main' of https://github.com/software-mansion-labs/expen…
bgawkuc May 28, 2024
48de655
wip
bgawkuc May 28, 2024
238abd6
rename text input function & change performance types
bgawkuc May 28, 2024
9fc260d
wip
bgawkuc May 28, 2024
6ae0cca
wip
bgawkuc May 28, 2024
2cbb545
fix tests errors
bgawkuc May 28, 2024
8bdd994
define types for NativeModules
bgawkuc Jun 4, 2024
2b2ab09
add JSDoc to isAnimatedTextInputFocused
bgawkuc Jun 4, 2024
55a69fd
fix webpack plugin props
bgawkuc Jun 4, 2024
2a983c9
Merge branch 'main' of https://github.com/software-mansion-labs/expen…
bgawkuc Jun 5, 2024
0723e43
fix ts error
bgawkuc Jun 5, 2024
42f1f6d
review adjustments
bgawkuc Jun 5, 2024
75b085a
fix storybook
bgawkuc Jun 5, 2024
729aaa1
rename preload webpack plugin type
bgawkuc Jun 5, 2024
fbaa95f
add create download queue types
bgawkuc Jun 6, 2024
355deae
change parse run opts
bgawkuc Jun 7, 2024
7d8dbc4
rename text input focused function and mouse event type
bgawkuc Jun 10, 2024
4541ce2
move CreateDownloadQueue type
bgawkuc Jun 10, 2024
cbe212e
remove redundant check
bgawkuc Jun 10, 2024
90552a4
add module declaration for react-native-performance
bgawkuc Jun 10, 2024
a702e5f
Merge branch 'main' of https://github.com/software-mansion-labs/expen…
bgawkuc Jun 11, 2024
649529e
Merge branch 'main' into ts/enable-no-unsafe-call
bgawkuc Jun 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ module.exports = {
__DEV__: 'readonly',
},
rules: {
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import * as core from '@actions/core';
import fs from 'fs';

type RegressionEntry = {
metadata?: {
creationDate: string;
};
name: string;
meanDuration: number;
meanCount: number;
};

const run = () => {
// Prefix path to the graphite metric
const GRAPHITE_PATH = 'reassure';
Expand All @@ -24,11 +33,11 @@ const run = () => {
}

try {
const current = JSON.parse(entry);
const current: RegressionEntry = JSON.parse(entry);

// Extract timestamp, Graphite accepts timestamp in seconds
if (current.metadata?.creationDate) {
timestamp = Math.floor(new Date(current.metadata.creationDate as string).getTime() / 1000);
timestamp = Math.floor(new Date(current.metadata.creationDate).getTime() / 1000);
}

if (current.name && current.meanDuration && current.meanCount && timestamp) {
Expand Down
4 changes: 2 additions & 2 deletions .github/libs/GitUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ function getCommitHistoryAsJSON(fromTag: string, toTag: string): Promise<CommitT
console.log(`Running command: git ${args.join(' ')}`);
const spawnedProcess = spawn('git', args);
spawnedProcess.on('message', console.log);
spawnedProcess.stdout.on('data', (chunk) => {
spawnedProcess.stdout.on('data', (chunk: Buffer) => {
console.log(chunk.toString());
stdout += chunk.toString();
});
spawnedProcess.stderr.on('data', (chunk) => {
spawnedProcess.stderr.on('data', (chunk: Buffer) => {
console.error(chunk.toString());
stderr += chunk.toString();
});
Expand Down
9 changes: 6 additions & 3 deletions .storybook/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/* eslint-disable no-param-reassign */

/* eslint-disable @typescript-eslint/naming-convention */
import type Environment from 'config/webpack/types';
import dotenv from 'dotenv';
import path from 'path';
import {DefinePlugin} from 'webpack';
Expand All @@ -18,6 +19,8 @@ type CustomWebpackConfig = {
};
};

type CustomWebpackFunction = ({file, platform}: Environment) => CustomWebpackConfig;

let envFile: string;
switch (process.env.ENV) {
case 'production':
Expand All @@ -31,9 +34,9 @@ switch (process.env.ENV) {
}

const env = dotenv.config({path: path.resolve(__dirname, `../${envFile}`)});
const custom: CustomWebpackConfig = require('../config/webpack/webpack.common').default({
envFile,
});
const customFunction: CustomWebpackFunction = require('../config/webpack/webpack.common').default;

const custom: CustomWebpackConfig = customFunction({file: envFile});

const webpackConfig = ({config}: {config: Configuration}) => {
if (!config.resolve) {
Expand Down
17 changes: 15 additions & 2 deletions config/webpack/webpack.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,27 @@ import dotenv from 'dotenv';
import fs from 'fs';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import path from 'path';
import type {Configuration} from 'webpack';
import type {Compiler, Configuration} from 'webpack';
import {DefinePlugin, EnvironmentPlugin, IgnorePlugin, ProvidePlugin} from 'webpack';
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer';
import CustomVersionFilePlugin from './CustomVersionFilePlugin';
import type Environment from './types';

// importing anything from @vue/preload-webpack-plugin causes an error
type Options = {
rel: string;
as: string;
fileWhitelist: RegExp[];
include: string;
};

type PreloadWebpackPluginClass = {
new (options?: Options): PreloadWebpackPluginClass;
apply: (compiler: Compiler) => void;
};

// require is necessary, there are no types for this package and the declaration file can't be seen by the build process which causes an error.
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
const PreloadWebpackPlugin: PreloadWebpackPluginClass = require('@vue/preload-webpack-plugin');

const includeModules = [
'react-native-animatable',
Expand Down
8 changes: 7 additions & 1 deletion desktop/createDownloadQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ type DownloadItem = {
options: Options;
};

type CreateDownloadQueue = () => {
enqueueDownloadItem: (item: DownloadItem) => void;
dequeueDownloadItem: () => DownloadItem | undefined;
};

/**
* Returns the filename with extension based on the given name and MIME type.
* @param name - The name of the file.
Expand All @@ -28,7 +33,7 @@ const getFilenameFromMime = (name: string, mime: string): string => {
return `${name}.${extensions}`;
};

const createDownloadQueue = () => {
const createDownloadQueue: CreateDownloadQueue = () => {
const downloadItemProcessor = (item: DownloadItem): Promise<void> =>
new Promise((resolve, reject) => {
let downloadTimeout: NodeJS.Timeout;
Expand Down Expand Up @@ -114,3 +119,4 @@ const createDownloadQueue = () => {
};

export default createDownloadQueue;
export type {DownloadItem, CreateDownloadQueue};
5 changes: 3 additions & 2 deletions desktop/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import type PlatformSpecificUpdater from '@src/setup/platformSetup/types';
import type {Locale} from '@src/types/onyx';
import type {CreateDownloadQueue, DownloadItem} from './createDownloadQueue';
import ELECTRON_EVENTS from './ELECTRON_EVENTS';

const createDownloadQueue = require('./createDownloadQueue').default;
const createDownloadQueue: CreateDownloadQueue = require('./createDownloadQueue').default;

const port = process.env.PORT ?? 8082;
const {DESKTOP_SHORTCUT_ACCELERATOR, LOCALES} = CONST;
Expand Down Expand Up @@ -617,7 +618,7 @@ const mainWindow = (): Promise<void> => {

const downloadQueue = createDownloadQueue();
ipcMain.on(ELECTRON_EVENTS.DOWNLOAD, (event, downloadData) => {
const downloadItem = {
const downloadItem: DownloadItem = {
...downloadData,
win: browserWindow,
};
Expand Down
4 changes: 2 additions & 2 deletions jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ jest.mock('react-native-onyx/dist/storage', () => mockStorage);
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter');

// Turn off the console logs for timing events. They are not relevant for unit tests and create a lot of noise
jest.spyOn(console, 'debug').mockImplementation((...params) => {
if (params[0].indexOf('Timing:') === 0) {
jest.spyOn(console, 'debug').mockImplementation((...params: string[]) => {
if (params[0].startsWith('Timing:')) {
return;
}

Expand Down
7 changes: 4 additions & 3 deletions src/components/AmountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import CONST from '@src/CONST';
import BigNumberPad from './BigNumberPad';
import FormHelpMessage from './FormHelpMessage';
import isTextInputFocused from './TextInput/BaseTextInput/isTextInputFocused';
import type {BaseTextInputProps, BaseTextInputRef} from './TextInput/BaseTextInput/types';
import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol';
import type TextInputWithCurrencySymbolProps from './TextInputWithCurrencySymbol/types';
Expand Down Expand Up @@ -94,7 +95,7 @@ function AmountForm(
if (!textInput.current) {
return;
}
if (!textInput.current.isFocused()) {
if (!isTextInputFocused(textInput)) {
textInput.current.focus();
}
};
Expand Down Expand Up @@ -143,7 +144,7 @@ function AmountForm(
*/
const updateAmountNumberPad = useCallback(
(key: string) => {
if (shouldUpdateSelection && !textInput.current?.isFocused()) {
if (shouldUpdateSelection && !isTextInputFocused(textInput)) {
textInput.current?.focus();
}
// Backspace button is pressed
Expand All @@ -168,7 +169,7 @@ function AmountForm(
*/
const updateLongPressHandlerState = useCallback((value: boolean) => {
setShouldUpdateSelection(!value);
if (!value && !textInput.current?.isFocused()) {
if (!value && !isTextInputFocused(textInput)) {
textInput.current?.focus();
}
}, []);
Expand Down
7 changes: 4 additions & 3 deletions src/components/EmojiPicker/EmojiPickerMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {scrollTo} from 'react-native-reanimated';
import EmojiPickerMenuItem from '@components/EmojiPicker/EmojiPickerMenuItem';
import Text from '@components/Text';
import TextInput from '@components/TextInput';
import isTextInputFocused from '@components/TextInput/BaseTextInput/isTextInputFocused';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -86,7 +87,7 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
}

// If the input is not focused and the new index is out of range, focus the input
if (newIndex < 0 && !searchInputRef.current?.isFocused() && shouldFocusInputOnScreenFocus) {
if (newIndex < 0 && !isTextInputFocused(searchInputRef) && shouldFocusInputOnScreenFocus) {
searchInputRef.current?.focus();
}
},
Expand Down Expand Up @@ -165,12 +166,12 @@ function EmojiPickerMenu({onEmojiSelected, activeEmoji}: EmojiPickerMenuProps, r
// Enable keyboard movement if tab or enter is pressed or if shift is pressed while the input
// is not focused, so that the navigation and tab cycling can be done using the keyboard without
// interfering with the input behaviour.
if (keyBoardEvent.key === 'Tab' || keyBoardEvent.key === 'Enter' || (keyBoardEvent.key === 'Shift' && searchInputRef.current && !searchInputRef.current.isFocused())) {
if (keyBoardEvent.key === 'Tab' || keyBoardEvent.key === 'Enter' || (keyBoardEvent.key === 'Shift' && searchInputRef.current && !isTextInputFocused(searchInputRef))) {
setIsUsingKeyboardMovement(true);
}

// We allow typing in the search box if any key is pressed apart from Arrow keys.
if (searchInputRef.current && !searchInputRef.current.isFocused() && ReportUtils.shouldAutoFocusOnKeyPress(keyBoardEvent)) {
if (searchInputRef.current && !isTextInputFocused(searchInputRef) && ReportUtils.shouldAutoFocusOnKeyPress(keyBoardEvent)) {
searchInputRef.current.focus();
}
},
Expand Down
13 changes: 8 additions & 5 deletions src/components/Hoverable/ActiveHoverable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type HoverableProps from './types';

type ActiveHoverableProps = Omit<HoverableProps, 'disabled'>;

type OnMouseEvent = (e: MouseEvent) => void;

function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, shouldFreezeCapture, children}: ActiveHoverableProps, outerRef: Ref<HTMLElement>) {
const [isHovered, setIsHovered] = useState(false);

Expand Down Expand Up @@ -98,9 +100,10 @@ function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, shouldFreez

const child = useMemo(() => getReturnValue(children, !isScrollingRef.current && isHovered), [children, isHovered]);

const childOnMouseEnter = child.props.onMouseEnter;
const childOnMouseLeave = child.props.onMouseLeave;
const childOnMouseMove = child.props.onMouseMove;
const childOnMouseEnter: OnMouseEvent = child.props.onMouseEnter;
const childOnMouseLeave: OnMouseEvent = child.props.onMouseLeave;
const childOnMouseMove: OnMouseEvent = child.props.onMouseMove;
const childOnBlur: OnMouseEvent = child.props.onBlur;

const hoverAndForwardOnMouseEnter = useCallback(
(e: MouseEvent) => {
Expand All @@ -127,9 +130,9 @@ function ActiveHoverable({onHoverIn, onHoverOut, shouldHandleScroll, shouldFreez
setIsHovered(false);
}

child.props.onBlur?.(event);
childOnBlur?.(event);
},
[child.props],
[childOnBlur],
);

const handleAndForwardOnMouseMove = useCallback(
Expand Down
3 changes: 2 additions & 1 deletion src/components/MoneyRequestAmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import getOperatingSystem from '@libs/getOperatingSystem';
import * as MoneyRequestUtils from '@libs/MoneyRequestUtils';
import shouldIgnoreSelectionWhenUpdatedManually from '@libs/shouldIgnoreSelectionWhenUpdatedManually';
import CONST from '@src/CONST';
import isTextInputFocused from './TextInput/BaseTextInput/isTextInputFocused';
import type {BaseTextInputRef} from './TextInput/BaseTextInput/types';
import TextInputWithCurrencySymbol from './TextInputWithCurrencySymbol';

Expand Down Expand Up @@ -201,7 +202,7 @@ function MoneyRequestAmountInput(
}));

useEffect(() => {
if (!currency || typeof amount !== 'number' || (formatAmountOnBlur && textInput.current?.isFocused()) || shouldKeepUserInput) {
if ((!currency || typeof amount !== 'number' || (formatAmountOnBlur && isTextInputFocused(textInput))) ?? shouldKeepUserInput) {
return;
}
const frontendAmount = onFormatAmount(amount, currency);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import type {BaseTextInputRef} from './types';

/** Checks that text input has the isFocused method and is focused. */
export default function isTextInputFocused(textInput: React.MutableRefObject<BaseTextInputRef | null>): boolean | null {
return textInput.current && 'isFocused' in textInput.current && (textInput.current as AnimatedTextInputRef).isFocused();
}
Comment on lines +1 to +7
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels more like some utility function, is this indeed the best place to put such method based on our guidelines?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is the main/core text input so nab, moving this ahead

3 changes: 2 additions & 1 deletion src/components/Tooltip/BaseTooltip/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ function Tooltip(
(e: MouseEvent) => {
updateTargetAndMousePosition(e);
if (React.isValidElement(children)) {
children.props.onMouseEnter?.(e);
const onMouseEnter: (e: MouseEvent) => void | undefined = children.props.onMouseEnter;
onMouseEnter?.(e);
}
},
[children, updateTargetAndMousePosition],
Expand Down
10 changes: 8 additions & 2 deletions src/components/Tooltip/PopoverAnchorTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ function PopoverAnchorTooltip({shouldRender = true, children, ...props}: Tooltip

const isPopoverRelatedToTooltipOpen = useMemo(() => {
// eslint-disable-next-line @typescript-eslint/dot-notation
const tooltipNode = tooltipRef.current?.['_childNode'] ?? null;
if (isOpen && popover?.anchorRef?.current && tooltipNode && (tooltipNode.contains(popover.anchorRef.current) || tooltipNode === popover.anchorRef.current)) {
const tooltipNode: Node | null = tooltipRef.current?.['_childNode'] ?? null;

if (
isOpen &&
popover?.anchorRef?.current &&
tooltipNode &&
((popover.anchorRef.current instanceof Node && tooltipNode.contains(popover.anchorRef.current)) || tooltipNode === popover.anchorRef.current)
bgawkuc marked this conversation as resolved.
Show resolved Hide resolved
) {
return true;
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/WalletStatementModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {WalletStatementOnyxProps, WalletStatementProps} from './types';
import type {WalletStatementMessage, WalletStatementOnyxProps, WalletStatementProps} from './types';

function WalletStatementModal({statementPageURL, session}: WalletStatementProps) {
const styles = useThemeStyles();
Expand All @@ -18,7 +18,7 @@ function WalletStatementModal({statementPageURL, session}: WalletStatementProps)
/**
* Handles in-app navigation for iframe links
*/
const navigate = (event: MessageEvent) => {
const navigate = (event: MessageEvent<WalletStatementMessage>) => {
if (!event.data?.type || (event.data.type !== CONST.WALLET.WEB_MESSAGE_TYPE.STATEMENT && event.data.type !== CONST.WALLET.WEB_MESSAGE_TYPE.CONCIERGE)) {
return;
}
Expand Down
7 changes: 6 additions & 1 deletion src/components/WalletStatementModal/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ type WalletStatementProps = WalletStatementOnyxProps & {
statementPageURL: string;
};

export type {WalletStatementProps, WalletStatementOnyxProps};
type WalletStatementMessage = {
url: string;
type: string;
};

export type {WalletStatementProps, WalletStatementOnyxProps, WalletStatementMessage};
2 changes: 1 addition & 1 deletion src/libs/Environment/betaChecker/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as AppUpdate from '@libs/actions/AppUpdate';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import pkg from '../../../../package.json';
import type IsBetaBuild from './types';
import type {IsBetaBuild} from './types';

let isLastSavedBeta = false;
Onyx.connect({
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Environment/betaChecker/index.ios.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {NativeModules} from 'react-native';
import type IsBetaBuild from './types';
import type {IsBetaBuild} from './types';

/**
* Check to see if the build is staging (TestFlight) or production
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Environment/betaChecker/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type IsBetaBuild from './types';
import type {IsBetaBuild} from './types';

/**
* There's no beta build in non native
Expand Down
6 changes: 5 additions & 1 deletion src/libs/Environment/betaChecker/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
type IsBetaBuild = Promise<boolean>;

export default IsBetaBuild;
type EnvironmentCheckerModule = {
isBeta: () => IsBetaBuild;
};

export type {IsBetaBuild, EnvironmentCheckerModule};
Loading
Loading