From 2ade4acd524162d506024a9c2375368c53de39fa Mon Sep 17 00:00:00 2001 From: Sidney Keese Date: Mon, 14 Sep 2020 14:56:35 -0700 Subject: [PATCH] Migrate util, types, state, sticker-creator to ESLint --- .eslintignore | 8 +- .eslintrc.js | 25 +++- .gitignore | 1 + package.json | 1 + sticker-creator/app/index.tsx | 2 +- sticker-creator/app/stages/AppStage.tsx | 6 +- sticker-creator/app/stages/DropStage.tsx | 4 +- sticker-creator/app/stages/EmojiStage.tsx | 2 +- sticker-creator/app/stages/MetaStage.tsx | 11 +- sticker-creator/app/stages/ShareStage.tsx | 8 +- sticker-creator/app/stages/UploadStage.tsx | 7 +- .../components/ShareButtons.stories.tsx | 6 +- sticker-creator/components/ShareButtons.tsx | 123 +++++++++--------- .../components/StickerFrame.stories.tsx | 8 +- sticker-creator/components/StickerFrame.tsx | 19 ++- sticker-creator/components/StickerGrid.tsx | 3 +- .../components/StickerPackPreview.stories.tsx | 8 +- .../components/StickerPackPreview.tsx | 4 +- .../components/Toaster.stories.tsx | 8 +- sticker-creator/custom.d.ts | 1 + sticker-creator/elements/Button.stories.tsx | 20 +-- sticker-creator/elements/Button.tsx | 13 +- .../elements/ConfirmDialog.stories.tsx | 6 +- sticker-creator/elements/ConfirmDialog.tsx | 12 +- sticker-creator/elements/CopyText.stories.tsx | 6 +- sticker-creator/elements/CopyText.tsx | 48 +++---- sticker-creator/elements/DropZone.stories.tsx | 4 +- sticker-creator/elements/DropZone.tsx | 6 +- .../elements/LabeledCheckbox.stories.tsx | 6 +- sticker-creator/elements/LabeledCheckbox.tsx | 4 +- .../elements/LabeledInput.stories.tsx | 6 +- sticker-creator/elements/LabeledInput.tsx | 4 +- .../elements/MessageBubble.stories.tsx | 6 +- sticker-creator/elements/MessageBubble.tsx | 5 +- .../elements/MessageSticker.stories.tsx | 6 +- sticker-creator/elements/MessageSticker.tsx | 6 +- .../elements/PageHeader.stories.tsx | 6 +- .../elements/ProgressBar.stories.tsx | 6 +- .../elements/StickerPreview.stories.tsx | 6 +- sticker-creator/elements/StoryRow.tsx | 10 +- sticker-creator/elements/Toast.stories.tsx | 6 +- sticker-creator/elements/Toast.tsx | 6 +- .../elements/Typography.stories.tsx | 16 ++- sticker-creator/elements/Typography.tsx | 1 - sticker-creator/index.tsx | 1 + sticker-creator/root.tsx | 8 +- sticker-creator/store/ducks/stickers.ts | 77 +++++++---- sticker-creator/store/reducer.ts | 1 + sticker-creator/tsconfig.json | 9 ++ sticker-creator/util/i18n.tsx | 9 +- sticker-creator/util/preload.ts | 13 +- ts/components/conversation/MessageDetail.tsx | 2 +- ts/model-types.d.ts | 8 +- ts/state/createStore.ts | 23 +++- ts/state/ducks/calling.ts | 2 +- ts/state/ducks/conversations.ts | 20 +-- ts/state/ducks/emojis.ts | 2 +- ts/state/ducks/items.ts | 13 +- ts/state/ducks/search.ts | 2 +- ts/state/ducks/stickers.ts | 5 +- ts/state/reducer.ts | 4 +- ts/state/roots/createCallManager.tsx | 4 +- ts/state/roots/createCompositionArea.tsx | 7 +- ts/state/roots/createLeftPane.tsx | 4 +- ts/state/roots/createSafetyNumberViewer.tsx | 7 +- ts/state/roots/createShortcutGuideModal.tsx | 7 +- ts/state/roots/createStickerManager.tsx | 4 +- ts/state/roots/createStickerPreviewModal.tsx | 7 +- ts/state/roots/createTimeline.tsx | 7 +- ts/state/selectors/conversations.ts | 49 +++---- ts/state/selectors/search.ts | 16 +-- ts/state/selectors/stickers.ts | 8 +- ts/state/selectors/user.ts | 2 +- ts/state/smart/CallManager.tsx | 2 + ts/state/smart/CompositionArea.tsx | 3 +- ts/state/smart/ContactName.tsx | 2 +- ts/state/smart/LeftPane.tsx | 2 + ts/state/smart/Timeline.tsx | 9 +- ts/state/smart/TimelineItem.tsx | 2 + ts/state/smart/TimelineLoadingRow.tsx | 17 ++- ts/types/Attachment.ts | 44 +++++-- ts/types/Contact.tsx | 6 +- ts/types/Dialogs.ts | 2 + ts/types/Logging.ts | 2 +- ts/types/MIME.ts | 2 +- ts/types/Message.ts | 4 +- ts/types/PhoneNumber.ts | 6 +- ts/types/Settings.ts | 9 +- ts/util/Sound.ts | 7 +- ts/util/batcher.ts | 19 ++- ts/util/callingTones.ts | 5 +- ts/util/cleanSearchTerm.ts | 2 +- ts/util/combineNames.ts | 4 +- ts/util/deleteForEveryone.ts | 2 +- ts/util/formatDuration.ts | 2 +- ts/util/formatRelativeTime.ts | 14 +- ts/util/getInitials.ts | 4 +- ts/util/getStringForProfileChange.ts | 6 +- ts/util/hasExpired.ts | 4 +- ts/util/hooks.ts | 13 +- ts/util/imageToBlurHash.ts | 3 +- ts/util/isFileDangerous.ts | 1 - ts/util/libphonenumberInstance.ts | 2 +- ts/util/lint/analyze_exceptions.ts | 3 +- ts/util/lint/exceptions.json | 2 +- ts/util/lint/linter.ts | 2 + ts/util/lint/sort_exceptions.ts | 2 - ts/util/lint/util.ts | 8 +- ts/util/makeLookup.ts | 4 +- ts/util/registration.ts | 11 +- ts/util/sgnlHref.ts | 3 +- ts/util/waitBatcher.ts | 40 +++--- ts/util/zkgroup.ts | 20 +-- tslint.json | 7 +- yarn.lock | 2 +- 115 files changed, 647 insertions(+), 448 deletions(-) create mode 100644 sticker-creator/custom.d.ts create mode 100644 sticker-creator/tsconfig.json diff --git a/.eslintignore b/.eslintignore index 2a75f71bb339..858e9947573c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -24,25 +24,19 @@ test/blanket_mocha.js # TypeScript generated files ts/**/*.js +sticker-creator/**/*.js **/*.d.ts webpack.config.ts # Temporarily ignored during TSLint transition # JIRA: DESKTOP-304 -sticker-creator/**/*.ts -sticker-creator/**/*.tsx ts/*.ts -ts/components/*.ts -ts/components/*.tsx ts/components/stickers/** ts/shims/** ts/sql/** -ts/state/** ts/storybook/** ts/styleguide/** ts/test/** ts/textsecure/** -ts/types/** ts/updater/** -ts/util/** diff --git a/.eslintrc.js b/.eslintrc.js index 6c355611d751..78f24dc4697b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -77,6 +77,8 @@ const rules = { // Prefer functional components with default params 'react/require-default-props': 'off', + + 'jsx-a11y/label-has-associated-control': ['error', { assert: 'either' }], }; module.exports = { @@ -94,7 +96,7 @@ module.exports = { overrides: [ { - files: ['*.ts', '*.tsx'], + files: ['ts/**/*.ts', 'ts/**/*.tsx'], parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.json', @@ -113,12 +115,31 @@ module.exports = { ], rules, }, + { + files: ['sticker-creator/**/*.ts', 'sticker-creator/**/*.tsx'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './sticker-creator/tsconfig.json', + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 2018, + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'airbnb-typescript-prettier', + ], + rules, + }, { files: ['**/*.stories.tsx', 'ts/build/**'], rules: { ...rules, 'import/no-extraneous-dependencies': 'off', - 'react/jsx-props-no-spreading': 'off', 'react/no-array-index-key': 'off', }, }, diff --git a/.gitignore b/.gitignore index 7a0ffc6d2975..ae9ef166bf9a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ test/test.js # React / TypeScript ts/**/*.js ts/protobuf/*.d.ts +sticker-creator/**/*.js # CSS Modules **/*.scss.d.ts diff --git a/package.json b/package.json index b1d54b761097..3f33f8f4090b 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "p-props": "4.0.0", "p-queue": "6.2.1", "pify": "3.0.0", + "popper.js": "1.15.0", "protobufjs": "6.8.6", "proxy-agent": "3.1.1", "react": "16.8.3", diff --git a/sticker-creator/app/index.tsx b/sticker-creator/app/index.tsx index 7afbe05ed8da..cdcfcb2ee185 100644 --- a/sticker-creator/app/index.tsx +++ b/sticker-creator/app/index.tsx @@ -9,7 +9,7 @@ import * as styles from './index.scss'; import { PageHeader } from '../elements/PageHeader'; import { useI18n } from '../util/i18n'; -export const App = () => { +export const App: React.ComponentType = () => { const i18n = useI18n(); return ( diff --git a/sticker-creator/app/stages/AppStage.tsx b/sticker-creator/app/stages/AppStage.tsx index a555dbc4fde2..14007843751d 100644 --- a/sticker-creator/app/stages/AppStage.tsx +++ b/sticker-creator/app/stages/AppStage.tsx @@ -32,7 +32,7 @@ const getClassName = ({ noMessage, empty }: Props) => { return styles.main; }; -export const AppStage = (props: Props) => { +export const AppStage: React.ComponentType = props => { const { children, next, @@ -67,7 +67,7 @@ export const AppStage = (props: Props) => { ) : null} {addMoreCount > 0 ? ( - + {i18n('StickerCreator--DropStage--addMore', [addMoreCount])} ) : null} @@ -75,7 +75,7 @@ export const AppStage = (props: Props) => { + ))} + ); - - return [ - // Facebook - [ - i18n('StickerCreator--ShareButtons--facebook'), - '#4267B2', - 'M20.155 10.656l-1.506.001c-1.181 0-1.41.561-1.41 1.384v1.816h2.817l-.367 2.845h-2.45V24h-2.937v-7.298h-2.456v-2.845h2.456V11.76c0-2.435 1.487-3.76 3.658-3.76 1.04 0 1.934.077 2.195.112v2.544z', - `https://www.facebook.com/sharer/sharer.php?u=${packUrl}`, - ], - // Twitter - [ - i18n('StickerCreator--ShareButtons--twitter'), - '#1CA1F2', - 'M22.362 12.737c.006.141.01.282.01.425 0 4.337-3.302 9.339-9.34 9.339A9.294 9.294 0 018 21.027c.257.03.518.045.783.045a6.584 6.584 0 004.077-1.405 3.285 3.285 0 01-3.067-2.279 3.312 3.312 0 001.483-.057 3.283 3.283 0 01-2.633-3.218v-.042c.442.246.949.394 1.487.411a3.282 3.282 0 01-1.016-4.383 9.32 9.32 0 006.766 3.43 3.283 3.283 0 015.593-2.994 6.568 6.568 0 002.085-.796 3.299 3.299 0 01-1.443 1.816A6.587 6.587 0 0024 11.038a6.682 6.682 0 01-1.638 1.699', - `https://twitter.com/intent/tweet?text=${text}`, - ], - // Pinterest - // [ - // i18n('StickerCreator--ShareButtons--pinterest'), - // '#BD081C', - // 'M17.234 19.563c-.992 0-1.926-.536-2.245-1.146 0 0-.534 2.118-.646 2.527-.398 1.444-1.569 2.889-1.66 3.007-.063.083-.203.057-.218-.052-.025-.184-.324-2.007.028-3.493l1.182-5.008s-.293-.587-.293-1.454c0-1.362.789-2.379 1.772-2.379.836 0 1.239.628 1.239 1.38 0 .84-.535 2.097-.811 3.261-.231.975.489 1.77 1.451 1.77 1.74 0 2.913-2.236 2.913-4.886 0-2.014-1.356-3.522-3.824-3.522-2.787 0-4.525 2.079-4.525 4.402 0 .8.237 1.365.607 1.802.17.201.194.282.132.512-.045.17-.145.576-.188.738-.061.233-.249.316-.46.23-1.283-.524-1.882-1.931-1.882-3.511C9.806 11.13 12.008 8 16.374 8c3.51 0 5.819 2.538 5.819 5.265 0 3.605-2.005 6.298-4.959 6.298', - // `https://pinterest.com/pin/create/button/?url=${packUrl}`, - // ], - // Whatsapp - [ - i18n('StickerCreator--ShareButtons--whatsapp'), - '#25D366', - 'M16.033 23.862h-.003a7.914 7.914 0 01-3.79-.965L8.035 24l1.126-4.109a7.907 7.907 0 01-1.059-3.964C8.104 11.556 11.661 8 16.033 8c2.121 0 4.113.826 5.61 2.325a7.878 7.878 0 012.321 5.609c-.002 4.371-3.56 7.928-7.931 7.928zm3.88-5.101c-.165.463-.957.885-1.338.942a2.727 2.727 0 01-1.248-.078 11.546 11.546 0 01-1.13-.418c-1.987-.858-3.286-2.859-3.385-2.991-.1-.132-.81-1.074-.81-2.049 0-.975.513-1.455.695-1.653a.728.728 0 01.528-.248c.132 0 .264.001.38.007.122.006.285-.046.446.34.165.397.56 1.372.61 1.471.05.099.083.215.017.347-.066.132-.1.215-.198.331-.1.115-.208.258-.297.347-.1.098-.203.206-.087.404.116.198.513.847 1.102 1.372.757.675 1.396.884 1.594.984.198.099.314.082.429-.05.116-.132.496-.578.628-.777.132-.198.264-.165.446-.099.18.066 1.156.545 1.354.645.198.099.33.148.38.231.049.083.049.479-.116.942zm-3.877-9.422c-3.636 0-6.594 2.956-6.595 6.589 0 1.245.348 2.458 1.008 3.507l.157.249-.666 2.432 2.495-.654.24.142a6.573 6.573 0 003.355.919h.003a6.6 6.6 0 006.592-6.59 6.55 6.55 0 00-1.93-4.662 6.549 6.549 0 00-4.66-1.932z', - `https://wa.me?text=${text}`, - ], - ]; - }, [i18n, value]); - - return ( -
- {buttonPaths.map(([title, fill, path, url]) => ( - - ))} -
- ); -}); + } +); diff --git a/sticker-creator/components/StickerFrame.stories.tsx b/sticker-creator/components/StickerFrame.stories.tsx index 1b85c0aa1bce..75315de5bb10 100644 --- a/sticker-creator/components/StickerFrame.stories.tsx +++ b/sticker-creator/components/StickerFrame.stories.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; -import { StoryRow } from '../elements/StoryRow'; -import { StickerFrame } from './StickerFrame'; - import { storiesOf } from '@storybook/react'; import { boolean, select, text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; +import { StoryRow } from '../elements/StoryRow'; +import { StickerFrame } from './StickerFrame'; + storiesOf('Sticker Creator/components', module).add('StickerFrame', () => { const image = text('image url', '/fixtures/512x515-thumbs-up-lincoln.webp'); const showGuide = boolean('show guide', true); @@ -16,7 +16,7 @@ storiesOf('Sticker Creator/components', module).add('StickerFrame', () => { const [emoji, setEmoji] = React.useState(undefined); return ( - + - {mode !== 'add' ? ( + {// eslint-disable-next-line no-nested-ternary + mode !== 'add' ? ( image ? ( ) : ( @@ -191,14 +198,11 @@ export const StickerFrame = React.memo(
) : null} {mode === 'add' && onDrop ? ( - + ) : null} {mode === 'removable' ? (
- {images.map((src, id) => ( - {src} + {images.map(src => ( + {src} ))}
diff --git a/sticker-creator/components/Toaster.stories.tsx b/sticker-creator/components/Toaster.stories.tsx index 3227367bfa10..604022b92a8a 100644 --- a/sticker-creator/components/Toaster.stories.tsx +++ b/sticker-creator/components/Toaster.stories.tsx @@ -1,16 +1,18 @@ import * as React from 'react'; import { debounce, dropRight } from 'lodash'; -import { StoryRow } from '../elements/StoryRow'; -import { Toaster } from './Toaster'; - import { storiesOf } from '@storybook/react'; import { text as textKnob } from '@storybook/addon-knobs'; +import { StoryRow } from '../elements/StoryRow'; +import { Toaster } from './Toaster'; + storiesOf('Sticker Creator/components', module).add('Toaster', () => { const inputText = textKnob('Slices', ['error 1', 'error 2'].join('|')); const initialState = React.useMemo(() => inputText.split('|'), [inputText]); const [state, setState] = React.useState(initialState); + // TODO not sure how to fix this + // eslint-disable-next-line react-hooks/exhaustive-deps const handleDismiss = React.useCallback( // Debounce is required here since auto-dismiss is asynchronously called // from multiple rendered instances (multiple themes) diff --git a/sticker-creator/custom.d.ts b/sticker-creator/custom.d.ts new file mode 100644 index 000000000000..d5cf927a7cd3 --- /dev/null +++ b/sticker-creator/custom.d.ts @@ -0,0 +1 @@ +declare module '*.scss'; diff --git a/sticker-creator/elements/Button.stories.tsx b/sticker-creator/elements/Button.stories.tsx index 6e8f116cee75..e48887d1d25d 100644 --- a/sticker-creator/elements/Button.stories.tsx +++ b/sticker-creator/elements/Button.stories.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { Button } from './Button'; - import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { Button } from './Button'; + storiesOf('Sticker Creator/elements', module).add('Button', () => { const onClick = action('onClick'); const child = text('text', 'foo bar'); @@ -13,12 +13,12 @@ storiesOf('Sticker Creator/elements', module).add('Button', () => { return ( <> - - @@ -26,27 +26,27 @@ storiesOf('Sticker Creator/elements', module).add('Button', () => {
- - - - - diff --git a/sticker-creator/elements/Button.tsx b/sticker-creator/elements/Button.tsx index ca91946feeac..8242435e8068 100644 --- a/sticker-creator/elements/Button.tsx +++ b/sticker-creator/elements/Button.tsx @@ -3,10 +3,8 @@ import * as classnames from 'classnames'; import * as styles from './Button.scss'; export type Props = React.HTMLProps & { - className?: string; pill?: boolean; primary?: boolean; - children: React.ReactNode; }; const getClassName = ({ primary, pill }: Props) => { @@ -25,12 +23,15 @@ const getClassName = ({ primary, pill }: Props) => { return styles.base; }; -export const Button = (props: Props) => { - const { className, pill, primary, children, ...otherProps } = props; - +export const Button: React.ComponentType = ({ + className, + children, + ...otherProps +}) => { return ( - diff --git a/sticker-creator/elements/CopyText.stories.tsx b/sticker-creator/elements/CopyText.stories.tsx index 7f4dda056440..1b039d7830e6 100644 --- a/sticker-creator/elements/CopyText.stories.tsx +++ b/sticker-creator/elements/CopyText.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { CopyText } from './CopyText'; - import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { CopyText } from './CopyText'; + storiesOf('Sticker Creator/elements', module).add('CopyText', () => { const label = text('label', 'foo bar'); const value = text('value', 'foo bar'); diff --git a/sticker-creator/elements/CopyText.tsx b/sticker-creator/elements/CopyText.tsx index 3938282246fe..bb866126106d 100644 --- a/sticker-creator/elements/CopyText.tsx +++ b/sticker-creator/elements/CopyText.tsx @@ -10,27 +10,29 @@ export type Props = { onCopy?: () => unknown; }; -export const CopyText = React.memo(({ label, onCopy, value }: Props) => { - const i18n = useI18n(); - const handleClick = React.useCallback(() => { - copy(value); - if (onCopy) { - onCopy(); - } - }, [onCopy, value]); +export const CopyText: React.ComponentType = React.memo( + ({ label, onCopy, value }) => { + const i18n = useI18n(); + const handleClick = React.useCallback(() => { + copy(value); + if (onCopy) { + onCopy(); + } + }, [onCopy, value]); - return ( -
- - -
- ); -}); + return ( +
+ + +
+ ); + } +); diff --git a/sticker-creator/elements/DropZone.stories.tsx b/sticker-creator/elements/DropZone.stories.tsx index db9e94536ce8..da02175d202f 100644 --- a/sticker-creator/elements/DropZone.stories.tsx +++ b/sticker-creator/elements/DropZone.stories.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; -import { DropZone } from './DropZone'; - import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; +import { DropZone } from './DropZone'; + storiesOf('Sticker Creator/elements', module).add('DropZone', () => { return ; }); diff --git a/sticker-creator/elements/DropZone.tsx b/sticker-creator/elements/DropZone.tsx index 2dcf83bb6fbd..36b1e1de7d7d 100644 --- a/sticker-creator/elements/DropZone.tsx +++ b/sticker-creator/elements/DropZone.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { useDropzone } from 'react-dropzone'; +import { useDropzone, FileWithPath } from 'react-dropzone'; import * as styles from './DropZone.scss'; import { useI18n } from '../util/i18n'; @@ -21,12 +21,12 @@ const getClassName = ({ inner }: Props, isDragActive: boolean) => { return styles.standalone; }; -export const DropZone = (props: Props) => { +export const DropZone: React.ComponentType = props => { const { inner, onDrop, onDragActive } = props; const i18n = useI18n(); const handleDrop = React.useCallback( - files => { + (files: ReadonlyArray) => { onDrop(files.map(({ path }) => path)); }, [onDrop] diff --git a/sticker-creator/elements/LabeledCheckbox.stories.tsx b/sticker-creator/elements/LabeledCheckbox.stories.tsx index af206bf1ad80..ced0579e7495 100644 --- a/sticker-creator/elements/LabeledCheckbox.stories.tsx +++ b/sticker-creator/elements/LabeledCheckbox.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { LabeledCheckbox } from './LabeledCheckbox'; - import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { LabeledCheckbox } from './LabeledCheckbox'; + storiesOf('Sticker Creator/elements', module).add('Labeled Checkbox', () => { const child = text('label', 'foo bar'); const [checked, setChecked] = React.useState(false); diff --git a/sticker-creator/elements/LabeledCheckbox.tsx b/sticker-creator/elements/LabeledCheckbox.tsx index 53bad531c57c..4348ce26449a 100644 --- a/sticker-creator/elements/LabeledCheckbox.tsx +++ b/sticker-creator/elements/LabeledCheckbox.tsx @@ -17,7 +17,9 @@ const checkSvg = ( export const LabeledCheckbox = React.memo( ({ children, value, onChange }: Props) => { const handleChange = React.useCallback(() => { - onChange(!value); + if (onChange !== undefined) { + onChange(!value); + } }, [onChange, value]); const className = value ? styles.checkboxChecked : styles.checkbox; diff --git a/sticker-creator/elements/LabeledInput.stories.tsx b/sticker-creator/elements/LabeledInput.stories.tsx index c8772adae4ae..fef323c0c2cb 100644 --- a/sticker-creator/elements/LabeledInput.stories.tsx +++ b/sticker-creator/elements/LabeledInput.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { LabeledInput } from './LabeledInput'; - import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { LabeledInput } from './LabeledInput'; + storiesOf('Sticker Creator/elements', module).add('LabeledInput', () => { const child = text('label', 'foo bar'); const placeholder = text('placeholder', 'foo bar'); diff --git a/sticker-creator/elements/LabeledInput.tsx b/sticker-creator/elements/LabeledInput.tsx index 7acc808374d9..293e0a35bd3e 100644 --- a/sticker-creator/elements/LabeledInput.tsx +++ b/sticker-creator/elements/LabeledInput.tsx @@ -13,7 +13,9 @@ export const LabeledInput = React.memo( ({ children, value, placeholder, onChange }: Props) => { const handleChange = React.useCallback( (e: React.ChangeEvent) => { - onChange(e.currentTarget.value); + if (onChange !== undefined) { + onChange(e.currentTarget.value); + } }, [onChange] ); diff --git a/sticker-creator/elements/MessageBubble.stories.tsx b/sticker-creator/elements/MessageBubble.stories.tsx index a8de7c6fa486..c7ddef13fa89 100644 --- a/sticker-creator/elements/MessageBubble.stories.tsx +++ b/sticker-creator/elements/MessageBubble.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { MessageBubble } from './MessageBubble'; - import { storiesOf } from '@storybook/react'; import { number, text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { MessageBubble } from './MessageBubble'; + storiesOf('Sticker Creator/elements', module).add('MessageBubble', () => { const child = text('text', 'Foo bar banana baz'); const minutesAgo = number('minutesAgo', 3); diff --git a/sticker-creator/elements/MessageBubble.tsx b/sticker-creator/elements/MessageBubble.tsx index 222f84586a69..25a234dcb3bf 100644 --- a/sticker-creator/elements/MessageBubble.tsx +++ b/sticker-creator/elements/MessageBubble.tsx @@ -6,7 +6,10 @@ export type Props = Pick & { children: React.ReactNode; }; -export const MessageBubble = ({ children, minutesAgo }: Props) => { +export const MessageBubble: React.ComponentType = ({ + children, + minutesAgo, +}) => { return (
{children} diff --git a/sticker-creator/elements/MessageSticker.stories.tsx b/sticker-creator/elements/MessageSticker.stories.tsx index 5c1236e79647..2b773669f121 100644 --- a/sticker-creator/elements/MessageSticker.stories.tsx +++ b/sticker-creator/elements/MessageSticker.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { MessageSticker } from './MessageSticker'; - import { storiesOf } from '@storybook/react'; import { number, text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { MessageSticker } from './MessageSticker'; + storiesOf('Sticker Creator/elements', module).add('MessageSticker', () => { const image = text('image url', '/fixtures/512x515-thumbs-up-lincoln.webp'); const minutesAgo = number('minutesAgo', 3); diff --git a/sticker-creator/elements/MessageSticker.tsx b/sticker-creator/elements/MessageSticker.tsx index 63e4e995d88c..103e0f587e97 100644 --- a/sticker-creator/elements/MessageSticker.tsx +++ b/sticker-creator/elements/MessageSticker.tsx @@ -6,7 +6,11 @@ export type Props = MessageMetaProps & { image: string; }; -export const MessageSticker = ({ image, kind, minutesAgo }: Props) => { +export const MessageSticker: React.ComponentType = ({ + image, + kind, + minutesAgo, +}) => { return (
Sticker diff --git a/sticker-creator/elements/PageHeader.stories.tsx b/sticker-creator/elements/PageHeader.stories.tsx index ee8346d051d2..7661d6dad63d 100644 --- a/sticker-creator/elements/PageHeader.stories.tsx +++ b/sticker-creator/elements/PageHeader.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { PageHeader } from './PageHeader'; - import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { PageHeader } from './PageHeader'; + storiesOf('Sticker Creator/elements', module).add('PageHeader', () => { const child = text('text', 'foo bar'); diff --git a/sticker-creator/elements/ProgressBar.stories.tsx b/sticker-creator/elements/ProgressBar.stories.tsx index 4a7c6932e294..18b0edec582b 100644 --- a/sticker-creator/elements/ProgressBar.stories.tsx +++ b/sticker-creator/elements/ProgressBar.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { ProgressBar } from './ProgressBar'; - import { storiesOf } from '@storybook/react'; import { number } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { ProgressBar } from './ProgressBar'; + storiesOf('Sticker Creator/elements', module).add('ProgressBar', () => { const count = number('count', 5); const total = number('total', 10); diff --git a/sticker-creator/elements/StickerPreview.stories.tsx b/sticker-creator/elements/StickerPreview.stories.tsx index e87568a091fb..7d57ae9db900 100644 --- a/sticker-creator/elements/StickerPreview.stories.tsx +++ b/sticker-creator/elements/StickerPreview.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { StickerPreview } from './StickerPreview'; - import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { StickerPreview } from './StickerPreview'; + storiesOf('Sticker Creator/elements', module).add('StickerPreview', () => { const image = text('image url', '/fixtures/512x515-thumbs-up-lincoln.webp'); diff --git a/sticker-creator/elements/StoryRow.tsx b/sticker-creator/elements/StoryRow.tsx index 8ee8e185a597..cecea633d352 100644 --- a/sticker-creator/elements/StoryRow.tsx +++ b/sticker-creator/elements/StoryRow.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; import * as styles from './StoryRow.scss'; -export type Props = { - children: React.ReactChild; +type Props = { left?: boolean; right?: boolean; top?: boolean; @@ -29,6 +28,7 @@ const getClassName = ({ left, right, top, bottom }: Props) => { return styles.base; }; -export const StoryRow = (props: Props) => ( -
{props.children}
-); +export const StoryRow: React.ComponentType = ({ + children, + ...props +}) =>
{children}
; diff --git a/sticker-creator/elements/Toast.stories.tsx b/sticker-creator/elements/Toast.stories.tsx index f3cd8b937abe..a94d0d008537 100644 --- a/sticker-creator/elements/Toast.stories.tsx +++ b/sticker-creator/elements/Toast.stories.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { Toast } from './Toast'; - import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; import { action } from '@storybook/addon-actions'; +import { StoryRow } from './StoryRow'; +import { Toast } from './Toast'; + storiesOf('Sticker Creator/elements', module).add('Toast', () => { const child = text('text', 'foo bar'); diff --git a/sticker-creator/elements/Toast.tsx b/sticker-creator/elements/Toast.tsx index 51f9fe8e598f..f3123a3e9533 100644 --- a/sticker-creator/elements/Toast.tsx +++ b/sticker-creator/elements/Toast.tsx @@ -7,7 +7,11 @@ export type Props = React.HTMLProps & { }; export const Toast = React.memo(({ children, className, ...rest }: Props) => ( - )); diff --git a/sticker-creator/elements/Typography.stories.tsx b/sticker-creator/elements/Typography.stories.tsx index 434ac2b87b58..2f683ac806f6 100644 --- a/sticker-creator/elements/Typography.stories.tsx +++ b/sticker-creator/elements/Typography.stories.tsx @@ -1,27 +1,29 @@ -import * as React from 'react'; -import { StoryRow } from './StoryRow'; -import { H1, H2, Text } from './Typography'; +/* eslint-disable no-script-url, jsx-a11y/anchor-is-valid */ +import * as React from 'react'; import { storiesOf } from '@storybook/react'; import { text } from '@storybook/addon-knobs'; +import { StoryRow } from './StoryRow'; +import { H1, H2, Text } from './Typography'; + storiesOf('Sticker Creator/elements', module).add('Typography', () => { const child = text('text', 'foo bar'); return ( <> - +

{child}

- +

{child}

- + {child} {child} {child} {child} - + {child} {child} {child} {child}{' '} diff --git a/sticker-creator/elements/Typography.tsx b/sticker-creator/elements/Typography.tsx index 75d8746227fa..a4cfb1546337 100644 --- a/sticker-creator/elements/Typography.tsx +++ b/sticker-creator/elements/Typography.tsx @@ -35,7 +35,6 @@ export const Text = React.memo( children, className, center, - wide, secondary, ...rest }: Props & ParagraphProps) => ( diff --git a/sticker-creator/index.tsx b/sticker-creator/index.tsx index 40177ac54c56..ef0ac0fc4662 100644 --- a/sticker-creator/index.tsx +++ b/sticker-creator/index.tsx @@ -4,5 +4,6 @@ import { Root } from './root'; const root = document.getElementById('root'); +// eslint-disable-next-line no-console console.log('Sticker Creator: Starting root'); render(, root); diff --git a/sticker-creator/root.tsx b/sticker-creator/root.tsx index d31aa2e008b0..a4fe59471145 100644 --- a/sticker-creator/root.tsx +++ b/sticker-creator/root.tsx @@ -1,4 +1,3 @@ -// tslint:disable-next-line no-submodule-imports import { hot } from 'react-hot-loader/root'; import * as React from 'react'; import { Provider as ReduxProvider } from 'react-redux'; @@ -8,7 +7,12 @@ import { history } from './util/history'; import { store } from './store'; import { I18n } from './util/i18n'; -// @ts-ignore +declare global { + interface Window { + localeMessages: { [key: string]: { message: string } }; + } +} + const { localeMessages } = window; const ColdRoot = () => ( diff --git a/sticker-creator/store/ducks/stickers.ts b/sticker-creator/store/ducks/stickers.ts index 2da8eeffeba0..b67422631823 100644 --- a/sticker-creator/store/ducks/stickers.ts +++ b/sticker-creator/store/ducks/stickers.ts @@ -1,4 +1,4 @@ -// tslint:disable no-dynamic-delete +/* eslint-disable no-param-reassign */ import { useMemo } from 'react'; import { @@ -13,8 +13,9 @@ import { clamp, find, isNumber, pull, remove, take, uniq } from 'lodash'; import { SortEnd } from 'react-sortable-hoc'; import { bindActionCreators } from 'redux'; import arrayMove from 'array-move'; +// eslint-disable-next-line import/no-cycle import { AppState } from '../reducer'; -import { PackMetaData, WebpData } from '../../util/preload'; +import { PackMetaData, WebpData, StickerData } from '../../util/preload'; import { EmojiPickDataType } from '../../../ts/components/emoji/EmojiPicker'; import { convertShortName } from '../../../ts/components/emoji/lib'; @@ -46,6 +47,16 @@ export const minStickers = 1; export const maxStickers = 200; export const maxByteSize = 100 * 1024; +interface StateStickerData { + readonly webp?: WebpData; + readonly emoji?: EmojiPickDataType; +} + +interface StateToastData { + key: string; + subs?: Array; +} + export type State = { readonly order: Array; readonly cover?: WebpData; @@ -53,15 +64,28 @@ export type State = { readonly author: string; readonly packId: string; readonly packKey: string; - readonly toasts: Array<{ key: string; subs?: Array }>; + readonly toasts: Array; readonly data: { - readonly [src: string]: { - readonly webp?: WebpData; - readonly emoji?: EmojiPickDataType; - }; + readonly [src: string]: StateStickerData; }; }; +export type Actions = { + addWebp: typeof addWebp; + initializeStickers: typeof initializeStickers; + removeSticker: typeof removeSticker; + moveSticker: typeof moveSticker; + setCover: typeof setCover; + setEmoji: typeof setEmoji; + setTitle: typeof setTitle; + setAuthor: typeof setAuthor; + setPackMeta: typeof setPackMeta; + addToast: typeof addToast; + dismissToast: typeof dismissToast; + reset: typeof reset; + resetStatus: typeof resetStatus; +}; + const defaultState: State = { order: [], data: {}, @@ -193,21 +217,22 @@ export const reducer = reduceReducers( defaultState ); -export const useTitle = () => +export const useTitle = (): string => useSelector(({ stickers }: AppState) => stickers.title); -export const useAuthor = () => + +export const useAuthor = (): string => useSelector(({ stickers }: AppState) => stickers.author); -export const useCover = () => +export const useCover = (): WebpData | undefined => useSelector(({ stickers }: AppState) => stickers.cover); -export const useStickerOrder = () => +export const useStickerOrder = (): Array => useSelector(({ stickers }: AppState) => stickers.order); -export const useStickerData = (src: string) => +export const useStickerData = (src: string): StateStickerData => useSelector(({ stickers }: AppState) => stickers.data[src]); -export const useStickersReady = () => +export const useStickersReady = (): boolean => useSelector( ({ stickers }: AppState) => stickers.order.length >= minStickers && @@ -215,12 +240,12 @@ export const useStickersReady = () => Object.values(stickers.data).every(({ webp }) => !!webp) ); -export const useEmojisReady = () => +export const useEmojisReady = (): boolean => useSelector(({ stickers }: AppState) => Object.values(stickers.data).every(({ emoji }) => !!emoji) ); -export const useAllDataValid = () => { +export const useAllDataValid = (): boolean => { const stickersReady = useStickersReady(); const emojisReady = useEmojisReady(); const cover = useCover(); @@ -236,10 +261,12 @@ const selectUrl = createSelector( (id, key) => `https://signal.art/addstickers/#pack_id=${id}&pack_key=${key}` ); -export const usePackUrl = () => useSelector(selectUrl); -export const useToasts = () => +export const usePackUrl = (): string => useSelector(selectUrl); + +export const useToasts = (): Array => useSelector(({ stickers }: AppState) => stickers.toasts); -export const useAddMoreCount = () => + +export const useAddMoreCount = (): number => useSelector(({ stickers }: AppState) => clamp(minStickers - stickers.order.length, 0, minStickers) ); @@ -251,21 +278,23 @@ const selectOrderedData = createSelector( order.map(id => ({ ...data[id], emoji: convertShortName( - data[id].emoji.shortName, - data[id].emoji.skinTone + (data[id].emoji as EmojiPickDataType).shortName, + (data[id].emoji as EmojiPickDataType).skinTone ), })) ); -export const useSelectOrderedData = () => useSelector(selectOrderedData); +export const useSelectOrderedData = (): Array => + useSelector(selectOrderedData); const selectOrderedImagePaths = createSelector(selectOrderedData, data => - data.map(({ webp }) => webp.src) + data.map(({ webp }) => (webp as WebpData).src) ); -export const useOrderedImagePaths = () => useSelector(selectOrderedImagePaths); +export const useOrderedImagePaths = (): Array => + useSelector(selectOrderedImagePaths); -export const useStickerActions = () => { +export const useStickerActions = (): Actions => { const dispatch = useDispatch(); return useMemo( diff --git a/sticker-creator/store/reducer.ts b/sticker-creator/store/reducer.ts index b075bec06fc4..e5b95e08649c 100644 --- a/sticker-creator/store/reducer.ts +++ b/sticker-creator/store/reducer.ts @@ -1,4 +1,5 @@ import { combineReducers, Reducer } from 'redux'; +// eslint-disable-next-line import/no-cycle import { reducer as stickers } from './ducks/stickers'; export const reducer = combineReducers({ diff --git a/sticker-creator/tsconfig.json b/sticker-creator/tsconfig.json new file mode 100644 index 000000000000..692198e85e63 --- /dev/null +++ b/sticker-creator/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "lib": ["dom", "es2017"], + "jsx": "react", + "rootDir": "." + } +} diff --git a/sticker-creator/util/i18n.tsx b/sticker-creator/util/i18n.tsx index dd04b9cf0678..c7d63994e8fb 100644 --- a/sticker-creator/util/i18n.tsx +++ b/sticker-creator/util/i18n.tsx @@ -16,7 +16,7 @@ export type I18nProps = { messages: { [key: string]: { message: string } }; }; -export const I18n = ({ messages, children }: I18nProps) => { +export const I18n = ({ messages, children }: I18nProps): JSX.Element => { const getMessage = React.useCallback( (key, substitutions) => { if (Array.isArray(substitutions) && substitutions.length > 1) { @@ -28,7 +28,8 @@ export const I18n = ({ messages, children }: I18nProps) => { const { message } = messages[key]; if (!substitutions) { return message; - } else if (Array.isArray(substitutions)) { + } + if (Array.isArray(substitutions)) { return substitutions.reduce( (result, substitution) => result.toString().replace(/\$.+?\$/, substitution.toString()), @@ -50,7 +51,7 @@ export const I18n = ({ messages, children }: I18nProps) => { const placeholderName = match[1]; const value = substitutions[placeholderName]; if (!value) { - // tslint:disable-next-line no-console + // eslint-disable-next-line no-console console.error( `i18n: Value not provided for placeholder ${placeholderName} in key '${key}'` ); @@ -75,4 +76,4 @@ export const I18n = ({ messages, children }: I18nProps) => { ); }; -export const useI18n = () => React.useContext(I18nContext); +export const useI18n = (): I18nFn => React.useContext(I18nContext); diff --git a/sticker-creator/util/preload.ts b/sticker-creator/util/preload.ts index aa8c37259026..ebb5c0479ea7 100644 --- a/sticker-creator/util/preload.ts +++ b/sticker-creator/util/preload.ts @@ -1,5 +1,12 @@ import { Metadata } from 'sharp'; +declare global { + interface Window { + convertToWebp: ConvertToWebpFn; + encryptAndUpload: EncryptAndUploadFn; + } +} + export type WebpData = { buffer: Buffer; src: string; @@ -13,9 +20,6 @@ export type ConvertToWebpFn = ( height?: number ) => Promise; -// @ts-ignore -export const convertToWebp: ConvertToWebpFn = window.convertToWebp; - export type StickerData = { webp?: WebpData; emoji?: string }; export type PackMetaData = { packId: string; key: string }; @@ -26,5 +30,4 @@ export type EncryptAndUploadFn = ( onProgress?: () => unknown ) => Promise; -// @ts-ignore -export const encryptAndUpload: EncryptAndUploadFn = window.encryptAndUpload; +export const { encryptAndUpload, convertToWebp } = window; diff --git a/ts/components/conversation/MessageDetail.tsx b/ts/components/conversation/MessageDetail.tsx index a99b1f95f610..0012cf545a5e 100644 --- a/ts/components/conversation/MessageDetail.tsx +++ b/ts/components/conversation/MessageDetail.tsx @@ -182,7 +182,7 @@ export class MessageDetail extends React.Component { // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
- +
diff --git a/ts/model-types.d.ts b/ts/model-types.d.ts index 6473edf9cc71..9a76166c8bba 100644 --- a/ts/model-types.d.ts +++ b/ts/model-types.d.ts @@ -18,7 +18,9 @@ type DeletesAttributesType = { targetSentTimestamp: number; }; -declare class DeletesModelType extends Backbone.Model { +export declare class DeletesModelType extends Backbone.Model< + DeletesAttributesType +> { forMessage(message: MessageModelType): Array; onDelete(doe: DeletesAttributesType): Promise; } @@ -47,7 +49,9 @@ export type MessageAttributesType = { sourceUuid?: string; }; -declare class MessageModelType extends Backbone.Model { +export declare class MessageModelType extends Backbone.Model< + MessageAttributesType +> { id: string; static updateTimers(): void; diff --git a/ts/state/createStore.ts b/ts/state/createStore.ts index 1c8c9a77d9f8..e0710c0371a4 100644 --- a/ts/state/createStore.ts +++ b/ts/state/createStore.ts @@ -1,16 +1,27 @@ -import { applyMiddleware, createStore as reduxCreateStore } from 'redux'; +/* eslint-disable no-console */ + +import { + applyMiddleware, + createStore as reduxCreateStore, + DeepPartial, + Store, +} from 'redux'; import promise from 'redux-promise-middleware'; import { createLogger } from 'redux-logger'; -import { reducer } from './reducer'; +import { reducer, StateType } from './reducer'; + +declare global { + interface Console { + _log: Console['log']; + } +} -// @ts-ignore const env = window.getEnvironment(); // So Redux logging doesn't go to disk, and so we can get colors/styles const directConsole = { - // @ts-ignore log: console._log, groupCollapsed: console.groupCollapsed, group: console.group, @@ -27,7 +38,7 @@ const logger = createLogger({ // Exclude logger if we're in production mode const middlewareList = env === 'production' ? [promise] : [promise, logger]; -const enhancer = applyMiddleware.apply(null, middlewareList); +const enhancer = applyMiddleware(...middlewareList); -export const createStore = (initialState: any) => +export const createStore = (initialState: DeepPartial): Store => reduxCreateStore(reducer, initialState, enhancer); diff --git a/ts/state/ducks/calling.ts b/ts/state/ducks/calling.ts index f1f45c8a09c0..844289bc2701 100644 --- a/ts/state/ducks/calling.ts +++ b/ts/state/ducks/calling.ts @@ -17,7 +17,7 @@ import { // State -export type CallId = any; +export type CallId = unknown; export type CallDetailsType = { callId: CallId; diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index c117712849ae..052757ad592a 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ import { difference, fromPairs, @@ -113,7 +114,7 @@ export type MessageType = { deletedForEveryone?: boolean; errors?: Array; - group_update?: any; + group_update?: unknown; // No need to go beyond this; unused at this stage, since this goes into // a reducer still in plain JavaScript and comes out well-formed @@ -393,7 +394,10 @@ function removeAllConversations(): RemoveAllConversationsActionType { }; } -function selectMessage(messageId: string, conversationId: string) { +function selectMessage( + messageId: string, + conversationId: string +): MessageSelectedActionType { return { type: 'MESSAGE_SELECTED', payload: { @@ -567,13 +571,13 @@ function openConversationExternal( }; } -function showInbox() { +function showInbox(): ShowInboxActionType { return { type: 'SHOW_INBOX', payload: null, }; } -function showArchivedConversations() { +function showArchivedConversations(): ShowArchivedConversationsActionType { return { type: 'SHOW_ARCHIVED_CONVERSATIONS', payload: null, @@ -596,7 +600,7 @@ function getEmptyState(): ConversationsStateType { function hasMessageHeightChanged( message: MessageType, previous: MessageType -): Boolean { +): boolean { const messageAttachments = message.attachments || []; const previousAttachments = previous.attachments || []; @@ -687,8 +691,7 @@ export function reducer( const { id, data } = payload; const { conversationLookup } = state; - let showArchived = state.showArchived; - let selectedConversation = state.selectedConversation; + let { showArchived, selectedConversation } = state; const existing = conversationLookup[id]; // In the change case we only modify the lookup if we already had that conversation @@ -1087,7 +1090,8 @@ export function reducer( } } - // Update oldest and newest if we receive older/newer messages (or duplicated timestamps!) + // Update oldest and newest if we receive older/newer + // messages (or duplicated timestamps!) if (first && oldest && first.received_at <= oldest.received_at) { oldest = pick(first, ['id', 'received_at']); } diff --git a/ts/state/ducks/emojis.ts b/ts/state/ducks/emojis.ts index a4229c6a79fe..b656fc0e926f 100644 --- a/ts/state/ducks/emojis.ts +++ b/ts/state/ducks/emojis.ts @@ -31,7 +31,7 @@ export const actions = { onUseEmoji, }; -export const useActions = () => useBoundActions(actions); +export const useActions = (): typeof actions => useBoundActions(actions); function onUseEmoji({ shortName }: EmojiPickDataType): OnUseEmojiAction { return { diff --git a/ts/state/ducks/items.ts b/ts/state/ducks/items.ts index 11a436f1e20f..684ab55e9e18 100644 --- a/ts/state/ducks/items.ts +++ b/ts/state/ducks/items.ts @@ -9,7 +9,7 @@ import { useBoundActions } from '../../util/hooks'; // State export type ItemsStateType = { - readonly [key: string]: any; + readonly [key: string]: unknown; }; // Actions @@ -23,7 +23,7 @@ type ItemPutExternalAction = { type: 'items/PUT_EXTERNAL'; payload: { key: string; - value: any; + value: unknown; }; }; @@ -58,9 +58,9 @@ export const actions = { resetItems, }; -export const useActions = () => useBoundActions(actions); +export const useActions = (): typeof actions => useBoundActions(actions); -function putItem(key: string, value: any): ItemPutAction { +function putItem(key: string, value: unknown): ItemPutAction { storageShim.put(key, value); return { @@ -69,7 +69,7 @@ function putItem(key: string, value: any): ItemPutAction { }; } -function putItemExternal(key: string, value: any): ItemPutExternalAction { +function putItemExternal(key: string, value: unknown): ItemPutExternalAction { return { type: 'items/PUT_EXTERNAL', payload: { @@ -139,4 +139,5 @@ const selectRecentEmojis = createSelector( recents => recents.filter(isShortName) ); -export const useRecentEmojis = () => useSelector(selectRecentEmojis); +export const useRecentEmojis = (): Array => + useSelector(selectRecentEmojis); diff --git a/ts/state/ducks/search.ts b/ts/state/ducks/search.ts index 67da48b124b3..c7963e9589e3 100644 --- a/ts/state/ducks/search.ts +++ b/ts/state/ducks/search.ts @@ -292,7 +292,7 @@ async function queryConversationsAndContacts( for (let i = 0; i < max; i += 1) { const conversation = searchResults[i]; - if (conversation.type === 'private' && !Boolean(conversation.lastMessage)) { + if (conversation.type === 'private' && !conversation.lastMessage) { contacts.push(conversation.id); } else { conversations.push(conversation.id); diff --git a/ts/state/ducks/stickers.ts b/ts/state/ducks/stickers.ts index f931dfcdc225..e10300c58634 100644 --- a/ts/state/ducks/stickers.ts +++ b/ts/state/ducks/stickers.ts @@ -376,7 +376,10 @@ export function reducer( action: StickersActionType ): StickersStateType { if (action.type === 'stickers/STICKER_PACK_ADDED') { - const { payload } = action; + // ts complains due to `stickers: {}` being overridden by the payload + // but without full confidence that that's the case, `any` and ignore + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { payload } = action as any; const newPack = { stickers: {}, ...payload, diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index fb667c5a3149..c609535d8858 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -92,5 +92,5 @@ export const reducers = { user, }; -// @ts-ignore: AnyAction breaks strong type checking inside reducers -export const reducer = combineReducers(reducers); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const reducer = combineReducers(reducers as any); diff --git a/ts/state/roots/createCallManager.tsx b/ts/state/roots/createCallManager.tsx index 0161a54acf4c..d3cacb7c90e1 100644 --- a/ts/state/roots/createCallManager.tsx +++ b/ts/state/roots/createCallManager.tsx @@ -7,9 +7,11 @@ import { SmartCallManager } from '../smart/CallManager'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredCallManager = SmartCallManager as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ -export const createCallManager = (store: Store) => ( +export const createCallManager = (store: Store): React.ReactElement => ( diff --git a/ts/state/roots/createCompositionArea.tsx b/ts/state/roots/createCompositionArea.tsx index 7d64f6888538..c3c02dcf2e3c 100644 --- a/ts/state/roots/createCompositionArea.tsx +++ b/ts/state/roots/createCompositionArea.tsx @@ -7,9 +7,14 @@ import { SmartCompositionArea } from '../smart/CompositionArea'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredCompositionArea = SmartCompositionArea as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ -export const createCompositionArea = (store: Store, props: Object) => ( +export const createCompositionArea = ( + store: Store, + props: Record +): React.ReactElement => ( diff --git a/ts/state/roots/createLeftPane.tsx b/ts/state/roots/createLeftPane.tsx index 73cde2a75291..5d577d9f60c8 100644 --- a/ts/state/roots/createLeftPane.tsx +++ b/ts/state/roots/createLeftPane.tsx @@ -7,9 +7,11 @@ import { SmartLeftPane } from '../smart/LeftPane'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredLeftPane = SmartLeftPane as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ -export const createLeftPane = (store: Store) => ( +export const createLeftPane = (store: Store): React.ReactElement => ( diff --git a/ts/state/roots/createSafetyNumberViewer.tsx b/ts/state/roots/createSafetyNumberViewer.tsx index e67094522017..f93f2312ce1e 100644 --- a/ts/state/roots/createSafetyNumberViewer.tsx +++ b/ts/state/roots/createSafetyNumberViewer.tsx @@ -7,14 +7,19 @@ import { SmartSafetyNumberViewer } from '../smart/SafetyNumberViewer'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredSafetyNumberViewer = SmartSafetyNumberViewer as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ type Props = { contactID: string; onClose?: () => void; }; -export const createSafetyNumberViewer = (store: Store, props: Props) => ( +export const createSafetyNumberViewer = ( + store: Store, + props: Props +): React.ReactElement => ( diff --git a/ts/state/roots/createShortcutGuideModal.tsx b/ts/state/roots/createShortcutGuideModal.tsx index 2233058d5951..4910d564f0e7 100644 --- a/ts/state/roots/createShortcutGuideModal.tsx +++ b/ts/state/roots/createShortcutGuideModal.tsx @@ -7,9 +7,14 @@ import { SmartShortcutGuideModal } from '../smart/ShortcutGuideModal'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredShortcutGuideModal = SmartShortcutGuideModal as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ -export const createShortcutGuideModal = (store: Store, props: Object) => ( +export const createShortcutGuideModal = ( + store: Store, + props: Record +): React.ReactElement => ( diff --git a/ts/state/roots/createStickerManager.tsx b/ts/state/roots/createStickerManager.tsx index 47523e361429..7eaf953b1d08 100644 --- a/ts/state/roots/createStickerManager.tsx +++ b/ts/state/roots/createStickerManager.tsx @@ -7,9 +7,11 @@ import { SmartStickerManager } from '../smart/StickerManager'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredStickerManager = SmartStickerManager as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ -export const createStickerManager = (store: Store) => ( +export const createStickerManager = (store: Store): React.ReactElement => ( diff --git a/ts/state/roots/createStickerPreviewModal.tsx b/ts/state/roots/createStickerPreviewModal.tsx index 9ce2de21ad3a..b986c9fb91fd 100644 --- a/ts/state/roots/createStickerPreviewModal.tsx +++ b/ts/state/roots/createStickerPreviewModal.tsx @@ -7,9 +7,14 @@ import { SmartStickerPreviewModal } from '../smart/StickerPreviewModal'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredStickerPreviewModal = SmartStickerPreviewModal as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ -export const createStickerPreviewModal = (store: Store, props: Object) => ( +export const createStickerPreviewModal = ( + store: Store, + props: Record +): React.ReactElement => ( diff --git a/ts/state/roots/createTimeline.tsx b/ts/state/roots/createTimeline.tsx index 36ccf29ce49c..c46be1ee8c65 100644 --- a/ts/state/roots/createTimeline.tsx +++ b/ts/state/roots/createTimeline.tsx @@ -7,9 +7,14 @@ import { SmartTimeline } from '../smart/Timeline'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredTimeline = SmartTimeline as any; +/* eslint-disable @typescript-eslint/no-explicit-any */ -export const createTimeline = (store: Store, props: Object) => ( +export const createTimeline = ( + store: Store, + props: Record +): React.ReactElement => ( diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index a037f3f5652a..5dab1dddc6f9 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -49,7 +49,7 @@ export const getSelectedMessage = createSelector( getConversations, (state: ConversationsStateType): SelectedMessageType | undefined => { if (!state.selectedMessage) { - return; + return undefined; } return { @@ -136,21 +136,19 @@ export const _getLeftPaneLists = ( const max = values.length; for (let i = 0; i < max; i += 1) { let conversation = values[i]; - if (!conversation.activeAt) { - continue; - } + if (conversation.activeAt) { + if (selectedConversation === conversation.id) { + conversation = { + ...conversation, + isSelected: true, + }; + } - if (selectedConversation === conversation.id) { - conversation = { - ...conversation, - isSelected: true, - }; - } - - if (conversation.isArchived) { - archivedConversations.push(conversation); - } else { - conversations.push(conversation); + if (conversation.isArchived) { + archivedConversations.push(conversation); + } else { + conversations.push(conversation); + } } } @@ -220,7 +218,7 @@ export const getConversationSelector = createSelector( return (id: string) => { const conversation = lookup[id]; if (!conversation) { - return; + return undefined; } return selector(conversation); @@ -236,17 +234,12 @@ export const getConversationSelector = createSelector( // - message details export function _messageSelector( message: MessageType, - // @ts-ignore - ourNumber: string, - // @ts-ignore - regionCode: string, + _ourNumber: string, + _regionCode: string, interactionMode: 'mouse' | 'keyboard', - // @ts-ignore - conversation?: ConversationType, - // @ts-ignore - author?: ConversationType, - // @ts-ignore - quoted?: ConversationType, + _conversation?: ConversationType, + _author?: ConversationType, + _quoted?: ConversationType, selectedMessageId?: string, selectedMessageCounter?: number ): TimelineItemType { @@ -319,7 +312,7 @@ export const getMessageSelector = createSelector( return (id: string) => { const message = messageLookup[id]; if (!message) { - return; + return undefined; } const { conversationId, source, type, quote } = message; @@ -441,7 +434,7 @@ export const getConversationMessagesSelector = createSelector( return (id: string): TimelinePropsType | undefined => { const conversation = messagesByConversation[id]; if (!conversation) { - return; + return undefined; } return conversationMessagesSelector(conversation); diff --git a/ts/state/selectors/search.ts b/ts/state/selectors/search.ts index 1fcfeb4e8000..f8c00eb9da52 100644 --- a/ts/state/selectors/search.ts +++ b/ts/state/selectors/search.ts @@ -207,7 +207,7 @@ export const getSearchResults = createSelector( items, messagesLoading, noResults, - regionCode: regionCode, + regionCode, searchConversationName, searchTerm: state.query, selectedConversationId, @@ -218,14 +218,10 @@ export const getSearchResults = createSelector( export function _messageSearchResultSelector( message: MessageSearchResultType, - // @ts-ignore - ourNumber: string, - // @ts-ignore - regionCode: string, - // @ts-ignore - sender?: ConversationType, - // @ts-ignore - recipient?: ConversationType, + _ourNumber: string, + _regionCode: string, + _sender?: ConversationType, + _recipient?: ConversationType, searchConversationId?: string, selectedMessageId?: string ): MessageSearchResultPropsDataType { @@ -282,7 +278,7 @@ export const getMessageSearchResultSelector = createSelector( return (id: string) => { const message = messageSearchResultLookup[id]; if (!message) { - return; + return undefined; } const { conversationId, source, type } = message; diff --git a/ts/state/selectors/stickers.ts b/ts/state/selectors/stickers.ts index cd2966724f7e..9b197114cab8 100644 --- a/ts/state/selectors/stickers.ts +++ b/ts/state/selectors/stickers.ts @@ -31,12 +31,12 @@ const getSticker = ( ): StickerType | undefined => { const pack = packs[packId]; if (!pack) { - return; + return undefined; } const sticker = pack.stickers[stickerId]; if (!sticker) { - return; + return undefined; } const isEphemeral = pack.status === 'ephemeral'; @@ -67,7 +67,7 @@ export const translatePackFromDB = ( blessedPacks: Dictionary, stickersPath: string, tempPath: string -) => { +): StickerPackType => { const { id, stickers, status, coverStickerId } = pack; const isEphemeral = status === 'ephemeral'; @@ -92,7 +92,7 @@ export const translatePackFromDB = ( const filterAndTransformPacks = ( packs: Dictionary, packFilter: (sticker: StickerPackDBType) => boolean, - packSort: (sticker: StickerPackDBType) => any, + packSort: (sticker: StickerPackDBType) => number | null, blessedPacks: Dictionary, stickersPath: string, tempPath: string diff --git a/ts/state/selectors/user.ts b/ts/state/selectors/user.ts index c4e2517b4ceb..7ad36cdaf0fa 100644 --- a/ts/state/selectors/user.ts +++ b/ts/state/selectors/user.ts @@ -32,7 +32,7 @@ export const getUserUuid = createSelector( export const getUserAgent = createSelector( getItems, - (state: ItemsStateType): string => state.userAgent + (state: ItemsStateType): string => state.userAgent as string ); export const getIntl = createSelector( diff --git a/ts/state/smart/CallManager.tsx b/ts/state/smart/CallManager.tsx index c995ca612b47..fa5765da49e7 100644 --- a/ts/state/smart/CallManager.tsx +++ b/ts/state/smart/CallManager.tsx @@ -10,7 +10,9 @@ import { SmartCallingDeviceSelection } from './CallingDeviceSelection'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredCallingDeviceSelection = SmartCallingDeviceSelection as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ function renderDeviceSelection(): JSX.Element { return ; diff --git a/ts/state/smart/CompositionArea.tsx b/ts/state/smart/CompositionArea.tsx index eaa56f0d5fb1..51790ddbc1d4 100644 --- a/ts/state/smart/CompositionArea.tsx +++ b/ts/state/smart/CompositionArea.tsx @@ -89,4 +89,5 @@ const dispatchPropsMap = { const smart = connect(mapStateToProps, dispatchPropsMap); -export const SmartCompositionArea = smart(CompositionArea); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const SmartCompositionArea = smart(CompositionArea as any); diff --git a/ts/state/smart/ContactName.tsx b/ts/state/smart/ContactName.tsx index 67f4b85a3bad..e47e5aa550a8 100644 --- a/ts/state/smart/ContactName.tsx +++ b/ts/state/smart/ContactName.tsx @@ -16,7 +16,7 @@ type ExternalProps = { conversationId: string; }; -export const SmartContactName = (props: ExternalProps) => { +export const SmartContactName: React.ComponentType = props => { const { conversationId } = props; const i18n = useSelector(getIntl); const getConversation = useSelector( diff --git a/ts/state/smart/LeftPane.tsx b/ts/state/smart/LeftPane.tsx index a6bfd6d91ba3..0d8c1b2d8b3d 100644 --- a/ts/state/smart/LeftPane.tsx +++ b/ts/state/smart/LeftPane.tsx @@ -21,12 +21,14 @@ import { SmartUpdateDialog } from './UpdateDialog'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredSmartMainHeader = SmartMainHeader as any; const FilteredSmartMessageSearchResult = SmartMessageSearchResult as any; const FilteredSmartNetworkStatus = SmartNetworkStatus as any; const FilteredSmartUpdateDialog = SmartUpdateDialog as any; const FilteredSmartExpiredBuildDialog = SmartExpiredBuildDialog as any; const FilteredSmartRelinkDialog = SmartRelinkDialog as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ function renderExpiredBuildDialog(): JSX.Element { return ; diff --git a/ts/state/smart/Timeline.tsx b/ts/state/smart/Timeline.tsx index 5286cab0f61e..071df6241781 100644 --- a/ts/state/smart/Timeline.tsx +++ b/ts/state/smart/Timeline.tsx @@ -22,11 +22,13 @@ import { SmartEmojiPicker } from './EmojiPicker'; // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredSmartTimelineItem = SmartTimelineItem as any; const FilteredSmartTypingBubble = SmartTypingBubble as any; const FilteredSmartLastSeenIndicator = SmartLastSeenIndicator as any; const FilteredSmartHeroRow = SmartHeroRow as any; const FilteredSmartTimelineLoadingRow = SmartTimelineLoadingRow as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ type ExternalProps = { id: string; @@ -38,7 +40,7 @@ type ExternalProps = { function renderItem( messageId: string, conversationId: string, - actionProps: Object + actionProps: Record ): JSX.Element { return ( ); } @@ -112,4 +114,5 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { const smart = connect(mapStateToProps, mapDispatchToProps); -export const SmartTimeline = smart(Timeline); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const SmartTimeline = smart(Timeline as any); diff --git a/ts/state/smart/TimelineItem.tsx b/ts/state/smart/TimelineItem.tsx index cf0b262869ca..c7eaf110ee6b 100644 --- a/ts/state/smart/TimelineItem.tsx +++ b/ts/state/smart/TimelineItem.tsx @@ -20,7 +20,9 @@ type ExternalProps = { // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +/* eslint-disable @typescript-eslint/no-explicit-any */ const FilteredSmartContactName = SmartContactName as any; +/* eslint-enable @typescript-eslint/no-explicit-any */ function renderContact(conversationId: string): JSX.Element { return ; diff --git a/ts/state/smart/TimelineLoadingRow.tsx b/ts/state/smart/TimelineLoadingRow.tsx index 77472e8f9367..97e2a18685d8 100644 --- a/ts/state/smart/TimelineLoadingRow.tsx +++ b/ts/state/smart/TimelineLoadingRow.tsx @@ -1,6 +1,6 @@ +import { isNumber } from 'lodash'; import { connect } from 'react-redux'; import { mapDispatchToProps } from '../actions'; -import { isNumber } from 'lodash'; import { STATE_ENUM, @@ -26,11 +26,16 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { const { isLoadingMessages, loadCountdownStart } = conversation; - const loadingState: STATE_ENUM = isLoadingMessages - ? 'loading' - : isNumber(loadCountdownStart) - ? 'countdown' - : 'idle'; + let loadingState: STATE_ENUM; + + if (isLoadingMessages) { + loadingState = 'loading'; + } else if (isNumber(loadCountdownStart)) { + loadingState = 'countdown'; + } else { + loadingState = 'idle'; + } + const duration = loadingState === 'countdown' ? LOAD_COUNTDOWN : undefined; const expiresAt = loadingState === 'countdown' && loadCountdownStart diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index 736798a34c36..8bc42acccec0 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -64,7 +64,7 @@ export function getExtensionForDisplay({ } if (!contentType) { - return; + return undefined; } const slash = contentType.indexOf('/'); @@ -72,10 +72,12 @@ export function getExtensionForDisplay({ return contentType.slice(slash + 1); } - return; + return undefined; } -export function isAudio(attachments?: Array) { +export function isAudio( + attachments?: Array +): boolean | undefined { return ( attachments && attachments[0] && @@ -84,7 +86,9 @@ export function isAudio(attachments?: Array) { ); } -export function canDisplayImage(attachments?: Array) { +export function canDisplayImage( + attachments?: Array +): boolean | 0 | undefined { const { height, width } = attachments && attachments[0] ? attachments[0] : { height: 0, width: 0 }; @@ -98,7 +102,7 @@ export function canDisplayImage(attachments?: Array) { ); } -export function getThumbnailUrl(attachment: AttachmentType) { +export function getThumbnailUrl(attachment: AttachmentType): string { if (attachment.thumbnail) { return attachment.thumbnail.url; } @@ -106,7 +110,7 @@ export function getThumbnailUrl(attachment: AttachmentType) { return getUrl(attachment); } -export function getUrl(attachment: AttachmentType) { +export function getUrl(attachment: AttachmentType): string { if (attachment.screenshot) { return attachment.screenshot.url; } @@ -114,7 +118,9 @@ export function getUrl(attachment: AttachmentType) { return attachment.url; } -export function isImage(attachments?: Array) { +export function isImage( + attachments?: Array +): boolean | undefined { return ( attachments && attachments[0] && @@ -123,14 +129,18 @@ export function isImage(attachments?: Array) { ); } -export function isImageAttachment(attachment: AttachmentType) { +export function isImageAttachment( + attachment: AttachmentType +): boolean | undefined { return ( attachment && attachment.contentType && isImageTypeSupported(attachment.contentType) ); } -export function hasImage(attachments?: Array) { +export function hasImage( + attachments?: Array +): string | boolean | undefined { return ( attachments && attachments[0] && @@ -138,11 +148,15 @@ export function hasImage(attachments?: Array) { ); } -export function isVideo(attachments?: Array) { +export function isVideo( + attachments?: Array +): boolean | undefined { return attachments && isVideoAttachment(attachments[0]); } -export function isVideoAttachment(attachment?: AttachmentType) { +export function isVideoAttachment( + attachment?: AttachmentType +): boolean | undefined { return ( attachment && attachment.contentType && @@ -150,7 +164,9 @@ export function isVideoAttachment(attachment?: AttachmentType) { ); } -export function hasVideoScreenshot(attachments?: Array) { +export function hasVideoScreenshot( + attachments?: Array +): string | null | undefined { const firstAttachment = attachments ? attachments[0] : null; return ( @@ -308,7 +324,7 @@ export const isFile = (attachment: Attachment): boolean => { export const isVoiceMessage = (attachment: Attachment): boolean => { const flag = SignalService.AttachmentPointer.Flags.VOICE_MESSAGE; const hasFlag = - // tslint:disable-next-line no-bitwise + // eslint-disable-next-line no-bitwise !is.undefined(attachment.flags) && (attachment.flags & flag) === flag; if (hasFlag) { return true; @@ -390,7 +406,7 @@ export const getFileExtension = ( attachment: Attachment ): string | undefined => { if (!attachment.contentType) { - return; + return undefined; } switch (attachment.contentType) { diff --git a/ts/types/Contact.tsx b/ts/types/Contact.tsx index 349c85027fbe..955b9c4572d3 100644 --- a/ts/types/Contact.tsx +++ b/ts/types/Contact.tsx @@ -1,6 +1,4 @@ -// @ts-ignore -import Attachments from '../../app/attachments'; -import { format as formatPhoneNumber } from '../types/PhoneNumber'; +import { format as formatPhoneNumber } from './PhoneNumber'; export interface ContactType { name?: Name; @@ -76,7 +74,7 @@ export function contactSelector( signalAccount?: string; getAbsoluteAttachmentPath: (path: string) => string; } -) { +): ContactType { const { getAbsoluteAttachmentPath, signalAccount, regionCode } = options; let { avatar } = contact; diff --git a/ts/types/Dialogs.ts b/ts/types/Dialogs.ts index 75ec75457397..7368664163d5 100644 --- a/ts/types/Dialogs.ts +++ b/ts/types/Dialogs.ts @@ -1,3 +1,5 @@ +/* eslint-disable camelcase */ + export enum Dialogs { None, Update, diff --git a/ts/types/Logging.ts b/ts/types/Logging.ts index c90163901f81..398417f51bfb 100644 --- a/ts/types/Logging.ts +++ b/ts/types/Logging.ts @@ -1,4 +1,4 @@ -type LogFunction = (...args: Array) => void; +type LogFunction = (...args: Array) => void; export type LoggerType = { fatal: LogFunction; diff --git a/ts/types/MIME.ts b/ts/types/MIME.ts index 5b7420051f64..065bff63bfaa 100644 --- a/ts/types/MIME.ts +++ b/ts/types/MIME.ts @@ -1,4 +1,4 @@ -export type MIMEType = string & { _mimeTypeBrand: any }; +export type MIMEType = string & { _mimeTypeBrand: never }; export const APPLICATION_OCTET_STREAM = 'application/octet-stream' as MIMEType; export const APPLICATION_JSON = 'application/json' as MIMEType; diff --git a/ts/types/Message.ts b/ts/types/Message.ts index 9a97e7b5d1fc..f02ee6b291d8 100644 --- a/ts/types/Message.ts +++ b/ts/types/Message.ts @@ -1,3 +1,5 @@ +/* eslint-disable camelcase */ + import { Attachment } from './Attachment'; import { ContactType } from './Contact'; import { IndexableBoolean, IndexablePresence } from './IndexedDB'; @@ -21,7 +23,7 @@ export type IncomingMessage = Readonly< // Optional body?: string; decrypted_at?: number; - errors?: Array; + errors?: Array; expireTimer?: number; messageTimer?: number; // deprecated isViewOnce?: number; diff --git a/ts/types/PhoneNumber.ts b/ts/types/PhoneNumber.ts index 75504a6fc665..479c936c7954 100644 --- a/ts/types/PhoneNumber.ts +++ b/ts/types/PhoneNumber.ts @@ -1,5 +1,5 @@ -import { instance, PhoneNumberFormat } from '../util/libphonenumberInstance'; import memoizee from 'memoizee'; +import { instance, PhoneNumberFormat } from '../util/libphonenumberInstance'; function _format( phoneNumber: string, @@ -73,8 +73,8 @@ export function normalize( return instance.format(parsedNumber, PhoneNumberFormat.E164); } - return; + return undefined; } catch (error) { - return; + return undefined; } } diff --git a/ts/types/Settings.ts b/ts/types/Settings.ts index 71a738e60553..2e757e1bdf2e 100644 --- a/ts/types/Settings.ts +++ b/ts/types/Settings.ts @@ -11,7 +11,8 @@ export enum AudioNotificationSupport { export function getAudioNotificationSupport(): AudioNotificationSupport { if (OS.isWindows(MIN_WINDOWS_VERSION) || OS.isMacOS()) { return AudioNotificationSupport.Native; - } else if (OS.isLinux()) { + } + if (OS.isLinux()) { return AudioNotificationSupport.Custom; } return AudioNotificationSupport.None; @@ -22,11 +23,11 @@ export const isAudioNotificationSupported = (): boolean => // Using `Notification::tag` has a bug on Windows 7: // https://github.com/electron/electron/issues/11189 -export const isNotificationGroupingSupported = () => +export const isNotificationGroupingSupported = (): boolean => !OS.isWindows() || OS.isWindows(MIN_WINDOWS_VERSION); // the "hide menu bar" option is specific to Windows and Linux -export const isHideMenuBarSupported = () => !OS.isMacOS(); +export const isHideMenuBarSupported = (): boolean => !OS.isMacOS(); // the "draw attention on notification" option is specific to Windows and Linux -export const isDrawAttentionSupported = () => !OS.isMacOS(); +export const isDrawAttentionSupported = (): boolean => !OS.isMacOS(); diff --git a/ts/util/Sound.ts b/ts/util/Sound.ts index afc3c49a30a5..87551cc661ed 100644 --- a/ts/util/Sound.ts +++ b/ts/util/Sound.ts @@ -7,8 +7,11 @@ export class Sound { static sounds = new Map(); private readonly context = new AudioContext(); + private readonly loop: boolean; + private node?: AudioBufferSourceNode; + private readonly src: string; constructor(options: SoundOpts) { @@ -16,7 +19,7 @@ export class Sound { this.src = options.src; } - async play() { + async play(): Promise { if (!Sound.sounds.has(this.src)) { try { const buffer = await Sound.loadSoundFile(this.src); @@ -44,7 +47,7 @@ export class Sound { this.node = soundNode; } - stop() { + stop(): void { if (this.node) { this.node.stop(0); this.node = undefined; diff --git a/ts/util/batcher.ts b/ts/util/batcher.ts index 6338bbf4b0dc..157c5a076dca 100644 --- a/ts/util/batcher.ts +++ b/ts/util/batcher.ts @@ -1,11 +1,16 @@ import PQueue from 'p-queue'; -// @ts-ignore +declare global { + interface Window { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + batchers: Array>; + waitForAllBatchers: () => Promise; + } +} + window.batchers = []; -// @ts-ignore window.waitForAllBatchers = async () => { - // @ts-ignore await Promise.all(window.batchers.map(item => item.flushAndWait())); }; @@ -32,7 +37,7 @@ export function createBatcher( options: BatcherOptionsType ): BatcherType { let batcher: BatcherType; - let timeout: any; + let timeout: NodeJS.Timeout | null; let items: Array = []; const queue = new PQueue({ concurrency: 1 }); @@ -70,18 +75,19 @@ export function createBatcher( async function onIdle() { while (anyPending()) { if (queue.size > 0 || queue.pending > 0) { + // eslint-disable-next-line no-await-in-loop await queue.onIdle(); } if (items.length > 0) { + // eslint-disable-next-line no-await-in-loop await sleep(options.wait * 2); } } } function unregister() { - // @ts-ignore - window.batchers = window.batchers.filter((item: any) => item !== batcher); + window.batchers = window.batchers.filter(item => item !== batcher); } async function flushAndWait() { @@ -104,7 +110,6 @@ export function createBatcher( unregister, }; - // @ts-ignore window.batchers.push(batcher); return batcher; diff --git a/ts/util/callingTones.ts b/ts/util/callingTones.ts index 95aec08f8887..131873df47e5 100644 --- a/ts/util/callingTones.ts +++ b/ts/util/callingTones.ts @@ -1,12 +1,13 @@ -import { Sound } from './Sound'; import PQueue from 'p-queue'; +import { Sound } from './Sound'; const ringtoneEventQueue = new PQueue({ concurrency: 1 }); class CallingTones { private ringtone?: Sound; - async playEndCall() { + // eslint-disable-next-line class-methods-use-this + async playEndCall(): Promise { const canPlayTone = await window.getCallRingtoneNotification(); if (!canPlayTone) { return; diff --git a/ts/util/cleanSearchTerm.ts b/ts/util/cleanSearchTerm.ts index 75c8837c1efb..c7a593762c96 100644 --- a/ts/util/cleanSearchTerm.ts +++ b/ts/util/cleanSearchTerm.ts @@ -1,4 +1,4 @@ -export function cleanSearchTerm(searchTerm: string) { +export function cleanSearchTerm(searchTerm: string): string { const lowercase = searchTerm.toLowerCase(); const withoutSpecialCharacters = lowercase.replace( /([!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~])/g, diff --git a/ts/util/combineNames.ts b/ts/util/combineNames.ts index 24f9400fd6d4..4fc382b8e91b 100644 --- a/ts/util/combineNames.ts +++ b/ts/util/combineNames.ts @@ -1,3 +1,5 @@ +/* eslint-disable camelcase */ + // We don't include unicode-12.1.0 because it's over 100MB in size // From https://github.com/mathiasbynens/unicode-12.1.0/tree/master/Block @@ -50,6 +52,7 @@ export function combineNames(given: string, family?: string): null | string { } function isAllCKJV(name: string): boolean { + // eslint-disable-next-line no-restricted-syntax for (const codePoint of name) { if (!isCKJV(codePoint)) { return false; @@ -59,7 +62,6 @@ function isAllCKJV(name: string): boolean { return true; } -// tslint:disable-next-line cyclomatic-complexity function isCKJV(codePoint: string) { if (codePoint === ' ') { return true; diff --git a/ts/util/deleteForEveryone.ts b/ts/util/deleteForEveryone.ts index 16b8cc3d9dfc..4455ba82f170 100644 --- a/ts/util/deleteForEveryone.ts +++ b/ts/util/deleteForEveryone.ts @@ -5,7 +5,7 @@ const ONE_DAY = 24 * 60 * 60 * 1000; export async function deleteForEveryone( message: MessageModelType, doe: DeletesModelType, - shouldPersist: boolean = true + shouldPersist = true ): Promise { // Make sure the server timestamps for the DOE and the matching message // are less than one day apart diff --git a/ts/util/formatDuration.ts b/ts/util/formatDuration.ts index c2c742cd7439..02a6ffc98ffe 100644 --- a/ts/util/formatDuration.ts +++ b/ts/util/formatDuration.ts @@ -2,7 +2,7 @@ import moment from 'moment'; const HOUR = 1000 * 60 * 60; -export function formatDuration(seconds: number) { +export function formatDuration(seconds: number): string { const time = moment.utc(seconds * 1000); if (seconds > HOUR) { diff --git a/ts/util/formatRelativeTime.ts b/ts/util/formatRelativeTime.ts index 9727414b8740..fc9b94c04c0b 100644 --- a/ts/util/formatRelativeTime.ts +++ b/ts/util/formatRelativeTime.ts @@ -35,7 +35,7 @@ function isYear(timestamp: moment.Moment) { export function formatRelativeTime( rawTimestamp: number | Date, options: { extended?: boolean; i18n: LocalizerType } -) { +): string { const { extended, i18n } = options; const formats = extended ? getExtendedFormats(i18n) : getShortFormats(i18n); @@ -45,13 +45,17 @@ export function formatRelativeTime( if (diff.years() >= 1 || !isYear(timestamp)) { return replaceSuffix(timestamp.format(formats.y)); - } else if (diff.months() >= 1 || diff.days() > 6) { + } + if (diff.months() >= 1 || diff.days() > 6) { return replaceSuffix(timestamp.format(formats.M)); - } else if (diff.days() >= 1 || !isToday(timestamp)) { + } + if (diff.days() >= 1 || !isToday(timestamp)) { return replaceSuffix(timestamp.format(formats.d)); - } else if (diff.hours() >= 1) { + } + if (diff.hours() >= 1) { return i18n('hoursAgo', [String(diff.hours())]); - } else if (diff.minutes() >= 1) { + } + if (diff.minutes() >= 1) { return i18n('minutesAgo', [String(diff.minutes())]); } diff --git a/ts/util/getInitials.ts b/ts/util/getInitials.ts index 1b38c0984370..bbb370c1b907 100644 --- a/ts/util/getInitials.ts +++ b/ts/util/getInitials.ts @@ -7,14 +7,14 @@ function removeNonInitials(name: string) { export function getInitials(name?: string): string | undefined { if (!name) { - return; + return undefined; } const cleaned = removeNonInitials(name); const parts = cleaned.split(' '); const initials = parts.map(part => part.trim()[0]); if (!initials.length) { - return; + return undefined; } return initials.slice(0, 2).join(''); diff --git a/ts/util/getStringForProfileChange.ts b/ts/util/getStringForProfileChange.ts index ab51f00bca85..62f2b0338bce 100644 --- a/ts/util/getStringForProfileChange.ts +++ b/ts/util/getStringForProfileChange.ts @@ -11,7 +11,7 @@ export function getStringForProfileChange( change: ProfileNameChangeType, changedContact: ConversationType, i18n: LocalizerType -) { +): string { if (change.type === 'name') { return changedContact.name ? i18n('contactChangedProfileName', { @@ -23,7 +23,7 @@ export function getStringForProfileChange( oldProfile: change.oldName, newProfile: change.newName, }); - } else { - throw new Error('TimelineItem: Unknown type!'); } + + throw new Error('TimelineItem: Unknown type!'); } diff --git a/ts/util/hasExpired.ts b/ts/util/hasExpired.ts index 5c4730d76e37..91d66c80f4b2 100644 --- a/ts/util/hasExpired.ts +++ b/ts/util/hasExpired.ts @@ -2,7 +2,7 @@ const env = window.getEnvironment(); const NINETY_ONE_DAYS = 86400 * 91 * 1000; -export function hasExpired() { +export function hasExpired(): boolean { const { getExpiration, log } = window; let buildExpiration = 0; @@ -31,5 +31,5 @@ export function hasExpired() { return Date.now() > buildExpiration && tooFarIntoFuture; } - return buildExpiration && Date.now() > buildExpiration; + return buildExpiration !== 0 && Date.now() > buildExpiration; } diff --git a/ts/util/hooks.ts b/ts/util/hooks.ts index ff9f55d68efe..6031b9f2bd93 100644 --- a/ts/util/hooks.ts +++ b/ts/util/hooks.ts @@ -5,16 +5,17 @@ import { useDispatch } from 'react-redux'; // Restore focus on teardown export const useRestoreFocus = ( // The ref for the element to receive initial focus - focusRef: React.RefObject, + focusRef: React.RefObject, // Allow for an optional root element that must exist root: boolean | HTMLElement | null = true -) => { +): void => { React.useEffect(() => { if (!root) { - return; + return undefined; } - const lastFocused = document.activeElement as any; + const lastFocused = document.activeElement as HTMLElement; + if (focusRef.current) { focusRef.current.focus(); } @@ -33,10 +34,10 @@ export const useRestoreFocus = ( export const useBoundActions = ( actions: T -) => { +): T => { const dispatch = useDispatch(); return React.useMemo(() => { return bindActionCreators(actions, dispatch); - }, [dispatch]); + }, [actions, dispatch]); }; diff --git a/ts/util/imageToBlurHash.ts b/ts/util/imageToBlurHash.ts index 1cddd4add3a4..d642a1bcf0a8 100644 --- a/ts/util/imageToBlurHash.ts +++ b/ts/util/imageToBlurHash.ts @@ -31,7 +31,6 @@ const loadImageData = async (input: Input): Promise => { 'imageToBlurHash: Failed to place image on canvas' ); reject(error); - return; }, // Calculating the blurhash on large images is a long-running and // synchronous operation, so here we ensure the images are a reasonable @@ -43,7 +42,7 @@ const loadImageData = async (input: Input): Promise => { }); }; -export const imageToBlurHash = async (input: Input) => { +export const imageToBlurHash = async (input: Input): Promise => { const { data, width, height } = await loadImageData(input); // 4 horizontal components and 3 vertical components return encode(data, width, height, 4, 3); diff --git a/ts/util/isFileDangerous.ts b/ts/util/isFileDangerous.ts index 50653c50c23f..03f3decc0afe 100644 --- a/ts/util/isFileDangerous.ts +++ b/ts/util/isFileDangerous.ts @@ -1,4 +1,3 @@ -// tslint:disable-next-line max-line-length const DANGEROUS_FILE_TYPES = /\.(ADE|ADP|APK|BAT|CAB|CHM|CMD|COM|CPL|DIAGCAB|DLL|DMG|EXE|HTA|INF|INS|ISP|JAR|JS|JSE|LIB|LNK|MDE|MHT|MSC|MSI|MSP|MST|NSH|PIF|PS1|PSC1|PSM1|PSRC|REG|SCR|SCT|SETTINGCONTENT-MS|SHB|SYS|VB|VBE|VBS|VXD|WSC|WSF|WSH)\.?$/i; export function isFileDangerous(fileName: string): boolean { diff --git a/ts/util/libphonenumberInstance.ts b/ts/util/libphonenumberInstance.ts index 40855ac23ad8..ad0fe81bbe3b 100644 --- a/ts/util/libphonenumberInstance.ts +++ b/ts/util/libphonenumberInstance.ts @@ -1,6 +1,6 @@ import libphonenumber from 'google-libphonenumber'; const instance = libphonenumber.PhoneNumberUtil.getInstance(); -const PhoneNumberFormat = libphonenumber.PhoneNumberFormat; +const { PhoneNumberFormat } = libphonenumber; export { instance, PhoneNumberFormat }; diff --git a/ts/util/lint/analyze_exceptions.ts b/ts/util/lint/analyze_exceptions.ts index 4ee1b73cc601..63ed7e4f31bc 100644 --- a/ts/util/lint/analyze_exceptions.ts +++ b/ts/util/lint/analyze_exceptions.ts @@ -1,5 +1,3 @@ -// tslint:disable no-console - import { join } from 'path'; import { fromPairs, groupBy, map } from 'lodash'; @@ -26,4 +24,5 @@ const byRuleThenByCategory = fromPairs( }) ); +// eslint-disable-next-line no-console console.log(JSON.stringify(byRuleThenByCategory, null, ' ')); diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index eafd1569a56d..309ebe2e223c 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -12785,7 +12785,7 @@ "rule": "jQuery-$(", "path": "sticker-creator/util/i18n.tsx", "line": " const FIND_REPLACEMENTS = /\\$([^$]+)\\$/g;", - "lineNumber": 39, + "lineNumber": 40, "reasonCategory": "falseMatch", "updated": "2020-07-21T18:34:59.251Z" }, diff --git a/ts/util/lint/linter.ts b/ts/util/lint/linter.ts index c6052a8c5048..0ef0e02ccda5 100644 --- a/ts/util/lint/linter.ts +++ b/ts/util/lint/linter.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ // tslint:disable no-console import * as fs from 'fs'; import { join, relative } from 'path'; @@ -298,6 +299,7 @@ function setupRules(allRules: Array) { throw new Error(`Rule '${rule.name}' is missing an expression`); } + // eslint-disable-next-line no-param-reassign rule.regex = new RegExp(rule.expression, 'g'); }); } diff --git a/ts/util/lint/sort_exceptions.ts b/ts/util/lint/sort_exceptions.ts index f5c9d7306f68..162ff0115a5a 100644 --- a/ts/util/lint/sort_exceptions.ts +++ b/ts/util/lint/sort_exceptions.ts @@ -1,5 +1,3 @@ -// tslint:disable no-console - import { join } from 'path'; import { writeFileSync } from 'fs'; diff --git a/ts/util/lint/util.ts b/ts/util/lint/util.ts index 7c246eb300de..e626ee3c2f9d 100644 --- a/ts/util/lint/util.ts +++ b/ts/util/lint/util.ts @@ -1,4 +1,4 @@ -// tslint:disable no-console +/* eslint-disable no-console */ import { readFileSync } from 'fs'; @@ -8,7 +8,7 @@ import { ExceptionType } from './types'; export const ENCODING = 'utf8'; -export function loadJSON(target: string) { +export function loadJSON(target: string): T { try { const contents = readFileSync(target, ENCODING); @@ -19,6 +19,8 @@ export function loadJSON(target: string) { } } -export function sortExceptions(exceptions: Array) { +export function sortExceptions( + exceptions: Array +): Array { return orderBy(exceptions, ['path', 'lineNumber', 'rule']); } diff --git a/ts/util/makeLookup.ts b/ts/util/makeLookup.ts index 23cea025f563..44cc9efe0571 100644 --- a/ts/util/makeLookup.ts +++ b/ts/util/makeLookup.ts @@ -2,10 +2,8 @@ import { fromPairs, map } from 'lodash'; export function makeLookup( items: Array, - key: string + key: keyof T ): { [key: string]: T } { - // Yep, we can't index into item without knowing what it is. True. But we want to. - // @ts-ignore const pairs = map(items, item => [item[key], item]); return fromPairs(pairs); diff --git a/ts/util/registration.ts b/ts/util/registration.ts index d529b41016cc..90d8aa8193f5 100644 --- a/ts/util/registration.ts +++ b/ts/util/registration.ts @@ -1,21 +1,20 @@ -export function markEverDone() { - // @ts-ignore +export function markEverDone(): void { window.storage.put('chromiumRegistrationDoneEver', ''); } -export function markDone() { +export function markDone(): void { markEverDone(); window.storage.put('chromiumRegistrationDone', ''); } -export async function remove() { +export async function remove(): Promise { await window.storage.remove('chromiumRegistrationDone'); } -export function isDone() { +export function isDone(): boolean { return window.storage.get('chromiumRegistrationDone') === ''; } -export function everDone() { +export function everDone(): boolean { return window.storage.get('chromiumRegistrationDoneEver') === '' || isDone(); } diff --git a/ts/util/sgnlHref.ts b/ts/util/sgnlHref.ts index 3d3e9f6332d0..9618005b6167 100644 --- a/ts/util/sgnlHref.ts +++ b/ts/util/sgnlHref.ts @@ -3,7 +3,8 @@ import { LoggerType } from '../types/Logging'; function parseUrl(value: unknown, logger: LoggerType): null | URL { if (value instanceof URL) { return value; - } else if (typeof value === 'string') { + } + if (typeof value === 'string') { try { return new URL(value); } catch (err) { diff --git a/ts/util/waitBatcher.ts b/ts/util/waitBatcher.ts index 482e30af2e03..64c250e7c32c 100644 --- a/ts/util/waitBatcher.ts +++ b/ts/util/waitBatcher.ts @@ -1,24 +1,29 @@ import PQueue from 'p-queue'; -// @ts-ignore +declare global { + interface Window { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + waitBatchers: Array>; + waitForAllWaitBatchers: () => Promise; + } +} + window.waitBatchers = []; -// @ts-ignore window.waitForAllWaitBatchers = async () => { - // @ts-ignore await Promise.all(window.waitBatchers.map(item => item.onIdle())); }; type ItemHolderType = { - resolve: (value?: any) => void; - reject: (error: Error) => void; + resolve?: (value?: unknown) => void; + reject?: (error: Error) => void; item: ItemType; }; type ExplodedPromiseType = { - resolve: (value?: any) => void; - reject: (error: Error) => void; - promise: Promise; + resolve?: (value?: unknown) => void; + reject?: (error: Error) => void; + promise: Promise; }; type BatcherOptionsType = { @@ -43,7 +48,7 @@ export function createWaitBatcher( options: BatcherOptionsType ): BatcherType { let waitBatcher: BatcherType; - let timeout: any; + let timeout: NodeJS.Timeout | null; let items: Array> = []; const queue = new PQueue({ concurrency: 1 }); @@ -55,11 +60,15 @@ export function createWaitBatcher( try { await options.processBatch(itemsRef.map(item => item.item)); itemsRef.forEach(item => { - item.resolve(); + if (item.resolve) { + item.resolve(); + } }); } catch (error) { itemsRef.forEach(item => { - item.reject(error); + if (item.reject) { + item.reject(error); + } }); } }); @@ -69,18 +78,17 @@ export function createWaitBatcher( let resolve; let reject; - // tslint:disable-next-line:promise-must-complete const promise = new Promise((resolveParam, rejectParam) => { resolve = resolveParam; reject = rejectParam; }); - // @ts-ignore return { promise, resolve, reject }; } async function add(item: ItemType) { const { promise, resolve, reject } = _makeExplodedPromise(); + items.push({ resolve, reject, @@ -111,19 +119,20 @@ export function createWaitBatcher( async function onIdle() { while (anyPending()) { if (queue.size > 0 || queue.pending > 0) { + // eslint-disable-next-line no-await-in-loop await queue.onIdle(); } if (items.length > 0) { + // eslint-disable-next-line no-await-in-loop await sleep(options.wait * 2); } } } function unregister() { - // @ts-ignore window.waitBatchers = window.waitBatchers.filter( - (item: any) => item !== waitBatcher + item => item !== waitBatcher ); } @@ -134,7 +143,6 @@ export function createWaitBatcher( unregister, }; - // @ts-ignore window.waitBatchers.push(waitBatcher); return waitBatcher; diff --git a/ts/util/zkgroup.ts b/ts/util/zkgroup.ts index a008a1b318db..46bd3c98d03e 100644 --- a/ts/util/zkgroup.ts +++ b/ts/util/zkgroup.ts @@ -1,5 +1,3 @@ -export * from 'zkgroup'; - import { AuthCredential, ClientZkAuthOperations, @@ -24,7 +22,7 @@ import { typedArrayToArrayBuffer, } from '../Crypto'; -// Simple utility functions +export * from 'zkgroup'; export function arrayBufferToCompatArray( arrayBuffer: ArrayBuffer @@ -57,7 +55,7 @@ export function compatArrayToHex(compatArray: FFICompatArrayType): string { export function decryptGroupBlob( clientZkGroupCipher: ClientZkGroupCipher, ciphertext: ArrayBuffer -) { +): ArrayBuffer { return compatArrayToArrayBuffer( clientZkGroupCipher.decryptBlob(arrayBufferToCompatArray(ciphertext)) ); @@ -117,7 +115,7 @@ export function decryptUuid( export function deriveProfileKeyVersion( profileKeyBase64: string, uuid: string -) { +): string { const profileKeyArray = base64ToCompatArray(profileKeyBase64); const profileKey = new ProfileKey(profileKeyArray); @@ -126,7 +124,9 @@ export function deriveProfileKeyVersion( return profileKeyVersion.toString(); } -export function deriveGroupPublicParams(groupSecretParamsBuffer: ArrayBuffer) { +export function deriveGroupPublicParams( + groupSecretParamsBuffer: ArrayBuffer +): ArrayBuffer { const groupSecretParams = new GroupSecretParams( arrayBufferToCompatArray(groupSecretParamsBuffer) ); @@ -136,7 +136,9 @@ export function deriveGroupPublicParams(groupSecretParamsBuffer: ArrayBuffer) { ); } -export function deriveGroupID(groupSecretParamsBuffer: ArrayBuffer) { +export function deriveGroupID( + groupSecretParamsBuffer: ArrayBuffer +): ArrayBuffer { const groupSecretParams = new GroupSecretParams( arrayBufferToCompatArray(groupSecretParamsBuffer) ); @@ -163,7 +165,7 @@ export function deriveGroupSecretParams( export function encryptGroupBlob( clientZkGroupCipher: ClientZkGroupCipher, plaintext: ArrayBuffer -) { +): ArrayBuffer { return compatArrayToArrayBuffer( clientZkGroupCipher.encryptBlob(arrayBufferToCompatArray(plaintext)) ); @@ -203,7 +205,7 @@ export function getAuthCredentialPresentation( clientZkAuthOperations: ClientZkAuthOperations, authCredentialBase64: string, groupSecretParamsBase64: string -) { +): ArrayBuffer { const authCredential = new AuthCredential( base64ToCompatArray(authCredentialBase64) ); diff --git a/tslint.json b/tslint.json index cca358a9914c..bb5df2f54334 100644 --- a/tslint.json +++ b/tslint.json @@ -177,6 +177,8 @@ "rulesDirectory": ["node_modules/tslint-microsoft-contrib"], "linterOptions": { "exclude": [ + "sticker-creator/**/*.ts", + "sticker-creator/**/*.tsx", "ts/*.ts", "ts/backbone/**", "ts/build/**", @@ -186,7 +188,10 @@ "ts/notifications/**", "ts/protobuf/**", "ts/scripts/**", - "ts/services/**" + "ts/services/**", + "ts/state/**", + "ts/types/**", + "ts/util/**" ] } } diff --git a/yarn.lock b/yarn.lock index e1926063a638..1d9d57c196f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12324,7 +12324,7 @@ polished@^3.3.1: dependencies: "@babel/runtime" "^7.4.5" -popper.js@^1.14.4, popper.js@^1.14.7: +popper.js@1.15.0, popper.js@^1.14.4, popper.js@^1.14.7: version "1.15.0" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2" integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==