From 4669472f4e80f42fe36b7da55f0957b15d5767a9 Mon Sep 17 00:00:00 2001 From: Chris Heo Date: Tue, 5 Mar 2024 23:01:09 -0800 Subject: [PATCH 01/57] Update tsconfig.json --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index b82268fd8..e6ec0941c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "module": "es2020", "esModuleInterop": true, "moduleResolution": "node", + "strictNullChecks": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, "typeRoots": ["./node_modules/@types", "./types"] From 0ddd1980aa077241a270da105f01c975351f39d3 Mon Sep 17 00:00:00 2001 From: OnestarLee <100272033+OnestarLee@users.noreply.github.com> Date: Tue, 19 Mar 2024 08:35:51 +0900 Subject: [PATCH 02/57] fix: Fixed type error. (#1011) Fixed type errors when 'strictNullChecks' is 'true'. --- src/ui/AdminMessage/index.tsx | 2 +- src/ui/Avatar/index.tsx | 87 ++++++++++---------- src/ui/BottomSheet/index.tsx | 4 +- src/ui/Button/utils.ts | 4 +- src/ui/ChannelAvatar/utils.ts | 3 +- src/ui/ContextMenu/MenuItems.tsx | 13 ++- src/ui/DateSeparator/index.tsx | 2 +- src/ui/FileMessageItemBody/index.tsx | 6 +- src/ui/FileViewer/index.tsx | 2 +- src/ui/Icon/type.ts | 2 +- src/ui/ImageGrid/index.tsx | 2 +- src/ui/ImageRenderer/index.tsx | 29 ++++--- src/ui/ImageRenderer/useDynamicSideLength.ts | 4 +- src/ui/ImageRenderer/utils.ts | 2 +- src/ui/LegacyEditUserProfile/index.tsx | 30 +++---- src/utils/index.ts | 13 +-- src/utils/numberToPx.ts | 5 +- 17 files changed, 110 insertions(+), 100 deletions(-) diff --git a/src/ui/AdminMessage/index.tsx b/src/ui/AdminMessage/index.tsx index 2aedfc2c4..23a9f69de 100644 --- a/src/ui/AdminMessage/index.tsx +++ b/src/ui/AdminMessage/index.tsx @@ -12,7 +12,7 @@ interface AdminMessageProps { export default function AdminMessage({ className = '', message, -}: AdminMessageProps): ReactElement { +}: AdminMessageProps): ReactElement | null { if (!(message?.isAdminMessage || message?.messageType) || !message?.isAdminMessage?.() || message?.messageType !== 'admin') { return null; } diff --git a/src/ui/Avatar/index.tsx b/src/ui/Avatar/index.tsx index 2fafe8b82..adbd374d0 100644 --- a/src/ui/Avatar/index.tsx +++ b/src/ui/Avatar/index.tsx @@ -153,47 +153,46 @@ interface AvatarProps { customDefaultComponent?({ width, height }: { width: number | string, height: number | string }): ReactElement; } -function Avatar( - { - className = '', - src = '', - alt = '', - width = '56px', - height = '56px', - zIndex = 0, - left = '', - onClick, - customDefaultComponent, - }: AvatarProps, - ref?: RefObject, -): ReactElement { - return ( -
- -
- ); -} - -export default React.forwardRef(Avatar); +const Avatar = React.forwardRef( + ( + { + className = '', + src = '', + alt = '', + width = '56px', + height = '56px', + zIndex = 0, + left = '', + onClick, + customDefaultComponent, + }: AvatarProps, + ref: RefObject) => { + return ( +
+ +
+ ); + }); +export default Avatar; \ No newline at end of file diff --git a/src/ui/BottomSheet/index.tsx b/src/ui/BottomSheet/index.tsx index bd12b8c02..d4447da17 100644 --- a/src/ui/BottomSheet/index.tsx +++ b/src/ui/BottomSheet/index.tsx @@ -18,7 +18,7 @@ const BottomSheet: React.FunctionComponent = (props: BottomShe } = props; // https://github.com/testing-library/react-testing-library/issues/62#issuecomment-438653348 - const portalRoot = useRef(); + const portalRoot = useRef(); portalRoot.current = document.getElementById(MODAL_ROOT); if (!portalRoot.current) { portalRoot.current = document.createElement('div'); @@ -43,7 +43,7 @@ const BottomSheet: React.FunctionComponent = (props: BottomShe `} onClick={(e) => { e?.stopPropagation(); - onBackdropClick(); + onBackdropClick?.(); }} /> , diff --git a/src/ui/Button/utils.ts b/src/ui/Button/utils.ts index 39a25964c..beb770a44 100644 --- a/src/ui/Button/utils.ts +++ b/src/ui/Button/utils.ts @@ -8,7 +8,7 @@ export function changeTypeToClassName(type: ButtonTypes): string { case ButtonTypes.DISABLED: return 'sendbird-button--disabled'; case ButtonTypes.WARNING: return 'sendbird-button--warning'; - default: return null; + default: return ''; } } @@ -16,6 +16,6 @@ export function changeSizeToClassName(size: ButtonSizes): string { switch (size) { case ButtonSizes.BIG: return 'sendbird-button--big'; case ButtonSizes.SMALL: return 'sendbird-button--small'; - default: return null; + default: return ''; } } diff --git a/src/ui/ChannelAvatar/utils.ts b/src/ui/ChannelAvatar/utils.ts index 45e62d26d..3cb33a497 100644 --- a/src/ui/ChannelAvatar/utils.ts +++ b/src/ui/ChannelAvatar/utils.ts @@ -3,10 +3,11 @@ import type { OpenChannel } from '@sendbird/chat/openChannel'; export const DEFAULT_URL_PREFIX = 'https://static.sendbird.com/sample/cover/cover_'; -export const getOpenChannelAvatar = (channel: OpenChannel): string => { +export const getOpenChannelAvatar = (channel: OpenChannel): string | undefined => { if (channel?.coverUrl) { return channel.coverUrl; } + return undefined; }; export const getChannelAvatarSource = (channel: GroupChannel, currentUserId: string): string | Array => { diff --git a/src/ui/ContextMenu/MenuItems.tsx b/src/ui/ContextMenu/MenuItems.tsx index fe07b5dd8..d78a0dbad 100644 --- a/src/ui/ContextMenu/MenuItems.tsx +++ b/src/ui/ContextMenu/MenuItems.tsx @@ -11,7 +11,7 @@ interface MenuItemsProps { closeDropdown: () => void; } -type MenuStyleType = { top?: number, left?: number }; +type MenuStyleType = { top: number, left: number }; interface MenuItemsState { menuStyle: MenuStyleType; handleClickOutside: (e: MouseEvent) => void; @@ -24,7 +24,7 @@ export default class MenuItems extends React.Component { /* noop */ }, }; } @@ -99,10 +99,15 @@ export default class MenuItems extends React.Component; + const { menuStyle } = this.state; const { children, style, className = '' } = this.props; + return ( createPortal( ( @@ -123,7 +128,7 @@ export default class MenuItems extends React.Component ), - document.getElementById('sendbird-dropdown-portal'), + portalElement ) ); } diff --git a/src/ui/DateSeparator/index.tsx b/src/ui/DateSeparator/index.tsx index cb223416c..837976273 100644 --- a/src/ui/DateSeparator/index.tsx +++ b/src/ui/DateSeparator/index.tsx @@ -15,7 +15,7 @@ export interface DateSeparatorProps { separatorColor?: Colors; } const DateSeparator = ({ - children = null, + children = undefined, className = '', separatorColor = Colors.ONBACKGROUND_4, }: DateSeparatorProps): ReactElement => { diff --git a/src/ui/FileMessageItemBody/index.tsx b/src/ui/FileMessageItemBody/index.tsx index 2f064104d..6218510c6 100644 --- a/src/ui/FileMessageItemBody/index.tsx +++ b/src/ui/FileMessageItemBody/index.tsx @@ -19,15 +19,15 @@ interface Props { } export default function FileMessageItemBody({ - className, + className = '', message, isByMe = false, mouseHover = false, isReactionEnabled = false, - truncateLimit = null, + truncateLimit = undefined, }: Props): ReactElement { const { isMobile } = useMediaQueryContext(); - const truncateMaxNum = truncateLimit || (isMobile ? 20 : null); + const truncateMaxNum = truncateLimit || (isMobile ? 20 : undefined); return (
0) ? 'reactions' : '', ])}> diff --git a/src/ui/ImageRenderer/index.tsx b/src/ui/ImageRenderer/index.tsx index 1e4b0fbd3..5ea892af0 100644 --- a/src/ui/ImageRenderer/index.tsx +++ b/src/ui/ImageRenderer/index.tsx @@ -6,7 +6,7 @@ import { useDynamicSideLength } from './useDynamicSideLength'; import { useLazyImageLoader } from '../../modules/Channel/components/Message/hooks/useLazyImageLoader'; import { noop } from '../../utils/utils'; -export function getBorderRadiusForImageRenderer(circle = false, borderRadius: string | number = null): string { +export function getBorderRadiusForImageRenderer(circle = false, borderRadius: string | number | null = null): string { return circle ? '50%' : numberToPx(borderRadius); } @@ -24,14 +24,14 @@ export interface ImageRendererProps { className?: string | Array; url: string; alt?: string; - width?: string | number; - maxSideLength?: string; - height?: string | number; + width?: string | number | null; + maxSideLength?: string | null; + height?: string | number | null; circle?: boolean; fixedSize?: boolean; - placeHolder?: ((props: { style: Record }) => ReactElement) | ReactElement; - defaultComponent?: (() => ReactElement) | ReactElement; - borderRadius?: string | number; + placeHolder?: ((props: { style: Record }) => ReactElement) | ReactElement | null; + defaultComponent?: (() => ReactElement) | ReactElement | null; + borderRadius?: string | number | null; onLoad?: () => void; onError?: () => void; shadeOnHover?: boolean; @@ -64,7 +64,7 @@ const ImageRenderer = ({ const ref = useRef(null); const isLoaded = useLazyImageLoader(ref); const internalUrl = isLoaded ? url : null; - + const [defaultComponentVisible, setDefaultComponentVisible] = useState(false); const [placeholderVisible, setPlaceholderVisible] = useState(true); const [dynamicMinWidth, dynamicMinHeight] = useDynamicSideLength({ @@ -73,7 +73,7 @@ const ImageRenderer = ({ maxSideLength, defaultMinLength: '400px', }); - + const renderPlaceholder = () => { if (typeof placeHolder === 'function') { return placeHolder({ @@ -91,12 +91,12 @@ const ImageRenderer = ({ } return placeHolder; }; - + const renderDefault = () => { if (typeof defaultComponent === 'function') return defaultComponent(); return defaultComponent; }; - + const renderImage = () => { const backgroundStyle = internalUrl ? { backgroundRepeat: 'no-repeat', @@ -104,7 +104,7 @@ const ImageRenderer = ({ backgroundSize: 'cover', backgroundImage: `url(${internalUrl})`, } : {}; - + return (
); }; - + return ( dynamicMinWidth && dynamicMinHeight && ( @@ -164,8 +164,7 @@ const ImageRenderer = ({ }} />}
- ) - ); + )) as React.ReactElement; }; // Image is loaded as a background-image, but this component serves as a hidden component to receive events indicating whether the image has actually been loaded. diff --git a/src/ui/ImageRenderer/useDynamicSideLength.ts b/src/ui/ImageRenderer/useDynamicSideLength.ts index bc8f6fc32..202364c41 100644 --- a/src/ui/ImageRenderer/useDynamicSideLength.ts +++ b/src/ui/ImageRenderer/useDynamicSideLength.ts @@ -2,8 +2,8 @@ import { useMemo } from 'react'; import { getDynamicMinLengthInPx } from './utils'; interface DynamicSideLengthProps { - width: string | number; - height: string | number; + width: string | number | null; + height: string | number | null; maxSideLength: string | null; defaultMinLength: string } diff --git a/src/ui/ImageRenderer/utils.ts b/src/ui/ImageRenderer/utils.ts index 58b0087e0..158a62313 100644 --- a/src/ui/ImageRenderer/utils.ts +++ b/src/ui/ImageRenderer/utils.ts @@ -1,7 +1,7 @@ import numberToPx from '../../utils/numberToPx'; export function getDynamicMinLengthInPx( - sideLength: string | number, + sideLength: string | number | null, maxSideLength: string | null, defaultMinLength: string, ): string { diff --git a/src/ui/LegacyEditUserProfile/index.tsx b/src/ui/LegacyEditUserProfile/index.tsx index b3306d8cb..4ffa90151 100644 --- a/src/ui/LegacyEditUserProfile/index.tsx +++ b/src/ui/LegacyEditUserProfile/index.tsx @@ -23,7 +23,7 @@ interface Props { user: User; theme?: string; onCancel(): void; - onSubmit(newFile: File, newNickname: string): void; + onSubmit(newFile: File | null, newNickname: string): void; changeTheme?(theme: string): void; onThemeChange?(theme: string): void; } @@ -34,14 +34,14 @@ export function EditUserProfile({ onCancel, onSubmit, changeTheme = noop, - onThemeChange = null, + onThemeChange, }: Props): ReactElement { - const hiddenInputRef = useRef(null); - const inputRef = useRef(null); - const formRef = useRef(null); + const hiddenInputRef = useRef(null); + const inputRef = useRef(null); + const formRef = useRef(null); const { stringSet } = useContext(LocalizationContext); - const [currentImg, setCurrentImg] = useState(null); - const [newFile, setNewFile] = useState(null); + const [currentImg, setCurrentImg] = useState(null); + const [newFile, setNewFile] = useState(null); return ( { - if (user.nickname !== '' && !inputRef.current.value) { - if (formRef.current.reportValidity) { // might not work in explorer + if (user.nickname !== '' && inputRef.current && !inputRef.current.value) { + if (formRef.current?.reportValidity) { // might not work in explorer formRef.current.reportValidity(); } return; } - onSubmit(inputRef.current.value, newFile); + onSubmit(newFile, inputRef.current?.value || ''); onCancel(); }} > @@ -82,15 +82,17 @@ export function EditUserProfile({ accept="image/gif, image/jpeg, image/png" style={{ display: 'none' }} onChange={(e) => { - setCurrentImg(URL.createObjectURL(e.target.files[0])); - setNewFile(e.target.files[0]); - hiddenInputRef.current.value = ''; + if (e.target.files && 0 < e.target.files?.length) { + setCurrentImg(URL.createObjectURL(e.target.files[0])); + setNewFile(e.target.files[0]); + if (hiddenInputRef.current) {hiddenInputRef.current.value = '';} + } }} /> hiddenInputRef.current.click()} + onClick={() => hiddenInputRef.current?.click()} >