From 61d92acad7821ca298f6bb8bd30139c54ba2ca52 Mon Sep 17 00:00:00 2001 From: Christian Rackerseder Date: Fri, 8 May 2026 16:23:52 +0200 Subject: [PATCH] chore(webapp): continue strict-boolean migration with explicit nullish checks --- apps/server/Server.ts | 13 +- apps/server/config.ts | 12 +- .../googlewebmaster/googleWebmasterRoute.ts | 2 +- apps/server/routes/redirectRoutes.ts | 2 +- apps/server/routes/root.ts | 2 +- apps/server/util/browserUtil.ts | 2 +- .../script/components/AppLoader/AppLoader.tsx | 4 +- .../AppNotification/AppNotification.tsx | 4 +- .../src/script/components/Avatar/Avatar.tsx | 2 +- .../script/components/Avatar/AvatarBadge.tsx | 6 +- .../script/components/Avatar/AvatarImage.tsx | 5 +- .../Avatar/UserAvatar/UserAvatar.tsx | 5 +- .../UserStatusBadges/UserStatusBadges.tsx | 4 +- .../VerificationBadges/VerificationBadges.tsx | 12 +- .../ShareModal/CellsShareExpirationFields.tsx | 12 +- .../Cells/ShareModal/shareModalSerializer.ts | 5 +- .../CellsConversationColumn.tsx | 2 +- .../CellsOwnerColumn/CellsOwnerColumn.tsx | 2 +- .../CellsShareModal/CellsShareModal.tsx | 6 +- .../useSearchCellsNodes/getUsersFromNodes.ts | 2 +- .../ConfigToolbar/ConfigToolbar.tsx | 4 +- .../ConnectRequests/ConnectionRequests.tsx | 11 +- .../components/Conversation/Conversation.tsx | 28 ++- .../CellsNewFileModal/CellsNewFileModal.tsx | 2 +- .../CellsNewFolderModal.tsx | 2 +- .../CellsNodeShareModal.tsx | 6 +- .../CellsTableOwnerColumn.tsx | 2 +- .../CellsFoldersListModalContent.tsx | 2 +- .../useCellsRenameNodeForm.ts | 6 +- .../useRestoreParentNode.ts | 2 +- .../ConversationCells/ConversationCells.tsx | 8 +- .../CellsModalContext/CellsModalContext.tsx | 2 +- .../common/getCellsApiPath/getCellsApiPath.ts | 2 +- .../common/openBreadcrumb/openBreadcrumb.ts | 2 +- .../common/openFolder/openFolder.ts | 2 +- .../common/recycleBin/recycleBin.ts | 2 +- .../useCellsNewNodeForm/cellsNodeFormUtils.ts | 2 +- .../useGetAllCellsNodes/getUsersFromNodes.ts | 2 +- .../useRefreshCellsState.ts | 4 +- .../ReadOnlyConversationMessage.tsx | 6 +- .../components/EmojiPicker/EmojiPicker.tsx | 4 +- .../FileEditor/FileEditor.tsx | 4 +- .../src/script/components/Giphy/Giphy.tsx | 12 +- .../HistoryExport/HistoryExport.tsx | 8 +- .../HistoryImport/HistoryImport.tsx | 12 +- .../src/script/components/Image/Image.tsx | 6 +- .../components/InViewport/InViewport.tsx | 4 +- .../script/components/InputBar/InputBar.tsx | 29 ++-- .../useLinkState/useLinkState.ts | 10 +- .../RichTextEditor/RichTextEditor.tsx | 4 +- .../RichTextEditor/nodes/Mention.tsx | 7 +- .../EmojiPickerPlugin/EmojiPickerPlugin.tsx | 10 +- .../plugins/MentionsPlugin/MentionsPlugin.tsx | 8 +- .../plugins/PastePlugin/PastePlugin.tsx | 10 +- .../TypeaheadMenuPlugin.tsx | 15 +- .../useFileHandling/useFileHandling.ts | 4 +- .../useMessageHandling/useMessageHandling.ts | 4 +- .../useMessageSend/useMessageSend.ts | 10 +- .../Message/ContentMessage/ContentMessage.tsx | 8 +- .../MessageReactions/EmojiPill.tsx | 4 +- .../PartialFailureToSend.tsx | 4 +- .../asset/AudioAsset/AudioAsset.tsx | 8 +- .../useAudioSeekBar/useAudioSeekBar.ts | 8 +- .../AudioAsset/AudioSeekBar/AudioSeekBar.tsx | 8 +- .../asset/FileAsset/FileAsset.tsx | 8 +- .../useVideoPlayback/useVideoPlayback.ts | 8 +- .../TextMessageRenderer.tsx | 16 +- .../asset/VideoAsset/VideoAsset.tsx | 12 +- .../asset/common/MediaButton/MediaButton.tsx | 6 +- .../useAssetTransfer/useAssetTransfer.ts | 9 +- .../Message/DecryptErrorMessage.tsx | 4 +- .../Message/FailedToAddUsersMessage.tsx | 8 +- .../Message/MemberMessage/MessageContent.tsx | 8 +- .../MessagesList/Message/Message.tsx | 8 +- .../Message/ReadIndicator/ReadIndicator.tsx | 6 +- .../Message/ReadReceiptStatus.tsx | 8 +- .../Message/VirtualizedMessage.tsx | 6 +- .../components/MessagesList/MessageList.tsx | 12 +- .../UploadAssets/UploadAssets.tsx | 6 +- .../VirtualizedMessagesList.tsx | 8 +- .../MessagesList/utils/messagesGroup.ts | 8 +- .../utils/virtualizedMessagesGroup.ts | 17 +- .../Modals/CreateConversation/utils.ts | 16 +- .../Modals/DetailViewModal/index.tsx | 7 +- .../GroupCreation/GroupCreationModal.tsx | 13 +- .../Modals/LegalHoldModal/LegalHoldModal.tsx | 8 +- .../Modals/ModalComponent/ModalComponent.tsx | 9 +- .../Modals/PrimaryModal/PrimaryModal.tsx | 23 ++- .../PrimaryModalShell/PrimaryModalShell.tsx | 6 +- .../QualityFeedbackModal.tsx | 6 +- .../components/Modals/UserModal/UserModal.tsx | 14 +- .../script/components/TitleBar/TitleBar.tsx | 12 +- .../script/components/UserList/UserList.tsx | 15 +- .../UserSearchableList/UserSearchableList.tsx | 26 +-- .../ZoomableImage/ZoomableImage.tsx | 12 +- .../calling/CallingOverlayContainer.tsx | 11 +- .../components/calling/ChooseScreen.tsx | 8 +- .../calling/FullscreenVideoCall.tsx | 14 +- .../components/calling/GroupVideoGrid.tsx | 12 +- .../script/components/panel/UserActions.tsx | 12 +- eslint.config.ts | 20 +++ eslint.strict-boolean-components.ts | 96 +++++++++++ libraries/api-client/src/apiClient.ts | 26 +-- libraries/api-client/src/asset/assetApi.ts | 10 +- libraries/api-client/src/auth/authApi.ts | 2 +- .../api-client/src/broadcast/broadcastApi.ts | 3 +- libraries/api-client/src/cells/cellsApi.ts | 161 ++++++++---------- .../src/connection/connectionApi.ts | 2 +- .../conversationApi/conversationApi.ts | 3 +- .../api-client/src/http/backendErrorMapper.ts | 4 +- libraries/api-client/src/http/httpClient.ts | 2 +- .../notificationApi/notificationApi.ts | 6 +- libraries/api-client/src/shims/node/cookie.ts | 7 +- .../src/tcp/reconnectingWebsocket.ts | 20 +-- .../api-client/src/tcp/webSocketClient.ts | 6 +- .../src/team/invitation/teamInvitationApi.ts | 4 +- .../api-client/src/team/member/memberApi.ts | 2 +- libraries/api-client/src/user/userApi.ts | 4 +- .../backFromSleepHandler.ts | 4 +- libraries/core/src/account.ts | 41 ++--- libraries/core/src/client/clientService.ts | 2 +- .../conversationService.ts | 20 +-- .../getConversationQualifiedMembers.ts | 4 +- .../message/messageToProtoMapper.ts | 14 +- .../message/textContentBuilder.ts | 2 +- .../conversation/message/userClientsUtil.ts | 2 +- .../subconversationService.ts | 6 +- .../src/cryptography/messageHashService.ts | 2 +- .../e2eIdentityService/e2eiServiceExternal.ts | 10 +- .../e2eIdentityService/e2eiServiceInternal.ts | 4 +- .../e2eIdentityService/steps/authorization.ts | 2 +- .../e2eIdentityService/steps/certificate.ts | 4 +- .../steps/dpopChallenge/dpopChallenge.ts | 3 +- .../events/messageAdd/messageAdd.ts | 6 +- .../mls/mlsService/mlsService.ts | 10 +- .../mls/recovery/mlsRecoveryOrchestrator.ts | 4 +- .../events/otrMessageAdd/otrMessageAdd.ts | 2 +- .../coreCryptoWrapper/coreCryptoWrapper.ts | 2 +- .../cryptoClient/cryptoboxWrapper.ts | 3 +- .../utility/getGenericMessageParams.ts | 3 +- .../utility/sessionHandler/sessionHandler.ts | 11 +- .../src/notification/notificationService.ts | 2 +- .../core/src/secretStore/encryptedStore.ts | 4 +- .../src/secretStore/secretKeyGenerator.ts | 4 +- libraries/core/src/user/userService.ts | 2 +- .../lowPrecisionTaskScheduler.ts | 4 +- .../src/util/taskScheduler/taskScheduler.ts | 4 +- 147 files changed, 765 insertions(+), 578 deletions(-) create mode 100644 eslint.strict-boolean-components.ts diff --git a/apps/server/Server.ts b/apps/server/Server.ts index d3370d9b1ab..31c761867df 100644 --- a/apps/server/Server.ts +++ b/apps/server/Server.ts @@ -177,7 +177,8 @@ class Server { // Scope clipboard delegation to self and the configured Collabora origin only. // Falls back to self-only when CELLS_PYDIO_URL is not configured. const collaboraOrigin = this.clientConfig.CELLS_PYDIO_URL; - const clipboardAllowlist = collaboraOrigin ? `(self "${collaboraOrigin}")` : '(self)'; + const clipboardAllowlist = + collaboraOrigin !== undefined && collaboraOrigin.length > 0 ? `(self "${collaboraOrigin}")` : '(self)'; this.app.use((_req, res, next) => { res.setHeader( 'Permissions-Policy', @@ -197,7 +198,7 @@ class Server { }); this.app.get('/favicon.ico', (_req, res) => res.sendFile(path.join(__dirname, 'static/image/favicon.ico'))); - if (!this.config.DEVELOPMENT) { + if (this.config.DEVELOPMENT === false) { this.app.get('/sw.js', (_req, res) => res.sendFile(path.join(__dirname, 'static/sw.js'))); } } @@ -237,7 +238,7 @@ class Server { } private initSiteMap(config: ServerConfig) { - if (config.APP_BASE) { + if (config.APP_BASE.length > 0) { const pages = () => [ { changeFreq: 'weekly', @@ -254,9 +255,9 @@ class Server { start(): Promise { return new Promise((resolve, reject) => { - if (this.server) { + if (this.server !== undefined) { reject('Server is already running.'); - } else if (this.config.PORT_HTTP) { + } else if (this.config.PORT_HTTP > 0) { if (this.config.DEVELOPMENT && this.config.DEVELOPMENT_ENABLE_TLS) { const options = { cert: fs.readFileSync(this.config.SSL_CERTIFICATE_PATH), @@ -275,7 +276,7 @@ class Server { } async stop(): Promise { - if (this.server) { + if (this.server !== undefined) { this.server.close(); this.server = undefined; } else { diff --git a/apps/server/config.ts b/apps/server/config.ts index f1fce650384..5f390796bf4 100644 --- a/apps/server/config.ts +++ b/apps/server/config.ts @@ -25,7 +25,7 @@ import path from 'path'; import {generateClientConfig, generateServerConfig, Env} from '@wireapp/config'; const versionData = readFileSync(path.resolve(__dirname, './version.json'), 'utf8'); -const version = versionData ? JSON.parse(versionData) : {version: 'unknown', commit: 'unknown'}; +const version = versionData.length > 0 ? JSON.parse(versionData) : {version: 'unknown', commit: 'unknown'}; const dotenvConfigurationIndentationSpaces = 2; // Determine the correct root path based on the directory structure @@ -54,13 +54,13 @@ console.info('[Config] dotenv config:', JSON.stringify(dotenvConfig, null, doten const env = dotenv.load(dotenvConfig) as Env; -console.info('[Config] Environment loaded. APP_BASE:', env.APP_BASE ? 'SET' : 'NOT SET'); +console.info('[Config] Environment loaded. APP_BASE:', env.APP_BASE.length > 0 ? 'SET' : 'NOT SET'); function generateUrls() { - const federation = env.FEDERATION; + const federation = env.FEDERATION ?? ''; - if (!federation) { - if (!env.APP_BASE || !env.BACKEND_REST || !env.BACKEND_WS) { + if (federation.length === 0) { + if (env.APP_BASE.length === 0 || env.BACKEND_REST.length === 0 || env.BACKEND_WS.length === 0) { console.error('[Config] Missing required environment variables!'); console.error('[Config] APP_BASE:', env.APP_BASE); console.error('[Config] BACKEND_REST:', env.BACKEND_REST); @@ -86,7 +86,7 @@ function generateUrls() { const commonConfig = { commit: version.commit, version: version.version, - env: env.NODE_ENV || 'production', + env: env.NODE_ENV.length > 0 ? env.NODE_ENV : 'production', urls: generateUrls(), }; diff --git a/apps/server/routes/googlewebmaster/googleWebmasterRoute.ts b/apps/server/routes/googlewebmaster/googleWebmasterRoute.ts index eac351cc98b..5d330fb5a2a 100644 --- a/apps/server/routes/googlewebmaster/googleWebmasterRoute.ts +++ b/apps/server/routes/googlewebmaster/googleWebmasterRoute.ts @@ -22,7 +22,7 @@ import {Router} from 'express'; import type {ServerConfig} from '@wireapp/config'; export const GoogleWebmasterRoute = (config: ServerConfig) => { - if (config.GOOGLE_WEBMASTER_ID) { + if (config.GOOGLE_WEBMASTER_ID.length > 0) { return Router().get(`/google${config.GOOGLE_WEBMASTER_ID}.html`, (_req, res) => { const responseBody = `google-site-verification: google${config.GOOGLE_WEBMASTER_ID}.html`; res.type('text/html; charset=utf-8').send(responseBody); diff --git a/apps/server/routes/redirectRoutes.ts b/apps/server/routes/redirectRoutes.ts index ec8817ce5c2..e82892cc818 100644 --- a/apps/server/routes/redirectRoutes.ts +++ b/apps/server/routes/redirectRoutes.ts @@ -81,7 +81,7 @@ export const RedirectRoutes = (config: ServerConfig) => [ .join('&'); return res.redirect( HTTP_STATUS.MOVED_TEMPORARILY, - `/?${queryString ? queryString : 'no_query=true'}#/e2ei-redirect`, + `/?${queryString.length > 0 ? queryString : 'no_query=true'}#/e2ei-redirect`, ); }), ]; diff --git a/apps/server/routes/root.ts b/apps/server/routes/root.ts index 7ccd3202aeb..ee17bbbaed5 100644 --- a/apps/server/routes/root.ts +++ b/apps/server/routes/root.ts @@ -37,7 +37,7 @@ async function addGeoIP(req: Request) { const lookup = await maxmind.open(countryDatabasePath); const result = lookup.get(ipAddress); - if (result) { + if (result !== undefined && result !== null) { countryCode = result.country?.iso_code ?? ''; } } catch { diff --git a/apps/server/util/browserUtil.ts b/apps/server/util/browserUtil.ts index fda216a4dff..3da83fd57e6 100644 --- a/apps/server/util/browserUtil.ts +++ b/apps/server/util/browserUtil.ts @@ -73,7 +73,7 @@ function parseUserAgent(userAgent?: string): ParsedUserAgent | null { userAgent = userAgent.toLowerCase(); - const getVersion = (app: string): string | undefined => (userAgent.match(new RegExp(`${app}/(.*)`, 'i')) || [])[0]; + const getVersion = (app: string): string | undefined => (userAgent.match(new RegExp(`${app}/(.*)`, 'i')) ?? [])[0]; const electronVersion = getVersion('Electron'); const wireVersion = getVersion('Wire'); diff --git a/apps/webapp/src/script/components/AppLoader/AppLoader.tsx b/apps/webapp/src/script/components/AppLoader/AppLoader.tsx index 6fc99013e63..04f8ff587f8 100644 --- a/apps/webapp/src/script/components/AppLoader/AppLoader.tsx +++ b/apps/webapp/src/script/components/AppLoader/AppLoader.tsx @@ -19,6 +19,8 @@ import {FC, ReactNode, useEffect, useRef, useState} from 'react'; +import is from '@sindresorhus/is'; + import {LoadingBar} from 'Components/LoadingBar/LoadingBar'; import {User} from 'Repositories/entity/User'; @@ -53,7 +55,7 @@ export const AppLoader: FC = ({init, children}) => { }).then(user => setSelfUser(user)); }, []); - if (selfUser) { + if (!is.nullOrUndefined(selfUser)) { return children(selfUser); } diff --git a/apps/webapp/src/script/components/AppNotification/AppNotification.tsx b/apps/webapp/src/script/components/AppNotification/AppNotification.tsx index 8de9d79f47e..ad8c96480e5 100644 --- a/apps/webapp/src/script/components/AppNotification/AppNotification.tsx +++ b/apps/webapp/src/script/components/AppNotification/AppNotification.tsx @@ -81,7 +81,7 @@ let roots: Record = {}; export const useAppNotification = (props?: AppNotificationOptions) => { const notificationId = useRef(null); - const activeWindow = props?.activeWindow || window; + const activeWindow = props?.activeWindow ?? window; useEffect(() => { setTimeout(() => { @@ -135,7 +135,7 @@ const injectToaster = (activeWindow: Window) => { const container = activeWindow.document.querySelector(APP_NOTIFICATION_SELECTOR); - if (!container) { + if (container === null) { throw new Error('Notification container not found!'); } diff --git a/apps/webapp/src/script/components/Avatar/Avatar.tsx b/apps/webapp/src/script/components/Avatar/Avatar.tsx index b442d8f759a..410d81e7b65 100644 --- a/apps/webapp/src/script/components/Avatar/Avatar.tsx +++ b/apps/webapp/src/script/components/Avatar/Avatar.tsx @@ -95,7 +95,7 @@ const Avatar = ({ event: ReactMouseEvent | ReactKeyBoardEvent, ) => { const parentNode = event.currentTarget.parentNode; - if (parentNode) { + if (parentNode !== null) { if (isKeyboardEvent(event)) { handleKeyDown({event, callback: () => onAvatarClick?.(participant), keys: [KEY.ENTER, KEY.SPACE]}); return; diff --git a/apps/webapp/src/script/components/Avatar/AvatarBadge.tsx b/apps/webapp/src/script/components/Avatar/AvatarBadge.tsx index b0ffea63885..acb990b0016 100644 --- a/apps/webapp/src/script/components/Avatar/AvatarBadge.tsx +++ b/apps/webapp/src/script/components/Avatar/AvatarBadge.tsx @@ -43,6 +43,8 @@ const AvatarBadge: React.FunctionComponent = ({state, iconSize const color: Record = { [STATE.BLOCKED]: 'var(--gray-70)', }; + const hasBackgroundColor = Object.hasOwn(backgroundColor, state); + const hasColor = Object.hasOwn(color, state); return (
= ({state, iconSize '&::before': { ...CSS_ICON(icons[state], iconSize), }, - backgroundColor: backgroundColor[state] || defaultBackgroundColor, + backgroundColor: hasBackgroundColor ? backgroundColor[state] : defaultBackgroundColor, borderRadius: '50%', - color: color[state] || defaultColor, + color: hasColor ? color[state] : defaultColor, }} data-uie-name="element-avatar-user-badge-icon" data-uie-value={state} diff --git a/apps/webapp/src/script/components/Avatar/AvatarImage.tsx b/apps/webapp/src/script/components/Avatar/AvatarImage.tsx index ed3d42ff7b7..13fcf1bfa76 100644 --- a/apps/webapp/src/script/components/Avatar/AvatarImage.tsx +++ b/apps/webapp/src/script/components/Avatar/AvatarImage.tsx @@ -20,6 +20,7 @@ import React, {useEffect, useState} from 'react'; import {CSSObject} from '@emotion/serialize'; +import is from '@sindresorhus/is'; import {Transition} from 'react-transition-group'; import {container} from 'tsyringe'; @@ -74,7 +75,7 @@ const AvatarImage: React.FunctionComponent = ({ setShowTransition(!isCached && !isSmall); try { const url = await assetRepository.getObjectUrl(pictureResource); - if (url) { + if (is.nonEmptyString(url)) { setAvatarImage(url); } setAvatarLoadingBlocked(false); @@ -95,7 +96,7 @@ const AvatarImage: React.FunctionComponent = ({ return ( setIsVisible(true)}> - + 0} timeout={showTransition ? 700 : 0}> {(state: string) => { return ( - {initials && } + {is.nonEmptyString(initials) && } { - const badges = Object.entries(config).filter(([_badge, shouldShow]) => shouldShow); + const badges = Object.entries(config).filter(([_badge, shouldShow]) => shouldShow === true); const badgesCount = badges.length; - if (!badgesCount) { + if (badgesCount === 0) { return null; } diff --git a/apps/webapp/src/script/components/Badge/components/VerificationBadges/VerificationBadges.tsx b/apps/webapp/src/script/components/Badge/components/VerificationBadges/VerificationBadges.tsx index 92066966be1..2cfcfbe6c2d 100644 --- a/apps/webapp/src/script/components/Badge/components/VerificationBadges/VerificationBadges.tsx +++ b/apps/webapp/src/script/components/Badge/components/VerificationBadges/VerificationBadges.tsx @@ -83,7 +83,7 @@ const useConversationVerificationState = (conversation: Conversation) => { }; const getMLSStatuses = ({identities, user}: {identities?: WireIdentity[]; user?: User}): MLSStatuses[] | undefined => { - if (!identities || !user) { + if (identities === undefined || user === undefined) { return undefined; } @@ -116,7 +116,11 @@ export const UserVerificationBadges = ({ }); let status: MLSStatuses | undefined = undefined; - if (mlsStatuses && mlsStatuses.length > 0 && mlsStatuses.every(status => status === MLSStatuses.VALID)) { + if ( + mlsStatuses !== undefined && + mlsStatuses.length > 0 && + mlsStatuses.every(status => status === MLSStatuses.VALID) + ) { status = MLSStatuses.VALID; } @@ -145,7 +149,7 @@ export const DeviceVerificationBadges = ({ }; async function loadUser() { - if (!identity) { + if (identity === undefined) { return; } const userEntity = await waitFor(() => @@ -161,7 +165,7 @@ export const DeviceVerificationBadges = ({ }, [identity]); let status: MLSStatuses | undefined = undefined; - if (isE2EIEnabled && identity && user) { + if (isE2EIEnabled && identity !== undefined && user !== undefined) { const mlsStatuses = getMLSStatuses({identities: [identity], user}); status = mlsStatuses?.[0]; } diff --git a/apps/webapp/src/script/components/Cells/ShareModal/CellsShareExpirationFields.tsx b/apps/webapp/src/script/components/Cells/ShareModal/CellsShareExpirationFields.tsx index a5043360215..16e0f3e6c6d 100644 --- a/apps/webapp/src/script/components/Cells/ShareModal/CellsShareExpirationFields.tsx +++ b/apps/webapp/src/script/components/Cells/ShareModal/CellsShareExpirationFields.tsx @@ -93,10 +93,10 @@ export interface CellsShareExpirationSelection { const parseTimeLabel = (value: string | number) => { const [timePart, periodPart] = `${value}`.trim().split(' '); - const [hourPart, minutePart] = (timePart || '').split(':'); + const [hourPart, minutePart] = (timePart ?? '').split(':'); const hour = Number(hourPart); const minutes = Number(minutePart); - const isPm = (periodPart || '').toUpperCase() === 'PM'; + const isPm = (periodPart ?? '').toUpperCase() === 'PM'; const hour24 = Number.isFinite(hour) ? (isPm ? (hour % 12) + 12 : hour % 12) : 0; const safeMinutes = Number.isFinite(minutes) ? minutes : 0; @@ -166,7 +166,7 @@ export const CellsShareExpirationFields = ({ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hour24, minutes, 0, 0); }, [selectedDate, selectedTime]); const isExpirationInvalid = useMemo( - () => Boolean(selectedDateTime && selectedDateTime.getTime() < Date.now()), + () => selectedDateTime !== null && selectedDateTime.getTime() < Date.now(), [selectedDateTime], ); const dateGroupStyles = isExpirationInvalid @@ -248,7 +248,7 @@ export const CellsShareExpirationFields = ({ placement="top start" shouldFlip={false} offset={8} - {...(portalContainer ? {portalContainer} : {})} + {...(portalContainer !== undefined ? {portalContainer} : {})} > @@ -286,9 +286,9 @@ export const CellsShareExpirationFields = ({ menuCSS={timeSelectMenuStyles} menuPlacement="top" maxMenuHeight={menuMaxHeight} - {...(portalContainer && {menuPortalTarget: portalContainer})} + {...(portalContainer !== undefined && {menuPortalTarget: portalContainer})} onChange={option => { - if (option) { + if (option !== null) { setSelectedTime(option as Option); } }} diff --git a/apps/webapp/src/script/components/Cells/ShareModal/shareModalSerializer.ts b/apps/webapp/src/script/components/Cells/ShareModal/shareModalSerializer.ts index 1fa2b9ba12b..f3536240001 100644 --- a/apps/webapp/src/script/components/Cells/ShareModal/shareModalSerializer.ts +++ b/apps/webapp/src/script/components/Cells/ShareModal/shareModalSerializer.ts @@ -46,10 +46,11 @@ export const serializeShareModalInput = ({ isEditingPassword, }: ShareModalInput): ShareModalSerializedInput => { const trimmedPassword = passwordValue.trim(); - const hasValidExpiration = !expirationEnabled || (expirationDateTime && !expirationInvalid); + const hasValidExpiration = + expirationEnabled === false || (expirationDateTime !== null && expirationInvalid === false); const accessEnd = expirationEnabled - ? expirationDateTime && !expirationInvalid + ? expirationDateTime !== null && expirationInvalid === false ? Math.floor(expirationDateTime.getTime() / 1000).toString() : undefined : null; diff --git a/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsConversationColumn/CellsConversationColumn.tsx b/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsConversationColumn/CellsConversationColumn.tsx index 2e94b2d0f03..8bfd49825c4 100644 --- a/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsConversationColumn/CellsConversationColumn.tsx +++ b/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsConversationColumn/CellsConversationColumn.tsx @@ -32,7 +32,7 @@ interface CellsConversationColumnProps { } export const CellsConversationColumn = ({conversation, name}: CellsConversationColumnProps) => { - if (!conversation) { + if (conversation === undefined) { return {name}; } diff --git a/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsOwnerColumn/CellsOwnerColumn.tsx b/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsOwnerColumn/CellsOwnerColumn.tsx index 827fe566fb3..8a0038d7592 100644 --- a/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsOwnerColumn/CellsOwnerColumn.tsx +++ b/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsOwnerColumn/CellsOwnerColumn.tsx @@ -29,7 +29,7 @@ interface CellsTableOwnerColumnProps { } export const CellsTableOwnerColumn = ({owner, user}: CellsTableOwnerColumnProps) => { - if (!user) { + if (user === null) { return {owner}; } diff --git a/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsShareModal/CellsShareModal.tsx b/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsShareModal/CellsShareModal.tsx index 2e9dd4497a3..6c32d0b95cc 100644 --- a/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsShareModal/CellsShareModal.tsx +++ b/apps/webapp/src/script/components/CellsGlobalView/CellsTable/CellsTableColumns/CellsShareModal/CellsShareModal.tsx @@ -71,7 +71,7 @@ export const showShareModal = ({type, uuid, cellsRepository}: ShareModalParams) primaryAction: { action: () => { const submitHandler = submitHandlers.get(modalId); - if (submitHandler) { + if (submitHandler !== undefined) { void submitHandler(); } }, @@ -114,7 +114,7 @@ const CellsShareModal = ({type, uuid, cellsRepository, modalId}: ShareModalParam // Initialize toggles and values based on existing link data useEffect(() => { - if (!isEnabled) { + if (isEnabled === false) { initializedLinkIdRef.current = null; return; } @@ -188,7 +188,7 @@ const CellsShareModal = ({type, uuid, cellsRepository, modalId}: ShareModalParam useEffect(() => { submitHandlers.set(modalId, async () => { if ( - !isEnabled || + isEnabled === false || status !== 'success' || node?.publicLink?.uuid === undefined || node.publicLink.uuid.length === 0 diff --git a/apps/webapp/src/script/components/CellsGlobalView/useSearchCellsNodes/getUsersFromNodes.ts b/apps/webapp/src/script/components/CellsGlobalView/useSearchCellsNodes/getUsersFromNodes.ts index 76a3e3e80c3..02415f85e0f 100644 --- a/apps/webapp/src/script/components/CellsGlobalView/useSearchCellsNodes/getUsersFromNodes.ts +++ b/apps/webapp/src/script/components/CellsGlobalView/useSearchCellsNodes/getUsersFromNodes.ts @@ -31,7 +31,7 @@ export const getUsersFromNodes = async ({ nodes: RestNode[]; userRepository: UserRepository; }) => { - if (!nodes?.length) { + if (nodes.length === 0) { return []; } diff --git a/apps/webapp/src/script/components/ConfigToolbar/ConfigToolbar.tsx b/apps/webapp/src/script/components/ConfigToolbar/ConfigToolbar.tsx index 6bd16cb0957..5c1f02b42f0 100644 --- a/apps/webapp/src/script/components/ConfigToolbar/ConfigToolbar.tsx +++ b/apps/webapp/src/script/components/ConfigToolbar/ConfigToolbar.tsx @@ -79,7 +79,7 @@ export function ConfigToolbar() { const conversationState = container.resolve(ConversationState); const activeConversation = conversationState?.activeConversation(); - if (!activeConversation) { + if (activeConversation === undefined || activeConversation === null) { if (isActive) { const MS_IN_SEC = 1000; timeoutId = window.setTimeout(sendMessage, messageDelaySec * MS_IN_SEC); @@ -175,7 +175,7 @@ export function ConfigToolbar() { const entries = Object.entries(configObj); return entries.map(([key, value]) => { - const path = parentPath ? `${parentPath}.${key}` : key; + const path = parentPath.length > 0 ? `${parentPath}.${key}` : key; return (
diff --git a/apps/webapp/src/script/components/ConnectRequests/ConnectionRequests.tsx b/apps/webapp/src/script/components/ConnectRequests/ConnectionRequests.tsx index fc64c153f47..9e3b9cc583c 100644 --- a/apps/webapp/src/script/components/ConnectRequests/ConnectionRequests.tsx +++ b/apps/webapp/src/script/components/ConnectRequests/ConnectionRequests.tsx @@ -57,7 +57,12 @@ export const ConnectRequests = ({ const user1Connection = user1.connection(); const user2Connection = user2.connection(); - if (!user1Connection || !user2Connection) { + if ( + user1Connection === undefined || + user1Connection === null || + user2Connection === undefined || + user2Connection === null + ) { return 0; } @@ -72,7 +77,7 @@ export const ConnectRequests = ({ const {setCurrentTab: setCurrentSidebarTab} = useSidebarStore(); const scrollToBottom = (behavior: ScrollBehavior = 'auto') => { - if (connectRequestsRefEnd.current) { + if (connectRequestsRefEnd.current !== null) { connectRequestsRefEnd.current.scrollIntoView({behavior}); } }; @@ -140,7 +145,7 @@ export const ConnectRequests = ({
{connectRequest.handle}
- {classifiedDomains && ( + {classifiedDomains !== null && classifiedDomains !== undefined && ( )} diff --git a/apps/webapp/src/script/components/Conversation/Conversation.tsx b/apps/webapp/src/script/components/Conversation/Conversation.tsx index 1d8586fba75..15e3c22beb5 100644 --- a/apps/webapp/src/script/components/Conversation/Conversation.tsx +++ b/apps/webapp/src/script/components/Conversation/Conversation.tsx @@ -157,7 +157,11 @@ export const Conversation = ({ const uploadImages = useCallback( (images: File[]) => { - if (!activeConversation || isHittingUploadLimit(images, repositories.asset)) { + if ( + activeConversation === null || + activeConversation === undefined || + isHittingUploadLimit(images, repositories.asset) + ) { return; } @@ -183,7 +187,7 @@ export const Conversation = ({ const uploadFiles = useCallback( (files: File[]) => { - if (!activeConversation) { + if (activeConversation === null || activeConversation === undefined) { return; } @@ -259,7 +263,7 @@ export const Conversation = ({ }; const clickOnCancelRequest = (messageEntity: MemberMessage): void => { - if (activeConversation) { + if (activeConversation !== null && activeConversation !== undefined) { const nextConversationEntity = conversationRepository.getNextConversation(activeConversation); mainViewModel.actions.cancelConnectionRequest(messageEntity.otherUser(), true, nextConversationEntity); } @@ -271,7 +275,8 @@ export const Conversation = ({ const isUserEntity = !isServiceEntity(userEntity); if ( - activeConversation && + activeConversation !== null && + activeConversation !== undefined && isUserEntity && (userEntity.isDeleted || (isSingleModeConversation && !userEntity.isMe)) ) { @@ -290,7 +295,7 @@ export const Conversation = ({ }; const showParticipants = (participants: User[]) => { - if (activeConversation) { + if (activeConversation !== null && activeConversation !== undefined) { openRightSidebar(PanelState.CONVERSATION_PARTICIPANTS, {entity: activeConversation, highlighted: participants}); } }; @@ -437,7 +442,12 @@ export const Conversation = ({ }; try { - if (messageEntity.fromDomain !== undefined && messageEntity.fromDomain.length > 0 && activeConversation) { + if ( + messageEntity.fromDomain !== undefined && + messageEntity.fromDomain.length > 0 && + activeConversation !== null && + activeConversation !== undefined + ) { await repositories.message.resetSession( {domain: messageEntity.fromDomain, id: messageEntity.from}, messageEntity.clientId, @@ -457,7 +467,7 @@ export const Conversation = ({ const needsUpdate = conversationLastRead < lastKnownTimestamp; // if no message provided it means we need to jump to the last message - if (needsUpdate && (!messageEntity || isLastReceivedMessage(messageEntity, conversationEntity))) { + if (needsUpdate && (messageEntity === undefined || isLastReceivedMessage(messageEntity, conversationEntity))) { conversationEntity.setTimestamp(lastKnownTimestamp, ConversationEntity.TIMESTAMP_TYPE.LAST_READ); repositories.message.markAsRead(conversationEntity); } @@ -558,7 +568,7 @@ export const Conversation = ({ rootProps={getRootProps()} inputProps={getInputProps()} > - {activeConversation && ( + {activeConversation !== null && activeConversation !== undefined && ( <> )} - {isGiphyModalOpen && inputValue && ( + {isGiphyModalOpen && inputValue.length > 0 && ( )} diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFileModal/CellsNewFileModal.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFileModal/CellsNewFileModal.tsx index f2f192ee646..4bd3e263104 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFileModal/CellsNewFileModal.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFileModal/CellsNewFileModal.tsx @@ -80,7 +80,7 @@ export const CellsNewFileModal = ({ {t('cells.newItemMenuModal.secondaryAction')} - + {t('cells.newItemMenuModal.primaryAction')} diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFolderModal/CellsNewFolderModal.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFolderModal/CellsNewFolderModal.tsx index 8854da0307d..fcb83b9f44a 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFolderModal/CellsNewFolderModal.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsHeader/CellsNewMenu/CellsNewFolderModal/CellsNewFolderModal.tsx @@ -71,7 +71,7 @@ export const CellsNewFolderModal = ({ {t('cells.newItemMenuModal.secondaryAction')} - + {t('cells.newItemMenuModal.primaryAction')} diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsNodeShareModal/CellsNodeShareModal.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsNodeShareModal/CellsNodeShareModal.tsx index 5b3ababa924..9c0b66adea6 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsNodeShareModal/CellsNodeShareModal.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsNodeShareModal/CellsNodeShareModal.tsx @@ -72,7 +72,7 @@ export const showShareModal = ({type, uuid, conversationId, cellsRepository}: Sh primaryAction: { action: () => { const submitHandler = submitHandlers.get(modalId); - if (submitHandler) { + if (submitHandler !== undefined) { void submitHandler(); } }, @@ -151,7 +151,7 @@ const CellShareModalContent = ({ }; useEffect(() => { - if (!isEnabled) { + if (isEnabled === false) { initializedLinkIdRef.current = null; return; } @@ -202,7 +202,7 @@ const CellShareModalContent = ({ useEffect(() => { submitHandlers.set(modalId, async () => { if ( - !isEnabled || + isEnabled === false || status !== 'success' || node?.publicLink?.uuid === undefined || node.publicLink.uuid.length === 0 diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableOwnerColumn/CellsTableOwnerColumn.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableOwnerColumn/CellsTableOwnerColumn.tsx index 543fa18825b..73990505cf0 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableOwnerColumn/CellsTableOwnerColumn.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableOwnerColumn/CellsTableOwnerColumn.tsx @@ -29,7 +29,7 @@ interface CellsTableOwnerColumnProps { } export const CellsTableOwnerColumn = ({owner, user}: CellsTableOwnerColumnProps) => { - if (!user) { + if (user === null) { return {owner}; } diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsMoveNodeModal/CellsFoldersListModalContent/CellsFoldersListModalContent.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsMoveNodeModal/CellsFoldersListModalContent/CellsFoldersListModalContent.tsx index f91a3d2e6b7..04ce0195c59 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsMoveNodeModal/CellsFoldersListModalContent/CellsFoldersListModalContent.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsMoveNodeModal/CellsFoldersListModalContent/CellsFoldersListModalContent.tsx @@ -47,7 +47,7 @@ export const CellsFoldersListModalContent = ({ }: CellsFoldersListModalContentProps) => { const breadcrumbs = getBreadcrumbsFromPath({baseCrumb: `${conversationName} files`, currentPath}); - const shouldDisplayEmptyItems = status === 'success' && !items.length; + const shouldDisplayEmptyItems = status === 'success' && items.length === 0; const handleFolderNavigate = (path: string) => { const newPath = path.split('/').slice(1).join('/'); diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsRenameNodeModal/useCellsRenameNodeForm/useCellsRenameNodeForm.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsRenameNodeModal/useCellsRenameNodeForm/useCellsRenameNodeForm.ts index 429b6e0bc32..0f5f10fbfdd 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsRenameNodeModal/useCellsRenameNodeForm/useCellsRenameNodeForm.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/CellsRenameNodeModal/useCellsRenameNodeForm/useCellsRenameNodeForm.ts @@ -40,11 +40,11 @@ export const useCellsRenameForm = ({node, cellsRepository, onSuccess}: UseCellsR const originalBaseName = trimFileExtension(node.name); const normalizedName = name.trim(); const hasInvalidCharacters = INVALID_CHARACTERS.some(char => normalizedName.includes(char)); - const isDisabled = isSubmitting || normalizedName === originalBaseName || !normalizedName; + const isDisabled = isSubmitting || normalizedName === originalBaseName || normalizedName.length === 0; const buildNewName = (baseName: string) => { const extension = getFileExtension(node.name); - return extension ? `${baseName}.${extension}` : baseName; + return extension.length > 0 ? `${baseName}.${extension}` : baseName; }; const renameNode = async (name: string) => { @@ -66,7 +66,7 @@ export const useCellsRenameForm = ({node, cellsRepository, onSuccess}: UseCellsR setError(null); setIsSubmitting(true); - if (!normalizedName) { + if (normalizedName.length === 0) { setError(t('cells.renameNodeModal.nameRequired')); setIsSubmitting(false); return; diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/useRestoreParentNode/useRestoreParentNode.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/useRestoreParentNode/useRestoreParentNode.ts index 9a233a03b2f..e0e5efff402 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/useRestoreParentNode/useRestoreParentNode.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/CellsTable/CellsTableColumns/CellsTableRowOptions/useRestoreParentNode/useRestoreParentNode.ts @@ -54,7 +54,7 @@ export const useRestoreParentNode = ({ try { const rootParentNode = await cellsRepository.lookupNodeByPath({path}); - if (!rootParentNode) { + if (rootParentNode === undefined) { throw new Error('Root parent node not found'); } diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/ConversationCells.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/ConversationCells.tsx index 4cbe815752b..8e7d38f411e 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/ConversationCells.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/ConversationCells.tsx @@ -102,8 +102,8 @@ export const ConversationCells = memo( }); const trimmedSearchValue = searchValue.trim(); - const isSearchActive = !!trimmedSearchValue; - const isSearchViewIdle = isSearchViewOpen && !trimmedSearchValue; + const isSearchActive = trimmedSearchValue.length > 0; + const isSearchViewIdle = isSearchViewOpen && trimmedSearchValue.length === 0; const wasSearchViewOpen = useRef(isSearchViewOpen); const handleClearSearch = () => { @@ -112,7 +112,7 @@ export const ConversationCells = memo( }; useEffect(() => { - if (wasSearchViewOpen.current && !isSearchViewOpen && searchValue) { + if (wasSearchViewOpen.current && !isSearchViewOpen && searchValue.length > 0) { handleClearSearch(); } wasSearchViewOpen.current = isSearchViewOpen; @@ -139,7 +139,7 @@ export const ConversationCells = memo( const isError = nodesStatus === 'error'; const isSuccess = nodesStatus === 'success'; - const hasNodes = !!nodes.length; + const hasNodes = nodes.length > 0; const emptyView = !isError && !hasNodes && isCellsStateReady; const isTableVisible = (isSuccess || isLoading) && isCellsStateReady && !isSearchViewIdle; diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/common/CellsModal/CellsModalContext/CellsModalContext.tsx b/apps/webapp/src/script/components/Conversation/ConversationCells/common/CellsModal/CellsModalContext/CellsModalContext.tsx index fa6ca2f15c7..ee904254679 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/common/CellsModal/CellsModalContext/CellsModalContext.tsx +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/common/CellsModal/CellsModalContext/CellsModalContext.tsx @@ -27,7 +27,7 @@ export const CellsModalContext = createContext(nul export const useCellsModal = () => { const context = useContext(CellsModalContext); - if (!context) { + if (context === null) { throw new Error('useCellsModal must be used within a CellsModalProvider'); } return context; diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/common/getCellsApiPath/getCellsApiPath.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/common/getCellsApiPath/getCellsApiPath.ts index e51e631e19c..6ea7f282592 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/common/getCellsApiPath/getCellsApiPath.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/common/getCellsApiPath/getCellsApiPath.ts @@ -34,7 +34,7 @@ export const getCellsApiPath = ({ // When there’re no files/folders in the bin, the api returns 404. // It’s because, we pass “recycle_bin” as a path for the api, but the folder itself doesn’t exist on the backend yet - it’s being created when a file/folder is deleted. // That's why if the current path (in the url) is the recycle bin, we don't want to add it to the path. We use the "deleted" flag instead (in useGetAllCellsNodes). - const path = currentPath && currentPath !== RECYCLE_BIN_PATH ? `/${currentPath}` : ''; + const path = currentPath.length > 0 && currentPath !== RECYCLE_BIN_PATH ? `/${currentPath}` : ''; return `${id}@${domain}${path}`; }; diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/common/openBreadcrumb/openBreadcrumb.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/common/openBreadcrumb/openBreadcrumb.ts index c76d2d8726d..8d820f02408 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/common/openBreadcrumb/openBreadcrumb.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/common/openBreadcrumb/openBreadcrumb.ts @@ -35,7 +35,7 @@ export const openBreadcrumb = ({conversationQualifiedId, path, event}: OpenBread generateConversationUrl({ id: conversationQualifiedId.id, domain: conversationQualifiedId.domain, - filePath: path ? `files/${encodeURIComponent(path)}` : 'files', + filePath: path.length > 0 ? `files/${encodeURIComponent(path)}` : 'files', }), )(event); }; diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/common/openFolder/openFolder.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/common/openFolder/openFolder.ts index 82ab04ae4f3..12a771bd4fe 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/common/openFolder/openFolder.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/common/openFolder/openFolder.ts @@ -34,7 +34,7 @@ interface OpenFolderParams { export const openFolder = ({conversationQualifiedId, name, event}: OpenFolderParams) => { const currentPath = getCellsFilesPath(); - const pathSegments = currentPath ? currentPath.split('/') : []; + const pathSegments = currentPath.length > 0 ? currentPath.split('/') : []; const encodedSegments = [...pathSegments, name].map(segment => encodeURIComponent(segment)); const newPath = encodedSegments.join('/'); diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/common/recycleBin/recycleBin.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/common/recycleBin/recycleBin.ts index 4e81ca1a2a8..42fbd17393f 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/common/recycleBin/recycleBin.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/common/recycleBin/recycleBin.ts @@ -40,5 +40,5 @@ export const isInRecycleBin = () => { export const getNodeRootParentPath = ({nodePath}: {nodePath: string}) => { const segments = nodePath.split('/'); const recycleBinIndex = segments.indexOf(RECYCLE_BIN_PATH); - return segments[recycleBinIndex + 1] || ''; + return segments[recycleBinIndex + 1] ?? ''; }; diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/common/useCellsNewNodeForm/cellsNodeFormUtils.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/common/useCellsNewNodeForm/cellsNodeFormUtils.ts index d544889dd13..e34d3d8d44d 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/common/useCellsNewNodeForm/cellsNodeFormUtils.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/common/useCellsNewNodeForm/cellsNodeFormUtils.ts @@ -28,7 +28,7 @@ export const NODE_NAME_MAX_LENGTH = 64; const INVALID_NODE_NAME_PATTERN = /[\\/"]/u; export const getNameValidationError = (name: string): Maybe => { - if (!name) { + if (name.length === 0) { return Maybe.just(t('cells.newItemMenuModalForm.nameRequired')); } diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/useGetAllCellsNodes/getUsersFromNodes.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/useGetAllCellsNodes/getUsersFromNodes.ts index 76a3e3e80c3..02415f85e0f 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/useGetAllCellsNodes/getUsersFromNodes.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/useGetAllCellsNodes/getUsersFromNodes.ts @@ -31,7 +31,7 @@ export const getUsersFromNodes = async ({ nodes: RestNode[]; userRepository: UserRepository; }) => { - if (!nodes?.length) { + if (nodes.length === 0) { return []; } diff --git a/apps/webapp/src/script/components/Conversation/ConversationCells/useRefreshCellsState/useRefreshCellsState.ts b/apps/webapp/src/script/components/Conversation/ConversationCells/useRefreshCellsState/useRefreshCellsState.ts index 395b0187afa..e9b2f556577 100644 --- a/apps/webapp/src/script/components/Conversation/ConversationCells/useRefreshCellsState/useRefreshCellsState.ts +++ b/apps/webapp/src/script/components/Conversation/ConversationCells/useRefreshCellsState/useRefreshCellsState.ts @@ -57,7 +57,7 @@ export const useRefreshCellsState = ({ return undefined; } - if (intervalRef.current) { + if (intervalRef.current !== null) { clearInterval(intervalRef.current); intervalRef.current = null; } @@ -83,7 +83,7 @@ export const useRefreshCellsState = ({ }, REFRESH_INTERVAL_MS); return () => { - if (intervalRef.current) { + if (intervalRef.current !== null) { clearInterval(intervalRef.current); intervalRef.current = null; } diff --git a/apps/webapp/src/script/components/Conversation/ReadOnlyConversationMessage/ReadOnlyConversationMessage.tsx b/apps/webapp/src/script/components/Conversation/ReadOnlyConversationMessage/ReadOnlyConversationMessage.tsx index 8dbf1982ce6..c32c62b5c3d 100644 --- a/apps/webapp/src/script/components/Conversation/ReadOnlyConversationMessage/ReadOnlyConversationMessage.tsx +++ b/apps/webapp/src/script/components/Conversation/ReadOnlyConversationMessage/ReadOnlyConversationMessage.tsx @@ -46,14 +46,14 @@ export const ReadOnlyConversationMessage: FC = } = useKoSubscribableChildren(conversation, ['readOnlyState', 'is1to1', 'participating_user_ets']); const user = is1to1 ? participatingUserEts[0] : null; - const {isBlocked: isUserBlocked} = useKoSubscribableChildren(user || userPlaceholder, ['isBlocked']); + const {isBlocked: isUserBlocked} = useKoSubscribableChildren(user !== null ? user : userPlaceholder, ['isBlocked']); - if (!user) { + if (user === null) { // This should never happen for 1:1 conversations return null; } - if (isUserBlocked) { + if (isUserBlocked === true) { return ( {t('conversationWithBlockedUser')} diff --git a/apps/webapp/src/script/components/EmojiPicker/EmojiPicker.tsx b/apps/webapp/src/script/components/EmojiPicker/EmojiPicker.tsx index d3b7cee391d..00c1de1afb2 100644 --- a/apps/webapp/src/script/components/EmojiPicker/EmojiPicker.tsx +++ b/apps/webapp/src/script/components/EmojiPicker/EmojiPicker.tsx @@ -57,9 +57,9 @@ export const EmojiPicker: FunctionComponent = properties function updateSize() { const emojiPickerWidth = 350; const reactionMenuOpenerButtonHeight = 40; - const left = mainElement && posX - emojiPickerWidth; + const left = mainElement !== null ? posX - emojiPickerWidth : 0; const top = Math.max( - mainElement && window.innerHeight - posY < mainElement.clientHeight + mainElement !== null && window.innerHeight - posY < mainElement.clientHeight ? posY - mainElement.offsetHeight + reactionMenuOpenerButtonHeight : posY, 0, diff --git a/apps/webapp/src/script/components/FileFullscreenModal/FileEditor/FileEditor.tsx b/apps/webapp/src/script/components/FileFullscreenModal/FileEditor/FileEditor.tsx index f551e167c92..243650cab2a 100644 --- a/apps/webapp/src/script/components/FileFullscreenModal/FileEditor/FileEditor.tsx +++ b/apps/webapp/src/script/components/FileFullscreenModal/FileEditor/FileEditor.tsx @@ -107,7 +107,7 @@ export const FileEditor = ({id}: FileEditorProps) => { }, [node, fetchNode]); useEffect(() => { - if (isLoading || isRecycled || (!isError && node)) { + if (isLoading || isRecycled || (isError === false && node !== null)) { return; } @@ -134,7 +134,7 @@ export const FileEditor = ({id}: FileEditorProps) => { }, [handleRetry, isError, isLoading, isRecycled, node]); useEffect(() => { - if (!isLoading && !isError && node && !isRecycled) { + if (isLoading === false && isError === false && node !== null && isRecycled === false) { hasShownErrorModal.current = false; } }, [isError, isLoading, isRecycled, node]); diff --git a/apps/webapp/src/script/components/Giphy/Giphy.tsx b/apps/webapp/src/script/components/Giphy/Giphy.tsx index 8a60f4f7603..c71a5024b97 100644 --- a/apps/webapp/src/script/components/Giphy/Giphy.tsx +++ b/apps/webapp/src/script/components/Giphy/Giphy.tsx @@ -109,7 +109,7 @@ const Giphy: FC = ({giphyRepository, defaultGiphyState = GiphyState. const onGridClick = async () => getGifs(currentQuery); const onBackClick = () => { - if (currentGif) { + if (currentGif !== null) { setGifs([currentGif]); setSelectedGif(currentGif); setGiphyState(GiphyState.RESULT); @@ -149,7 +149,7 @@ const Giphy: FC = ({giphyRepository, defaultGiphyState = GiphyState. }; const onSend = () => { - if (selectedGif) { + if (selectedGif !== null) { amplify.publish(WebAppEvents.EXTENSIONS.GIPHY.SEND, selectedGif.animated, currentQuery); setSelectedGif(null); @@ -170,7 +170,7 @@ const Giphy: FC = ({giphyRepository, defaultGiphyState = GiphyState. }; useEffect(() => { - if (inputValue) { + if (inputValue.length > 0) { requestAnimationFrame(() => setPlayAnimation(true)); showGiphy(inputValue); } @@ -223,7 +223,7 @@ const Giphy: FC = ({giphyRepository, defaultGiphyState = GiphyState.
)} - {isSingleGif && currentGif && ( + {isSingleGif && currentGif !== null && (
@@ -272,8 +272,8 @@ const Giphy: FC = ({giphyRepository, defaultGiphyState = GiphyState.
)} - {error && ( + {error !== null && (

{errorHeadline} diff --git a/apps/webapp/src/script/components/Image/Image.tsx b/apps/webapp/src/script/components/Image/Image.tsx index 4686e115e03..acefd372cfc 100644 --- a/apps/webapp/src/script/components/Image/Image.tsx +++ b/apps/webapp/src/script/components/Image/Image.tsx @@ -81,7 +81,7 @@ export const Image = ({ const {isFileSharingReceivingEnabled} = useKoSubscribableChildren(teamState, ['isFileSharingReceivingEnabled']); useEffect(() => { - if (!imageUrl && isInViewport && isFileSharingReceivingEnabled) { + if (imageUrl === undefined && isInViewport && isFileSharingReceivingEnabled) { void (async () => { try { const allowedImageTypes = [ @@ -114,12 +114,12 @@ export const Image = ({ const dummyImageUrl = `data:image/svg+xml;utf8,`; const assetUrl = imageUrl?.url ?? dummyImageUrl; - const isLoading = !imageUrl; + const isLoading = imageUrl === undefined; return ( setIsInViewport(true)} - css={getWrapperStyles(!!onClick)} + css={getWrapperStyles(onClick !== undefined)} className={cx(className, {'loading-dots image-asset--no-image': isLoading})} onClick={event => { if (!isLoading) { diff --git a/apps/webapp/src/script/components/InViewport/InViewport.tsx b/apps/webapp/src/script/components/InViewport/InViewport.tsx index c3280e54f1a..a9eb363ccba 100644 --- a/apps/webapp/src/script/components/InViewport/InViewport.tsx +++ b/apps/webapp/src/script/components/InViewport/InViewport.tsx @@ -46,7 +46,7 @@ export const InViewport = ({ useEffect(() => { const element = domNode.current; - if (!element) { + if (element === null) { return undefined; } @@ -71,7 +71,7 @@ export const InViewport = ({ onVisibilityLostTriggered = false; } - if (!onVisibilityLost) { + if (onVisibilityLost === undefined) { releaseTrackers(); } } diff --git a/apps/webapp/src/script/components/InputBar/InputBar.tsx b/apps/webapp/src/script/components/InputBar/InputBar.tsx index a09c77aab91..452fc740bb6 100644 --- a/apps/webapp/src/script/components/InputBar/InputBar.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBar.tsx @@ -161,10 +161,17 @@ export const InputBar = ({ }, }); - const inputPlaceholder = messageTimer ? t('tooltipConversationEphemeral') : t('tooltipConversationInputPlaceholder'); + const inputPlaceholder = + messageTimer !== null && messageTimer !== undefined + ? t('tooltipConversationEphemeral') + : t('tooltipConversationInputPlaceholder'); const isConnectionRequest = isOutgoingRequest || isIncomingRequest; - const hasLocalEphemeralTimer = isSelfDeletingMessagesEnabled && !!localMessageTimer && !hasGlobalMessageTimer; + const hasLocalEphemeralTimer = + isSelfDeletingMessagesEnabled && + localMessageTimer !== null && + localMessageTimer !== undefined && + !hasGlobalMessageTimer; const isTypingRef = useRef(false); const shouldReplaceEmoji = useUserPropertyValue( @@ -234,7 +241,7 @@ export const InputBar = ({ sendPastedFile: fileHandling.sendPastedFile, }); - if (fileHandling.pastedFile && !!isCellsEnabled) { + if (fileHandling.pastedFile !== null && fileHandling.pastedFile !== undefined && isCellsEnabled) { uploadPastedFiles(fileHandling.pastedFile); fileHandling.clearPastedFile(); } @@ -263,18 +270,18 @@ export const InputBar = ({ void sendMessage(); }, [isSendingDisabled, sendMessage]); - const showAvatar = !!messageContent.text.length; + const showAvatar = messageContent.text.length > 0; return (
{isTypingIndicatorEnabled && } - {classifiedDomains && !isConnectionRequest && ( + {classifiedDomains !== null && classifiedDomains !== undefined && !isConnectionRequest && ( )} - {isReplying && !isEditing && replyMessageEntity && ( + {isReplying && !isEditing && replyMessageEntity !== null && replyMessageEntity !== undefined && ( cancelMessageReply(false)} /> )} @@ -282,7 +289,7 @@ export const InputBar = ({ className={cx(`conversation-input-bar__input input-bar-container`, { [`conversation-input-bar__input--editing`]: isEditing, 'input-bar-container--with-toolbar': formatToolbar.open && showMarkdownPreview, - 'input-bar-container--with-files': !!files.length, + 'input-bar-container--with-files': files.length > 0, })} > {!isOutgoingRequest && ( @@ -297,7 +304,7 @@ export const InputBar = ({ /> )}
- {!isSelfUserRemoved && !fileHandling.pastedFile && ( + {!isSelfUserRemoved && (fileHandling.pastedFile === null || fileHandling.pastedFile === undefined) && ( - {!!files.length && } + {files.length > 0 && ( + + )} )} - {fileHandling.pastedFile && !isCellsEnabled && ( + {fileHandling.pastedFile !== null && fileHandling.pastedFile !== undefined && !isCellsEnabled && ( { const node = getSelectedNode(selection); const existingLink = $findMatchingParent(node, $isLinkNode); - if (!existingLink) { + if (existingLink === null) { setEditingLink({ text: selection.getTextContent(), url: '', @@ -113,7 +113,7 @@ export const useLinkState = () => { editor.update(() => { const sanitizedUrl = sanitizeUrl(url); - if (editingLink.node && $isLinkNode(editingLink.node)) { + if (editingLink.node !== null && $isLinkNode(editingLink.node)) { if (!editingLink.node.isAttached()) { // Node no longer exists in the editor return; @@ -140,7 +140,7 @@ export const useLinkState = () => { return; } - if (editingLink.selection) { + if (editingLink.selection !== null) { restoreSelection(selection, editingLink.selection); } @@ -185,7 +185,7 @@ export const useLinkState = () => { const handleClickCommand = useCallback( (event: MouseEvent) => { const linkDomNode = (event.target as HTMLElement).closest('a'); - if (!linkDomNode) { + if (linkDomNode === null) { return false; } @@ -199,7 +199,7 @@ export const useLinkState = () => { const node = getSelectedNode(selection); const linkNode = $findMatchingParent(node, $isLinkNode); - if (linkNode) { + if (linkNode !== null) { handleLinkClick(linkNode); } }); diff --git a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/RichTextEditor.tsx b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/RichTextEditor.tsx index a7ef923fcf0..50fc895a194 100644 --- a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/RichTextEditor.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/RichTextEditor.tsx @@ -112,7 +112,7 @@ export const RichTextEditor = ({ const handleChange = (editorState: EditorState) => { editorState.read(() => { - if (!editorRef.current) { + if (editorRef.current === null) { return; } @@ -129,7 +129,7 @@ export const RichTextEditor = ({ }); }; - const isEditing = !!editedMessage; + const isEditing = editedMessage !== undefined; return ( diff --git a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/nodes/Mention.tsx b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/nodes/Mention.tsx index c6d0c04a22a..28b51099160 100644 --- a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/nodes/Mention.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/nodes/Mention.tsx @@ -71,7 +71,8 @@ export const Mention = (props: MentionComponentProps) => { classes.push(classNameFocused); } - return classes.join(' ').trim() || undefined; + const classNames = classes.join(' ').trim(); + return classNames.length > 0 ? classNames : undefined; }, [className, classNameFocused, isFocused]); const deleteMention = useCallback( @@ -81,7 +82,7 @@ export const Mention = (props: MentionComponentProps) => { let shouldSelectNode = false; const selectedNodes = rangeSelection?.getNodes(); - const selectedNode = selectedNodes?.length === 1 ? selectedNodes[0] : null; + const selectedNode = selectedNodes !== undefined && selectedNodes.length === 1 ? selectedNodes[0] : null; if (selectedNode !== null) { const isCurrentNode = nodeKey === selectedNode?.getKey(); if (event.key === KEY.BACKSPACE) { @@ -130,7 +131,7 @@ export const Mention = (props: MentionComponentProps) => { const moveCursor = useCallback( (event: KeyboardEvent) => { const node = $getNodeByKey(nodeKey); - if (!node || !node.isSelected()) { + if (node === null || node === undefined || node.isSelected() === false) { return false; } diff --git a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/EmojiPickerPlugin/EmojiPickerPlugin.tsx b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/EmojiPickerPlugin/EmojiPickerPlugin.tsx index 66059caa339..71a8dd71175 100644 --- a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/EmojiPickerPlugin/EmojiPickerPlugin.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/EmojiPickerPlugin/EmojiPickerPlugin.tsx @@ -77,13 +77,13 @@ export class EmojiOption extends MenuOption { super(title + options.keywords?.join('_')); this.title = title.replace(/_/g, ' '); this.emoji = emoji; - this.keywords = options.keywords || []; + this.keywords = options.keywords ?? []; } } -const emojiUsageCount: Record = loadValue(StorageKey.CONVERSATION.EMOJI_USAGE_COUNT) || {}; +const emojiUsageCount: Record = loadValue(StorageKey.CONVERSATION.EMOJI_USAGE_COUNT) ?? {}; -const getUsageCount = (emojiName: string): number => emojiUsageCount[emojiName] || 0; +const getUsageCount = (emojiName: string): number => emojiUsageCount[emojiName] ?? 0; const MAX_EMOJI_SUGGESTION_COUNT = 5; @@ -158,7 +158,7 @@ export function EmojiPickerPlugin({openStateRef}: Props) { const getPosition = () => { const nativeSelection = window.getSelection(); - if (!rootElement || !nativeSelection) { + if (rootElement === null || nativeSelection === null) { return {bottom: 0, left: 0}; } @@ -174,7 +174,7 @@ export function EmojiPickerPlugin({openStateRef}: Props) { anchorElementRef, {selectedIndex, selectOptionAndCleanUp, setHighlightedIndex}, ) => { - if (!anchorElementRef.current || !options.length) { + if (anchorElementRef.current === null || options.length === 0) { return null; } diff --git a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/MentionsPlugin/MentionsPlugin.tsx b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/MentionsPlugin/MentionsPlugin.tsx index 460d44044ac..9e71940733a 100644 --- a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/MentionsPlugin/MentionsPlugin.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/MentionsPlugin/MentionsPlugin.tsx @@ -128,7 +128,7 @@ export function MentionsPlugin({onSearch, openStateRef}: MentionsPluginProps) { const insertMention = useCallback( (selectedOption: MenuOption, nodeToReplace: TextNode | null, closeMenu: () => void) => { editor.update(() => { - if (nodeToReplace) { + if (nodeToReplace !== null) { const mentionNode = $createMentionNode(TRIGGER, selectedOption.value); nodeToReplace.replace(mentionNode); mentionNode.insertAfter($createTextNode(' ')); @@ -142,7 +142,7 @@ export function MentionsPlugin({onSearch, openStateRef}: MentionsPluginProps) { const checkForMentionMatch = useCallback((text: string) => { // Don't show the menu if the next character is a word character const info = getSelectionInfo([TRIGGER]); - if (!info || (info.isTextNode && info.wordCharAfterCursor)) { + if (info === undefined || (info.isTextNode && info.wordCharAfterCursor)) { return null; } return checkForMentions(text); @@ -151,7 +151,7 @@ export function MentionsPlugin({onSearch, openStateRef}: MentionsPluginProps) { const rootElement = editor.getRootElement(); const getPosition = () => { - if (!rootElement) { + if (rootElement === null) { return {bottom: 0, left: 0}; } @@ -161,7 +161,7 @@ export function MentionsPlugin({onSearch, openStateRef}: MentionsPluginProps) { }; const menuRenderFn: MenuRenderFn = (anchorElementRef, params) => { - if (!anchorElementRef.current || !options.length) { + if (anchorElementRef.current === null || options.length === 0) { return null; } diff --git a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/PastePlugin/PastePlugin.tsx b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/PastePlugin/PastePlugin.tsx index a76bd397a82..54289af8815 100644 --- a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/PastePlugin/PastePlugin.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/PastePlugin/PastePlugin.tsx @@ -92,7 +92,7 @@ export const PastePlugin = ({getMentionCandidates, isPreviewMode}: PastePluginPr */ const handleLexicalMentions = useCallback( (doc: Document, selection: Selection, availableUsers: User[]): boolean => { - if (!selection) { + if (selection === null) { return false; } @@ -112,7 +112,7 @@ export const PastePlugin = ({getMentionCandidates, isPreviewMode}: PastePluginPr // Only convert to plain text if the mention is invalid if (!isValid) { const parent = mention.parentNode; - if (!parent) { + if (parent === null) { return; } @@ -225,7 +225,7 @@ export const PastePlugin = ({getMentionCandidates, isPreviewMode}: PastePluginPr const handlePaste = useCallback( (event: ClipboardEvent) => { const clipboardData = event.clipboardData; - if (!clipboardData) { + if (clipboardData === null) { return false; } @@ -236,12 +236,12 @@ export const PastePlugin = ({getMentionCandidates, isPreviewMode}: PastePluginPr editor.update(() => { try { const selection = $getSelection(); - if (!selection) { + if (selection === null) { $getSelection()?.insertText(plainText); return false; } - if (!htmlContent) { + if (htmlContent.length === 0) { selection.insertText(plainText); return false; } diff --git a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/TypeaheadMenuPlugin/TypeaheadMenuPlugin.tsx b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/TypeaheadMenuPlugin/TypeaheadMenuPlugin.tsx index d2c3fcf22e1..2f238f064f3 100644 --- a/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/TypeaheadMenuPlugin/TypeaheadMenuPlugin.tsx +++ b/apps/webapp/src/script/components/InputBar/InputBarEditor/RichTextEditor/plugins/TypeaheadMenuPlugin/TypeaheadMenuPlugin.tsx @@ -227,14 +227,17 @@ export function getScrollParent(element: HTMLElement, includeHidden: boolean): H if (style.position === 'fixed') { return document.body; } - for (let parent: HTMLElement | null = element; (parent = parent.parentElement); ) { + let parent: HTMLElement | null = element.parentElement; + while (parent !== null) { style = getComputedStyle(parent); if (excludeStaticParent && style.position === 'static') { + parent = parent.parentElement; continue; } if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) { return parent; } + parent = parent.parentElement; } return document.body; } @@ -391,7 +394,7 @@ function LexicalPopoverMenu({ KEY_ARROW_DOWN_COMMAND, payload => { const event = payload; - if (options !== null && options.length && selectedIndex !== null) { + if (options !== null && options.length > 0 && selectedIndex !== null) { const newSelectedIndex = (selectedIndex + 1) % options.length; updateSelectedIndex(newSelectedIndex); event.preventDefault(); @@ -405,7 +408,7 @@ function LexicalPopoverMenu({ KEY_ARROW_UP_COMMAND, payload => { const event = payload; - if (options !== null && options.length && selectedIndex !== null) { + if (options !== null && options.length > 0 && selectedIndex !== null) { const newSelectedIndex = selectedIndex > 0 ? selectedIndex - 1 : options.length - 1; updateSelectedIndex(newSelectedIndex); event.preventDefault(); @@ -471,10 +474,10 @@ function LexicalPopoverMenu({ const menu = menuRenderFn(anchorElementRef, listItemProps, resolution.match.matchingString); useLayoutEffect(() => { - if (onMenuVisibilityChange && menu !== null && !menuVisible) { + if (onMenuVisibilityChange !== undefined && menu !== null && !menuVisible) { onMenuVisibilityChange(true); setMenuVisible(true); - } else if (onMenuVisibilityChange && menu === null && menuVisible) { + } else if (onMenuVisibilityChange !== undefined && menu === null && menuVisible) { onMenuVisibilityChange(false); setMenuVisible(false); } @@ -641,7 +644,7 @@ export function TypeaheadMenuPlugin({ return; } const match = triggerFn(text, editor); - onQueryChange(match ? match.matchingString : null); + onQueryChange(match !== null ? match.matchingString : null); if (match !== null && !isSelectionOnEntityBoundary(editor, match.leadOffset)) { const isRangePositioned = tryToPositionRange(match.leadOffset, range); diff --git a/apps/webapp/src/script/components/InputBar/useFileHandling/useFileHandling.ts b/apps/webapp/src/script/components/InputBar/useFileHandling/useFileHandling.ts index d1deea4cca2..1b1123e91b8 100644 --- a/apps/webapp/src/script/components/InputBar/useFileHandling/useFileHandling.ts +++ b/apps/webapp/src/script/components/InputBar/useFileHandling/useFileHandling.ts @@ -44,7 +44,7 @@ export const useFileHandling = ({uploadDroppedFiles, uploadImages, isFileNameKep const clearPastedFile = () => setPastedFile(null); const sendPastedFile = () => { - if (pastedFile) { + if (pastedFile !== null) { uploadDroppedFiles([pastedFile]); clearPastedFile(); } @@ -57,7 +57,7 @@ export const useFileHandling = ({uploadDroppedFiles, uploadImages, isFileNameKep }; useEffect(() => { - if (!pastedFile) { + if (pastedFile === null) { return () => undefined; } diff --git a/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageHandling.ts b/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageHandling.ts index 3e81649a649..b12407e7a4d 100644 --- a/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageHandling.ts +++ b/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageHandling.ts @@ -189,9 +189,9 @@ export const useMessageHandling = ({ ); const cancelSending = useCallback(() => { - if (editedMessage) { + if (editedMessage !== undefined) { cancelMesssageEditingWithDraftReset(); - } else if (replyMessageEntity) { + } else if (replyMessageEntity !== null) { cancelMessageReply(); } }, [editedMessage, replyMessageEntity, cancelMesssageEditingWithDraftReset, cancelMessageReply]); diff --git a/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageSend/useMessageSend.ts b/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageSend/useMessageSend.ts index 703a3f8e9d3..b31ffe8b063 100644 --- a/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageSend/useMessageSend.ts +++ b/apps/webapp/src/script/components/InputBar/useMessageHandling/useMessageSend/useMessageSend.ts @@ -93,7 +93,7 @@ export const useMessageSend = ({ const cellsEnabled = Config.getConfig().FEATURE.ENABLE_CELLS; const generateQuote = useCallback(async (): Promise => { - return !replyMessageEntity + return replyMessageEntity === null ? Promise.resolve(undefined) : eventRepository.eventService .loadEvent(replyMessageEntity.conversation_id, replyMessageEntity.id) @@ -115,11 +115,11 @@ export const useMessageSend = ({ draftState.reset(); }); - if (!messageText.length && editedMessage) { + if (messageText.length === 0 && editedMessage !== undefined) { return messageRepository.deleteMessageForEveryone(conversation, editedMessage); } - if (editedMessage) { + if (editedMessage !== undefined) { messageRepository .sendMessageEdit(conversation, messageText, editedMessage, mentionEntities) .catch((error: unknown) => { @@ -199,7 +199,7 @@ export const useMessageSend = ({ return; } - if (pastedFile) { + if (pastedFile !== null) { return void sendPastedFile(); } @@ -222,7 +222,7 @@ export const useMessageSend = ({ return; } - if (editedMessage) { + if (editedMessage !== undefined) { await sendMessageEdit(messageText, mentions); } else { await sendFiles(); diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/ContentMessage.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/ContentMessage.tsx index 3b8e0ca3a7f..273f11ae30a 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/ContentMessage.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/ContentMessage.tsx @@ -208,7 +208,7 @@ export const ContentMessageComponent = ({ {isEphemeralMessage && (
@@ -218,13 +218,13 @@ export const ContentMessageComponent = ({
- {quote && ( + {quote !== null && quote !== undefined && ( )} - {failedToSend && ( + {failedToSend !== null && failedToSend !== undefined && ( 0 && ( diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/Warnings/PartialFailureToSend/PartialFailureToSend.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/Warnings/PartialFailureToSend/PartialFailureToSend.tsx index 3c153de0a8c..9d4e1160e60 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/Warnings/PartialFailureToSend/PartialFailureToSend.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/Warnings/PartialFailureToSend/PartialFailureToSend.tsx @@ -54,7 +54,7 @@ function generateNamedUsers( return userClientsOrQualifiedIds.reduce( (parsedUsers, currentQulifiedId) => { const user = users.find(user => matchQualifiedIds(user.qualifiedId, currentQulifiedId)); - if (user && user.name()) { + if (user !== undefined && user.name() !== '') { parsedUsers.namedUsers.push(user); } else { parsedUsers.unknownUsers.push(currentQulifiedId); @@ -69,7 +69,7 @@ function generateNamedUsers( const domainNamedUsers = Object.keys(domainUsers).reduce( (domainNamedUsers, userId) => { const user = users.find(user => matchQualifiedIds(user.qualifiedId, {id: userId, domain})); - if (user && user.name()) { + if (user !== undefined && user.name() !== '') { domainNamedUsers.namedUsers.push(user); } else { domainNamedUsers.unknownUsers.push({id: userId, domain}); diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAsset.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAsset.tsx index abc34f1a266..beacf212198 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAsset.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAsset.tsx @@ -89,7 +89,7 @@ export const AudioAsset = ({ }; useEffect(() => { - if (audioSrc && audioElement) { + if (audioSrc !== undefined && audioElement !== undefined) { const playPromise = audioElement.play(); playPromise?.catch((error: unknown) => { @@ -129,18 +129,18 @@ export const AudioAsset = ({ isFocusable={isFocusable} /> - {transferState !== AssetTransferState.UPLOADING && audioElement && ( + {transferState !== AssetTransferState.UPLOADING && audioElement !== undefined && ( <> {formatSeconds(audioTime)} {showLoudnessPreview ? ( - + ) : ( )} diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAssetSeekBar/AudioSeekBarV2/useAudioSeekBar/useAudioSeekBar.ts b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAssetSeekBar/AudioSeekBarV2/useAudioSeekBar/useAudioSeekBar.ts index a0b90c3708b..14a789fd149 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAssetSeekBar/AudioSeekBarV2/useAudioSeekBar/useAudioSeekBar.ts +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioAssetSeekBar/AudioSeekBarV2/useAudioSeekBar/useAudioSeekBar.ts @@ -50,7 +50,7 @@ export const useAudioSeekBar = ({asset, audioElement, svgRef}: UseAudioSeekBarPr }, [svgRef]); const onTimeUpdate = useCallback(() => { - if (!audioElement?.duration) { + if (audioElement.duration === 0) { return; } @@ -66,7 +66,7 @@ export const useAudioSeekBar = ({asset, audioElement, svgRef}: UseAudioSeekBarPr const onLevelClick = useCallback( (event: MouseEvent) => { - if (!svgRef.current || !audioElement?.duration) { + if (svgRef.current === null || audioElement.duration === 0) { return; } @@ -81,7 +81,7 @@ export const useAudioSeekBar = ({asset, audioElement, svgRef}: UseAudioSeekBarPr ); const path = useMemo(() => { - if (!svgWidth) { + if (svgWidth === 0) { return ''; } @@ -106,7 +106,7 @@ export const useAudioSeekBar = ({asset, audioElement, svgRef}: UseAudioSeekBarPr useEffect(() => { const assetLoudness = asset.meta?.loudness; - if (!assetLoudness) { + if (assetLoudness === undefined) { return; } diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioSeekBar/AudioSeekBar.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioSeekBar/AudioSeekBar.tsx index 2421a6ed1d9..4816ce2e62d 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioSeekBar/AudioSeekBar.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/AudioAsset/AudioSeekBar/AudioSeekBar.tsx @@ -48,7 +48,7 @@ const AudioSeekBar = ({asset, audioElement, disabled}: AudioSeekBarProps) => { useEffect(() => { const loudness = asset.meta?.loudness; - if (loudness) { + if (loudness !== undefined) { setLoudness(Array.from(loudness).map(level => level / 256)); } }, [asset]); @@ -65,7 +65,7 @@ const AudioSeekBar = ({asset, audioElement, disabled}: AudioSeekBarProps) => { useEffect(() => updateSvgWidth(), [svgNode.current]); useEffect(() => { - if (!svgWidth) { + if (svgWidth === 0) { return setPath(''); } @@ -86,7 +86,7 @@ const AudioSeekBar = ({asset, audioElement, disabled}: AudioSeekBarProps) => { const updateSvgWidth = () => setSvgWidth(svgNode.current?.clientWidth ?? 0); const onLevelClick = (event: React.MouseEvent) => { - if (!svgNode.current) { + if (svgNode.current === null) { return; } @@ -100,7 +100,7 @@ const AudioSeekBar = ({asset, audioElement, disabled}: AudioSeekBarProps) => { const onAudioEnded = () => setPosition(0); const onTimeUpdate = () => { - if (audioElement.duration) { + if (audioElement.duration > 0) { setPosition(audioElement.currentTime / audioElement.duration); } }; diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/FileAsset/FileAsset.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/FileAsset/FileAsset.tsx index 7596cdeb29c..7093aa74d93 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/FileAsset/FileAsset.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/FileAsset/FileAsset.tsx @@ -64,7 +64,9 @@ const FileAsset = ({ // uploaded completely we have to check if there is upload progress to // transition into the `AssetTransferState.UPLOADING` state. const assetStatus = - uploadProgress && (uploadProgress > 0 && uploadProgress < 100 ? AssetTransferState.UPLOADING : transferState); + uploadProgress !== undefined && uploadProgress !== null && uploadProgress > 0 && uploadProgress < 100 + ? AssetTransferState.UPLOADING + : transferState; const isPendingUpload = assetStatus === AssetTransferState.UPLOAD_PENDING; const isFailedUpload = assetStatus === AssetTransferState.UPLOAD_FAILED; @@ -116,7 +118,7 @@ const FileAsset = ({ asset.cancelDownload()} /> )} - {isUploading && cancelUpload()} />} + {isUploading && cancelUpload()} />} {(isFailedUpload || isFailedDownloadingDecrypt || isFailedDownloadingHash) && (
@@ -131,7 +133,7 @@ const FileAsset = ({ {formattedFileSize} - {fileExtension &&
  • {fileExtension}
  • } + {fileExtension.length > 0 &&
  • {fileExtension}
  • } {isUploading &&
  • {t('conversationAssetUploading')}
  • } diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/VideoAssetCard/VideoAssetPlayer/useVideoPlayback/useVideoPlayback.ts b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/VideoAssetCard/VideoAssetPlayer/useVideoPlayback/useVideoPlayback.ts index e2b02392800..c78c2c6a917 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/VideoAssetCard/VideoAssetPlayer/useVideoPlayback/useVideoPlayback.ts +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/MultipartAssets/VideoAssetCard/VideoAssetPlayer/useVideoPlayback/useVideoPlayback.ts @@ -37,7 +37,7 @@ export const useVideoPlayback = ({url, videoElement, isEnabled}: UseVideoPlaybac const playabilityStatusRef = useRef('not-checked'); const handleTimeUpdate = useCallback(() => { - if (!videoElement) { + if (videoElement === undefined) { return; } setCurrentTime(videoElement.currentTime); @@ -50,7 +50,7 @@ export const useVideoPlayback = ({url, videoElement, isEnabled}: UseVideoPlaybac const playable = await isVideoPlayable(url); - if (!playable) { + if (playable === false) { const status = 'unplayable'; // Todo: This needs to be revisited @@ -68,7 +68,7 @@ export const useVideoPlayback = ({url, videoElement, isEnabled}: UseVideoPlaybac }, []); const play = useCallback(async () => { - if (!videoElement) { + if (videoElement === undefined) { return; } @@ -78,7 +78,7 @@ export const useVideoPlayback = ({url, videoElement, isEnabled}: UseVideoPlaybac }, [videoElement]); const handlePlay = useCallback(async (): Promise => { - if (!isEnabled || url === undefined || url === '' || !videoElement) { + if (isEnabled === false || url === undefined || url === '' || videoElement === undefined) { return; } diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/TextMessageRenderer/TextMessageRenderer.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/TextMessageRenderer/TextMessageRenderer.tsx index 9597fc41335..faa7762c2ea 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/TextMessageRenderer/TextMessageRenderer.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/TextMessageRenderer/TextMessageRenderer.tsx @@ -59,11 +59,11 @@ const TextMessage: FC = ({ useEffect(() => { const element = containerRef.current; - if (element && collapse) { + if (element !== null && collapse) { const preNode = element.querySelector('pre'); - const collapsedHeight = collapsedHeightRef.current || element.clientHeight; - const width = Math.max(element.scrollWidth, preNode ? preNode.scrollWidth : 0); - const height = Math.max(element.scrollHeight, preNode ? preNode.scrollHeight : 0); + const collapsedHeight = collapsedHeightRef.current > 0 ? collapsedHeightRef.current : element.clientHeight; + const width = Math.max(element.scrollWidth, preNode !== null ? preNode.scrollWidth : 0); + const height = Math.max(element.scrollHeight, preNode !== null ? preNode.scrollHeight : 0); const isWider = width > element.clientWidth; const isHigher = height > collapsedHeight; collapsedHeightRef.current = collapsedHeight; @@ -74,7 +74,7 @@ const TextMessage: FC = ({ useEffect(() => { const element = containerRef.current; - if (!element) { + if (element === null) { return; } @@ -112,19 +112,19 @@ const TextMessage: FC = ({ const markdownLinkElement = target.closest('[data-md-link]'); const mentionElement = target.closest('.message-mention'); - if (markdownLinkElement) { + if (markdownLinkElement !== null) { const href = (markdownLinkElement as HTMLAnchorElement).href; const markdownLinkDetails = { href: href, }; forwardEvent(event.nativeEvent, 'markdownLink', markdownLinkDetails); - } else if (emailElement) { + } else if (emailElement !== null) { const href = (emailElement as HTMLAnchorElement).href; const markdownLinkDetails = { href: href, }; forwardEvent(event.nativeEvent, 'email', markdownLinkDetails); - } else if (mentionElement) { + } else if (mentionElement !== null) { const mentionMsgDetails = { userId: target.dataset.userId, userDomain: target.dataset.userDomain, diff --git a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset/VideoAsset.tsx b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset/VideoAsset.tsx index 9cc13ddce6b..2c137652205 100644 --- a/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset/VideoAsset.tsx +++ b/apps/webapp/src/script/components/MessagesList/Message/ContentMessage/asset/VideoAsset/VideoAsset.tsx @@ -124,7 +124,7 @@ const VideoAsset = ({ if (isFileSharingReceivingEnabled) { setDisplaySmall(false); - if (videoSrc && videoElement) { + if (videoSrc !== undefined && videoElement !== undefined) { videoElement.play(); } else { asset.status(AssetTransferState.DOWNLOADING); @@ -166,14 +166,14 @@ const VideoAsset = ({ }; const onVideoPlaying = (): void => { - if (!videoElement) { + if (videoElement === undefined) { return; } videoElement.style.backgroundColor = '#000'; }; useEffect(() => { - if (videoSrc && videoElement) { + if (videoSrc !== undefined && videoElement !== undefined) { const playPromise = videoElement.play(); playPromise?.catch((error: unknown) => { @@ -183,7 +183,7 @@ const VideoAsset = ({ }, [videoElement, videoSrc]); const syncVideoTimeRest = () => { - if (videoElement) { + if (videoElement !== undefined) { setVideoTimeRest(videoElement.duration - videoElement.currentTime); } }; @@ -225,7 +225,7 @@ const VideoAsset = ({ onTimeUpdate={syncVideoTimeRest} onLoadedMetadata={syncVideoTimeRest} className={cx({hidden: isUploading})} - style={{backgroundColor: videoPreview ? '#000' : ''}} + style={{backgroundColor: videoPreview !== undefined ? '#000' : ''}} tabIndex={TabIndex.UNFOCUSABLE} /> {videoPlaybackError ? ( @@ -259,7 +259,7 @@ const VideoAsset = ({ />
    - {isVideoLoaded && videoElement && ( + {isVideoLoaded && videoElement !== undefined && (
    { - if (mediaElement) { + if (mediaElement !== undefined) { mediaElement.addEventListener('playing', onPlay); mediaElement.addEventListener('pause', onPause); } return () => { - if (mediaElement) { + if (mediaElement !== undefined) { mediaElement.removeEventListener('playing', onPlay); mediaElement.removeEventListener('pause', onPause); } @@ -82,7 +82,7 @@ const MediaButton = ({ 'media-button-lg': large, })} > - {isUploaded && !isPlaying && mediaElement && ( + {isUploaded && !isPlaying && mediaElement !== undefined && (
    , - container || document.body, + container ?? document.body, )} ); diff --git a/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx b/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx index fa712dba49b..a838f85401b 100644 --- a/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx +++ b/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModal.tsx @@ -113,7 +113,7 @@ export const PrimaryModalComponent: FC = () => { }; const isPasswordOptional = () => { - const skipValidation = passwordOptional && !passwordInput.trim().length; + const skipValidation = passwordOptional && passwordInput.trim().length === 0; if (skipValidation) { return true; } @@ -131,12 +131,12 @@ export const PrimaryModalComponent: FC = () => { ValidationUtil.getNewPasswordPattern(Config.getConfig().NEW_PASSWORD_MINIMUM_LENGTH), ); const actionEnabled = isPasswordRequired ? isPasswordOptional() : true; - const inputActionEnabled = !isInput || !!inputValue.trim().length; + const inputActionEnabled = !isInput || inputValue.trim().length > 0; const areGuestLinkPasswordsValid = checkGuestLinkPassword(passwordValue, passwordConfirmationValue); const passwordGuestLinkActionEnabled = - (!isGuestLinkPassword || !!passwordValue.trim().length) && areGuestLinkPasswordsValid; + (!isGuestLinkPassword || passwordValue.trim().length > 0) && areGuestLinkPasswordsValid; const isPrimaryActionDisabled = (disabled: boolean | undefined) => { if (disabled === true) { @@ -181,7 +181,7 @@ export const PrimaryModalComponent: FC = () => { const confirm = () => { const action = content?.primaryAction?.action; - if (!action) { + if (action === undefined) { return; } const actions = { @@ -202,7 +202,7 @@ export const PrimaryModalComponent: FC = () => { const onOptionChange = (event: ChangeEvent) => { updateOptionChecked(event.target.checked); - if (primaryActionButtonRef.current) { + if (primaryActionButtonRef.current !== null) { primaryActionButtonRef.current.focus(); } }; @@ -228,9 +228,9 @@ export const PrimaryModalComponent: FC = () => { const targetElement = primaryBtnFirst ? primaryActionButtonRef.current : closeButtonRef.current; const fallbackElement = primaryBtnFirst ? closeButtonRef.current : primaryActionButtonRef.current; - if (targetElement) { + if (targetElement !== null) { targetElement.focus(); - } else if (fallbackElement) { + } else if (fallbackElement !== null) { fallbackElement.focus(); } }, 0); @@ -245,9 +245,14 @@ export const PrimaryModalComponent: FC = () => { closeAction(); } - if (isEnterKey(event) && primaryAction?.runActionOnEnterClick) { + if ( + isEnterKey(event) && + primaryAction !== undefined && + primaryAction !== null && + primaryAction.runActionOnEnterClick === true + ) { event.preventDefault(); - primaryAction?.action?.(); + primaryAction.action?.(); removeCurrentModal(); } }, diff --git a/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx b/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx index b4a53339a4b..d6ce985113b 100644 --- a/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx +++ b/apps/webapp/src/script/components/Modals/PrimaryModal/PrimaryModalShell/PrimaryModalShell.tsx @@ -52,18 +52,18 @@ export const PrimaryModalShell = ({ // Make detached window background inert when modal is shown useEffect(() => { - if (!container) { + if (container === undefined) { return undefined; } // Safety check const element = container instanceof HTMLElement ? container : null; - if (!element?.querySelector) { + if (element === null || element.querySelector === undefined) { return undefined; } const detachedWindowRoot = element.querySelector('#detached-window'); - if (!detachedWindowRoot || !(detachedWindowRoot instanceof HTMLElement)) { + if (detachedWindowRoot === null || !(detachedWindowRoot instanceof HTMLElement)) { return undefined; } diff --git a/apps/webapp/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx b/apps/webapp/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx index 7c494885f5b..90de506e8f1 100644 --- a/apps/webapp/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx +++ b/apps/webapp/src/script/components/Modals/QualityFeedbackModal/QualityFeedbackModal.tsx @@ -55,7 +55,7 @@ interface Props { export const QualityFeedbackModal = ({callingRepository}: Props) => { const userState = container.resolve(UserState); const {conversationId} = useCallAlertState(); - const call = conversationId && callingRepository.findCall(conversationId); + const call = conversationId !== undefined ? callingRepository.findCall(conversationId) : undefined; const [isChecked, setIsChecked] = useState(false); const {setQualityFeedbackModalShown, qualityFeedbackModalShown, setConversationId} = useCallAlertState(); const {self: selfUser} = useKoSubscribableChildren(userState, ['self']); @@ -68,7 +68,7 @@ export const QualityFeedbackModal = ({callingRepository}: Props) => { return null; } - if (!call) { + if (call === undefined) { logger.warn('Call not found for conversationId', conversationId); setQualityFeedbackModalShown(false); return null; @@ -117,7 +117,7 @@ export const QualityFeedbackModal = ({callingRepository}: Props) => {
      {ratingListItems.map(ratingItem => (
    • - {ratingItem?.headingTranslationKey && ( + {ratingItem.headingTranslationKey !== undefined && ( // headingTranslationKey has to broad type to specify it // TODO: narrow down the type
      {t(ratingItem.headingTranslationKey)}
      diff --git a/apps/webapp/src/script/components/Modals/UserModal/UserModal.tsx b/apps/webapp/src/script/components/Modals/UserModal/UserModal.tsx index 92c45c4733e..4c0a0a4aab1 100644 --- a/apps/webapp/src/script/components/Modals/UserModal/UserModal.tsx +++ b/apps/webapp/src/script/components/Modals/UserModal/UserModal.tsx @@ -106,7 +106,9 @@ export const UnverifiedUserWarning = ({user}: UnverifiedUserWarningProps) => {

      - {user ? t('userNotVerified', {user: user.name()}) : t('conversationConnectionVerificationWarning')} + {user !== undefined + ? t('userNotVerified', {user: user.name()}) + : t('conversationConnectionVerificationWarning')} { - if (userId) { + if (userId !== null) { userRepository // We want to get the fresh version of the user from backend (in case the user was deleted) .refreshUser(userId) @@ -185,7 +187,7 @@ const UserModal = ({ onClosed={onModalClosed} className="user-modal" css={userModalStyle} - data-uie-name={user ? 'modal-user-profile' : userNotFound ? 'modal-cannot-open-profile' : ''} + data-uie-name={user !== null ? 'modal-user-profile' : userNotFound ? 'modal-cannot-open-profile' : ''} wrapperCSS={userModalWrapperStyle} >

      @@ -211,9 +213,9 @@ const UserModal = ({
      - {user && ( + {user !== null && ( <> @@ -233,7 +235,7 @@ const UserModal = ({ /> )} - {isShown && !user && !userNotFound && ( + {isShown && user === null && !userNotFound && (
      diff --git a/apps/webapp/src/script/components/TitleBar/TitleBar.tsx b/apps/webapp/src/script/components/TitleBar/TitleBar.tsx index bea7ad0c5c8..8d075544734 100644 --- a/apps/webapp/src/script/components/TitleBar/TitleBar.tsx +++ b/apps/webapp/src/script/components/TitleBar/TitleBar.tsx @@ -138,7 +138,7 @@ export const TitleBar = ({ hasService: hasService || hasApps, }); - if (translationKey) { + if (translationKey !== '') { return t(translationKey); } @@ -146,7 +146,7 @@ export const TitleBar = ({ }, [hasDirectGuest, hasExternal, hasFederatedUsers, hasService, hasApps, is1to1, isRequest]); const hasCall = useMemo(() => { - const hasEntities = !!joinedCall; + const hasEntities = joinedCall !== undefined; return hasEntities && matchQualifiedIds(conversation.qualifiedId, joinedCall.conversation.qualifiedId); }, [conversation, joinedCall]); @@ -243,7 +243,7 @@ export const TitleBar = ({ }; useEffect(() => { - if (!activeCalls.length && currentFocusedElementRef.current) { + if (activeCalls.length === 0 && currentFocusedElementRef.current !== null) { currentFocusedElementRef.current.focus(); currentFocusedElementRef.current = null; } @@ -332,7 +332,9 @@ export const TitleBar = ({
      - {conversationSubtitle &&
      {conversationSubtitle}
      } + {conversationSubtitle.length > 0 && ( +
      {conversationSubtitle}
      + )}
    @@ -397,7 +399,7 @@ export const TitleBar = ({ )} - {badgeLabelCopy && ( + {badgeLabelCopy.length > 0 && (
  • ([UserListSections.CONTACTS]); + const sanitizedSelectedUsers = selectedUsers.filter((selectedUser): selectedUser is User => { + return selectedUser !== undefined && selectedUser !== null; + }); const hasMoreUsers = !truncate && filteredUsers.length > maxShownUsers; @@ -131,7 +134,7 @@ export const UserList = ({ const renderListItem = useCallback( (user: User, isLastItem: boolean = false) => { const isSelected = (userEntity: User): boolean => - isSelectable && selectedUsers.some(user => user.id === userEntity.id); + isSelectable && sanitizedSelectedUsers.some(user => user.id === userEntity.id); return (
  • @@ -154,14 +157,14 @@ export const UserList = ({
  • ); }, - [highlightedUserIds, isSelectable, isSelfVerified, mode, noSelfInteraction, selectedUsers, teamState], + [highlightedUserIds, isSelectable, isSelfVerified, mode, noSelfInteraction, sanitizedSelectedUsers, teamState], ); const adminsHeaderId = useId(); const membersHeaderId = useId(); let content; - const showRoles = !!conversation; + const showRoles = conversation !== undefined && conversation !== null; if (showRoles) { let members: User[] = []; let admins: User[] = []; @@ -232,9 +235,9 @@ export const UserList = ({ } else { const truncatedUsers = truncate ? filteredUsers.slice(0, reducedUserCount) : filteredUsers; const isSelected = (userEntity: User): boolean => - isSelectable && !!selectedUsers?.some(user => user.id === userEntity.id); + isSelectable && sanitizedSelectedUsers.some(user => user.id === userEntity.id); - const selectedUsersCount = selectedUsers.length; + const selectedUsersCount = sanitizedSelectedUsers.length; const hasSelectedUsers = selectedUsersCount > 0; const toggleFolder = (folderName: UserListSections) => { @@ -268,7 +271,7 @@ export const UserList = ({ className={cx('search-list', cssClasses)} > {isSelectedContactsOpen && - selectedUsers.map((user, index) => { + sanitizedSelectedUsers.map((user, index) => { const isLastItem = index === selectedUsersCount - 1; return renderListItem(user, isLastItem); diff --git a/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx b/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx index 2ee36d7e7ef..d404fb9b30d 100644 --- a/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx +++ b/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx @@ -19,6 +19,7 @@ import React, {useEffect, useState} from 'react'; +import is from '@sindresorhus/is'; import {QualifiedId, UserType} from '@wireapp/api-client/lib/user'; import {container} from 'tsyringe'; import {useDebouncedCallback} from 'use-debounce'; @@ -74,7 +75,11 @@ export const UserSearchableList = ({ const [filteredUsers, setFilteredUsers] = useState([]); const [remoteTeamMembers, setRemoteTeamMembers] = useState([]); - const filteredSelectedUsers = selectedUsers ? searchRepository.searchUserInSet(filter, selectedUsers) : undefined; + const sanitizedSelectedUsers = + selectedUsers?.filter((user): user is User => { + return !is.nullOrUndefined(user); + }) ?? []; + const filteredSelectedUsers = searchRepository.searchUserInSet(filter, sanitizedSelectedUsers); const selfInTeam = teamState.isInTeam(selfUser); @@ -131,7 +136,7 @@ export const UserSearchableList = ({ }, [filter, users.length]); const foundUserEntities = () => { - if (!remoteTeamMembers.length) { + if (remoteTeamMembers.length === 0) { return filteredUsers; } const {query: normalizedQuery} = searchRepository.normalizeQuery(filter); @@ -140,15 +145,16 @@ export const UserSearchableList = ({ ); }; - const toggleUserSelection = selectedUsers - ? (user: User) => { - if (selectedUsers.find(selectedUser => selectedUser.id === user.id)) { - onUpdateSelectedUsers?.([...selectedUsers].filter(selectedUser => selectedUser.id !== user.id)); - } else { - onUpdateSelectedUsers?.([...selectedUsers, user]); + const toggleUserSelection = + selectedUsers !== undefined + ? (user: User) => { + if (sanitizedSelectedUsers.find(selectedUser => selectedUser.id === user.id) !== undefined) { + onUpdateSelectedUsers?.([...sanitizedSelectedUsers].filter(selectedUser => selectedUser.id !== user.id)); + } else { + onUpdateSelectedUsers?.([...sanitizedSelectedUsers, user]); + } } - } - : undefined; + : undefined; const userList = foundUserEntities().filter( user => diff --git a/apps/webapp/src/script/components/ZoomableImage/ZoomableImage.tsx b/apps/webapp/src/script/components/ZoomableImage/ZoomableImage.tsx index c8fc817e24c..f5eacf263a5 100644 --- a/apps/webapp/src/script/components/ZoomableImage/ZoomableImage.tsx +++ b/apps/webapp/src/script/components/ZoomableImage/ZoomableImage.tsx @@ -35,7 +35,7 @@ const DEFAULT_OFFSET: Offset = {x: 0, y: 0}; function calculateZoomRatio(element: HTMLImageElement) { const parentElement = element.parentElement; - if (!parentElement) { + if (parentElement === null) { return 1; } @@ -49,7 +49,7 @@ function calculateZoomRatio(element: HTMLImageElement) { // if we will add more image zooming, we need to pass 2 props, for check if is image is zoomed and imageScale function calculateMaxOffset(containerRef: RefObject, imgRef: RefObject) { - if (!containerRef.current || !imgRef.current) { + if (containerRef.current === null || imgRef.current === null) { return { maxXOffset: 0, maxYOffset: 0, @@ -120,7 +120,7 @@ export const ZoomableImage = (props: ZoomableImageProps) => { }); if (!draggingRef.current && !isZoomEnabled) { - if (!imageRef.current) { + if (imageRef.current === null) { return; } @@ -186,7 +186,7 @@ export const ZoomableImage = (props: ZoomableImageProps) => { }; const handleMouseMove = (event: React.MouseEvent) => { - if (!isZoomEnabled || !mouseDownRef.current || !containerRef.current || !imageRef.current) { + if (!isZoomEnabled || !mouseDownRef.current || containerRef.current === null || imageRef.current === null) { return; } @@ -198,7 +198,7 @@ export const ZoomableImage = (props: ZoomableImageProps) => { draggingRef.current = true; - if (element.style.transition) { + if (element.style.transition.length > 0) { element.style.transition = ''; } @@ -217,7 +217,7 @@ export const ZoomableImage = (props: ZoomableImageProps) => { }; const updateZoomRatio = () => { - if (!imageRef.current) { + if (imageRef.current === null) { return; } diff --git a/apps/webapp/src/script/components/calling/CallingOverlayContainer.tsx b/apps/webapp/src/script/components/calling/CallingOverlayContainer.tsx index e2f78aa9b2b..4797aaa5a90 100644 --- a/apps/webapp/src/script/components/calling/CallingOverlayContainer.tsx +++ b/apps/webapp/src/script/components/calling/CallingOverlayContainer.tsx @@ -97,7 +97,7 @@ const CallingContainer = ({ const setActiveCallViewTab = (tab: CallViewTab) => { callState.activeCallViewTab(tab); - if (tab === CallViewTab.ALL && joinedCall) { + if (tab === CallViewTab.ALL && joinedCall !== null && joinedCall !== undefined) { callingRepository.requestCurrentPageVideoStreams(joinedCall); } }; @@ -137,7 +137,12 @@ const CallingContainer = ({ const conversation = joinedCall?.conversation; - if (!joinedCall || !conversation || conversation.isSelfUserRemoved()) { + if ( + joinedCall === null || + joinedCall === undefined || + conversation === undefined || + conversation.isSelfUserRemoved() + ) { return null; } @@ -150,7 +155,7 @@ const CallingContainer = ({ return ( - {isFullScreenOrDetached && !!videoGrid?.grid.length && ( + {isFullScreenOrDetached && (videoGrid?.grid.length ?? 0) > 0 && ( { const dialog = dialogRef.current; - if (!dialog) { + if (dialog === null) { return undefined; } @@ -70,8 +70,8 @@ function ChooseScreen({choose, callState = container.resolve(CallState)}: Choose const isDetachedWindow = callState.viewMode() === CallingViewMode.DETACHED_WINDOW; const focusContext = captureModalFocusContext({ - targetDocument: isDetachedWindow && detachedWindow ? detachedWindow.document : undefined, - container: isDetachedWindow && detachedWindow ? detachedWindow.document.body : undefined, + targetDocument: isDetachedWindow && detachedWindow !== null ? detachedWindow.document : undefined, + container: isDetachedWindow && detachedWindow !== null ? detachedWindow.document.body : undefined, }); restoreFocusRef.current = focusContext.createFocusRestorationCallback(); @@ -114,7 +114,7 @@ function ChooseScreen({choose, callState = container.resolve(CallState)}: Choose // Prevent focus from getting out of the dialog const handleFocusOut = (event: FocusEvent) => { const relatedTarget = event.relatedTarget as HTMLElement | null; - if (!relatedTarget || !dialog.contains(relatedTarget)) { + if (relatedTarget === null || !dialog.contains(relatedTarget)) { event.preventDefault(); const elements = getFocusableElements(); elements.at(0)?.focus(); diff --git a/apps/webapp/src/script/components/calling/FullscreenVideoCall.tsx b/apps/webapp/src/script/components/calling/FullscreenVideoCall.tsx index 8ee52db8482..7173bf63a3f 100644 --- a/apps/webapp/src/script/components/calling/FullscreenVideoCall.tsx +++ b/apps/webapp/src/script/components/calling/FullscreenVideoCall.tsx @@ -235,7 +235,7 @@ const FullscreenVideoCall = ({ } const targetDocument = - viewMode === CallingViewMode.DETACHED_WINDOW && detachedWindow ? detachedWindow.document : document; + viewMode === CallingViewMode.DETACHED_WINDOW && detachedWindow !== null ? detachedWindow.document : document; const onKeyDown = (event: KeyboardEvent): void => { const target = event.target as HTMLElement; @@ -247,7 +247,7 @@ const FullscreenVideoCall = ({ // Allow focus to move into the ChooseScreen dialog if it's open const chooseScreenDialog = targetDocument.querySelector('.choose-screen[role="dialog"]'); - if (chooseScreenDialog) { + if (chooseScreenDialog !== null) { return; } @@ -279,9 +279,9 @@ const FullscreenVideoCall = ({ }); const isMobile = useActiveWindowMatchMedia(QUERY.mobile); - const isPaginationVisible = !maximizedParticipant && activeCallViewTab === CallViewTab.ALL && totalPages > 1; + const isPaginationVisible = maximizedParticipant === null && activeCallViewTab === CallViewTab.ALL && totalPages > 1; - const isModerator = selfUser && roles[selfUser.id] === DefaultConversationRoleName.WIRE_ADMIN; + const isModerator = selfUser !== undefined && roles[selfUser.id] === DefaultConversationRoleName.WIRE_ADMIN; const backgroundEffectsHandler = callingRepository.getBackgroundEffectsHandler(); const selectedBackgroundEffect = useBackgroundEffectsStore(state => state.preferredEffect); @@ -390,7 +390,7 @@ const FullscreenVideoCall = ({ call={call} setMaximizedParticipant={participant => setMaximizedParticipant(call, participant)} /> - {classifiedDomains && ( + {classifiedDomains !== null && classifiedDomains !== undefined && ( {isConfirmCloseModalOpen && ( diff --git a/apps/webapp/src/script/components/calling/GroupVideoGrid.tsx b/apps/webapp/src/script/components/calling/GroupVideoGrid.tsx index b91a6a01571..1a1f3a34e59 100644 --- a/apps/webapp/src/script/components/calling/GroupVideoGrid.tsx +++ b/apps/webapp/src/script/components/calling/GroupVideoGrid.tsx @@ -92,7 +92,7 @@ const calculateRowsAndColumns = (params: CalculateRowsAndColumsParams): RowsAndC const {totalCount} = params; const desiredColumns = getDesiredColumns(params); const columns = Math.min(totalCount, desiredColumns); - const rows = totalCount ? Math.ceil(totalCount / columns) : 1; + const rows = totalCount > 0 ? Math.ceil(totalCount / columns) : 1; return {'--columns': columns, '--rows': rows}; }; @@ -168,11 +168,11 @@ const GroupVideoGrid = ({ return; } - const participant = grid.grid.find(participant => participant?.doesMatchIds(userId, clientId)) || null; + const participant = grid.grid.find(participant => participant?.doesMatchIds(userId, clientId)) ?? null; setMaximizedParticipant(participant); }; - const participants = (maximizedParticipant ? [maximizedParticipant] : grid.grid).filter(Boolean); + const participants = (maximizedParticipant !== null ? [maximizedParticipant] : grid.grid).filter(Boolean); useEffect(() => { setRowsAndColumns( @@ -194,7 +194,7 @@ const GroupVideoGrid = ({ }) => { if (isShort) { // Special case: use different layout for 2 participants when in short mode - if (grid.thumbnail && limits.WITH_THUMBNAIL != null) { + if (grid.thumbnail !== null && grid.thumbnail !== undefined && limits.WITH_THUMBNAIL != null) { return call.setNumberOfParticipantsInOnePage(limits.WITH_THUMBNAIL); } return call.setNumberOfParticipantsInOnePage(limits.SHORT); @@ -267,7 +267,7 @@ const GroupVideoGrid = ({ key={participant.clientId} selfParticipant={selfParticipant} participantCount={participants.length} - isMaximized={!!maximizedParticipant} + isMaximized={maximizedParticipant !== null} onTileDoubleClick={doubleClickedOnVideo} /> ))} @@ -301,7 +301,7 @@ const GroupVideoGrid = ({ )} )} - {grid.thumbnail && !thumbnail.hasActiveVideo && ( + {grid.thumbnail !== null && grid.thumbnail !== undefined && !thumbnail.hasActiveVideo && (
    { const connectionData = await actionsViewModel.sendConnectionRequest(user); - if (!connectionData) { + if (connectionData === undefined || connectionData === null) { // Sending the connection failed, there is nothing more to do return; } @@ -287,7 +287,7 @@ const UserActions = ({ : await actionsViewModel.getConversationById(conversationId); const savedConversation = await actionsViewModel.saveConversation(connectionConversation); - if (!conversation) { + if (conversation === undefined) { // Only open the new conversation if we aren't currently in a conversation context await actionsViewModel.open1to1Conversation(savedConversation); } @@ -319,7 +319,7 @@ const UserActions = ({ ? { click: async () => { await actionsViewModel.unblockUser(user); - await create1to1Conversation(user, !conversation); + await create1to1Conversation(user, conversation === undefined); onAction(Actions.UNBLOCK); }, Icon: Icon.BlockIcon, @@ -330,7 +330,7 @@ const UserActions = ({ const removeUserFromConversation: MenuItem | undefined = isNotMe && - conversation && + conversation !== undefined && !conversation.isSelfUserRemoved() && conversation.participating_user_ids().some(userId => matchQualifiedIds(userId, user)) && conversationRoleRepository?.canRemoveParticipants(conversation) === true @@ -357,7 +357,7 @@ const UserActions = ({ blockUser, unblockUser, removeUserFromConversation, - ].filter((item): item is MenuItem => !!item); + ].filter((item): item is MenuItem => item !== undefined); return items.length === 1 && isModal ? ( { - if (this.context) { + if (this.context !== undefined) { await this.logout(); } @@ -373,7 +374,7 @@ export class APIClient extends EventEmitter { } public async register(userAccount: RegisterData, clientType: ClientType = ClientType.PERMANENT): Promise { - if (this.context) { + if (this.context !== undefined) { await this.logout(); } @@ -412,16 +413,17 @@ export class APIClient extends EventEmitter { this.logger.warn('Could not get self user', (error as BackendError).message); } - this.context = this.context - ? {...this.context, clientType, domain: selfDomain} - : {clientType, userId, domain: selfDomain}; + this.context = + this.context !== undefined + ? {...this.context, clientType, domain: selfDomain} + : {clientType, userId, domain: selfDomain}; return this.context; } public disconnect(reason?: string): void { this.transport.ws.disconnect(reason); // Remove the cookie refresh listener to prevent memory leaks - if (this.cookieRefreshListener) { + if (this.cookieRefreshListener !== undefined) { CookieStore.emitter.off(CookieStore.TOPIC.COOKIE_REFRESH, this.cookieRefreshListener); } } @@ -440,16 +442,18 @@ export class APIClient extends EventEmitter { /** Should be used in cases where the user ID is MANDATORY. */ public get validatedUserId(): string { - if (this.userId !== undefined && this.userId.length > 0) { - return this.userId; + const userId = this.userId; + if (is.nonEmptyString(userId)) { + return userId; } throw new Error('No valid user ID.'); } /** Should be used in cases where the client ID is MANDATORY. */ public get validatedClientId(): string { - if (this.clientId !== undefined && this.clientId.length > 0) { - return this.clientId; + const clientId = this.clientId; + if (is.nonEmptyString(clientId)) { + return clientId; } throw new Error('No valid client ID.'); } diff --git a/libraries/api-client/src/asset/assetApi.ts b/libraries/api-client/src/asset/assetApi.ts index 313d64c2855..017f43d48bb 100644 --- a/libraries/api-client/src/asset/assetApi.ts +++ b/libraries/api-client/src/asset/assetApi.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import axios, {AxiosHeaderValue, AxiosRequestConfig} from 'axios'; import logdown from 'logdown'; @@ -104,7 +105,7 @@ export class AssetAPI { config.params.asset_token = token; } - if (forceCaching) { + if (forceCaching === true) { config.params.forceCaching = forceCaching; } @@ -150,7 +151,7 @@ export class AssetAPI { domain: options?.domain, }; - if (options?.auditData) { + if (options?.auditData !== undefined) { metadataObject.convId = options.auditData.conversationId; metadataObject.filename = options.auditData.filename; metadataObject.filetype = options.auditData.filetype; @@ -221,8 +222,9 @@ export class AssetAPI { throw new TypeError(`Expected asset ID "${assetId}" to only contain alphanumeric values and dashes.`); } - const isValidDomain = (domain: string) => - !!domain && /^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,}$/.test(domain); + const isValidDomain = (domain: string): boolean => { + return is.nonEmptyString(domain) && /^([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,}$/.test(domain); + }; if (!isValidDomain(assetDomain)) { throw new TypeError(`Invalid asset domain ${assetDomain}`); diff --git a/libraries/api-client/src/auth/authApi.ts b/libraries/api-client/src/auth/authApi.ts index e34b1378b59..936e3659e94 100644 --- a/libraries/api-client/src/auth/authApi.ts +++ b/libraries/api-client/src/auth/authApi.ts @@ -52,7 +52,7 @@ export class AuthAPI { url: AuthAPI.URL.COOKIES, }; - if (labels) { + if (labels !== undefined) { config.params.labels = labels.join(','); } diff --git a/libraries/api-client/src/broadcast/broadcastApi.ts b/libraries/api-client/src/broadcast/broadcastApi.ts index cb522ee3852..b820f0d432f 100644 --- a/libraries/api-client/src/broadcast/broadcastApi.ts +++ b/libraries/api-client/src/broadcast/broadcastApi.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import {proteus as ProtobufOTR} from '@wireapp/protocol-messaging/web/otr'; import {AxiosRequestConfig} from 'axios'; @@ -42,7 +43,7 @@ export class BroadcastAPI { sendingClientId: string, messageData: ProtobufOTR.QualifiedNewOtrMessage, ): Promise { - if (!sendingClientId) { + if (!is.nonEmptyString(sendingClientId)) { throw new ValidationError('Unable to send OTR message without client ID.'); } diff --git a/libraries/api-client/src/cells/cellsApi.ts b/libraries/api-client/src/cells/cellsApi.ts index 4c6bd32ad58..3c4de966cbd 100644 --- a/libraries/api-client/src/cells/cellsApi.ts +++ b/libraries/api-client/src/cells/cellsApi.ts @@ -99,10 +99,10 @@ export class CellsAPI { httpClient?: HttpClient; storageService?: CellsStorage; }) { - const http = httpClient || this.getHttpClient({cellsConfig}); + const http = httpClient ?? this.getHttpClient({cellsConfig}); this.storageService = - storageService || + storageService ?? new S3Service({ config: cellsConfig.s3, accessTokenStore: this.accessTokenStore, @@ -110,6 +110,22 @@ export class CellsAPI { this.client = new NodeServiceApi(undefined, undefined, http.client); } + private getInitializedClient(): NodeServiceApi { + if (this.client === null) { + throw new Error(CONFIGURATION_ERROR); + } + + return this.client; + } + + private getInitializedStorageService(): CellsStorage { + if (this.storageService === null) { + throw new Error(CONFIGURATION_ERROR); + } + + return this.storageService; + } + private getHttpClient({cellsConfig}: {cellsConfig: CellsConfig}) { const baseHttpClientConfig = { ...this.httpClientConfig, @@ -161,13 +177,11 @@ export class CellsAPI { progressCallback?: (progress: number) => void; abortController?: AbortController; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); let filePath = `${path}`.normalize('NFC'); - const result = await this.client.createCheck( + const result = await client.createCheck( { Inputs: [{Type: 'LEAF', Locator: {Path: filePath, Uuid: uuid}, VersionId: versionId}], FindAvailablePath: true, @@ -187,7 +201,8 @@ export class CellsAPI { 'Create-Version-Id': versionId, }; - await this.storageService.putObject({path: filePath, file, metadata, progressCallback, abortController}); + const storageService = this.getInitializedStorageService(); + await storageService.putObject({path: filePath, file, metadata, progressCallback, abortController}); return result.data; } @@ -203,11 +218,9 @@ export class CellsAPI { versionId: string; type: RestIncomingNode['Type']; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.createCheck({ + const result = await client.createCheck({ Inputs: [{Type: type, Locator: {Path: path.normalize('NFC'), Uuid: uuid}, VersionId: versionId}], FindAvailablePath: false, }); @@ -216,21 +229,17 @@ export class CellsAPI { } async promoteNodeDraft({uuid, versionId}: {uuid: string; versionId: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.promoteVersion(uuid, versionId, {Publish: true}); + const result = await client.promoteVersion(uuid, versionId, {Publish: true}); return result.data; } async deleteNodeDraft({uuid, versionId}: {uuid: string; versionId: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.deleteVersion(uuid, versionId); + const result = await client.deleteVersion(uuid, versionId); return result.data; } @@ -242,11 +251,9 @@ export class CellsAPI { uuid: string; permanently?: boolean; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.performAction('delete', { + const result = await client.performAction('delete', { Nodes: [{Uuid: uuid}], DeleteOptions: {PermanentDelete: permanently}, }); @@ -261,11 +268,9 @@ export class CellsAPI { currentPath: RestNodeLocator['Path']; targetPath: RestActionOptionsCopyMove['TargetPath']; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.performAction('move', { + const result = await client.performAction('move', { Nodes: [{Path: currentPath}], CopyMoveOptions: {TargetIsParent: true, TargetPath: targetPath}, AwaitStatus: 'Finished', @@ -276,24 +281,20 @@ export class CellsAPI { } async restoreNode({uuid}: {uuid: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.performAction('restore', {Nodes: [{Uuid: uuid}]}); + const result = await client.performAction('restore', {Nodes: [{Uuid: uuid}]}); return result.data; } async renameNode({currentPath, newName}: {currentPath: string; newName: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); const basePath = currentPath.split('/').slice(0, -1).join('/'); const newPath = `${basePath}/${newName}`; - const result = await this.client.performAction('move', { + const result = await client.performAction('move', { Nodes: [{Path: currentPath}], CopyMoveOptions: {TargetIsParent: false, TargetPath: newPath}, AwaitStatus: 'Finished', @@ -304,18 +305,16 @@ export class CellsAPI { } async lookupNodeByPath({path}: {path: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.lookup({ + const result = await client.lookup({ Scope: {Nodes: [{Path: path}]}, Flags: ['WithPreSignedURLs'], }); const node = result.data.Nodes?.[0]; - if (!node) { + if (node === undefined) { throw new Error(`File not found: ${path}`); } @@ -323,18 +322,16 @@ export class CellsAPI { } async lookupNodeByUuid({uuid}: {uuid: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.lookup({ + const result = await client.lookup({ Scope: {Nodes: [{Uuid: uuid}]}, Flags: ['WithPreSignedURLs'], }); const node = result.data.Nodes?.[0]; - if (!node) { + if (node === undefined) { throw new Error(`File not found: ${uuid}`); } @@ -342,11 +339,9 @@ export class CellsAPI { } async getNodeVersions({uuid, flags}: {uuid: string; flags?: Array}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.nodeVersions(uuid, {FilterBy: 'VersionsAll', Flags: flags}); + const result = await client.nodeVersions(uuid, {FilterBy: 'VersionsAll', Flags: flags}); const validation = RestNodeVersionsSchema.safeParse(result.data.Versions); @@ -354,15 +349,13 @@ export class CellsAPI { this.logger.warn('Get node versions response validation failed:', validation.error); } - return result.data.Versions || []; + return result.data.Versions ?? []; } async getNode({id, flags}: {id: string; flags?: Array}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.getByUuid(id, flags); + const result = await client.getByUuid(id, flags); const validation = RestNodeSchema.safeParse(result.data); @@ -390,9 +383,7 @@ export class CellsAPI { type?: RestIncomingNode['Type']; deleted?: boolean; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); const request: RestLookupRequest = { Scope: {Root: {Path: path}}, @@ -406,10 +397,10 @@ export class CellsAPI { }, }, SortField: sortBy, - SortDirDesc: sortDirection ? sortDirection === 'desc' : undefined, + SortDirDesc: sortDirection !== undefined ? sortDirection === 'desc' : undefined, }; - const result = await this.client.lookup(request); + const result = await client.lookup(request); return result.data; } @@ -435,17 +426,15 @@ export class CellsAPI { tags?: string[]; deleted?: boolean; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); const request: RestLookupRequest = { Scope: {Root: {Path: path}, Recursive: true}, Filters: { Text: {SearchIn: 'BaseName', Term: phrase}, - Type: type || 'UNKNOWN', + Type: type ?? 'UNKNOWN', Status: { - Deleted: deleted ? 'Only' : 'Not', + Deleted: deleted === true ? 'Only' : 'Not', }, Metadata: tags !== undefined && tags.length > 0 @@ -459,7 +448,7 @@ export class CellsAPI { SortDirDesc: sortDirection !== undefined ? sortDirection === 'desc' : undefined, }; - const result = await this.client.lookup(request); + const result = await client.lookup(request); return result.data; } @@ -477,11 +466,9 @@ export class CellsAPI { versionId?: RestIncomingNode['VersionId']; templateUuid?: NonNullable; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const response = await this.client.create({ + const response = await client.create({ Inputs: [ { Type: type, @@ -531,11 +518,9 @@ export class CellsAPI { } async deleteNodePublicLink({uuid}: {uuid: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.deletePublicLink(uuid); + const result = await client.deletePublicLink(uuid); return result.data; } @@ -561,9 +546,7 @@ export class CellsAPI { createPassword?: string; passwordEnabled?: boolean; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); const requestBody: { Link: RestShareLink; @@ -582,17 +565,15 @@ export class CellsAPI { requestBody.PasswordEnabled = passwordEnabled; } - const result = await this.client.createPublicLink(uuid, requestBody); + const result = await client.createPublicLink(uuid, requestBody); return result.data; } async getNodePublicLink({uuid}: {uuid: string}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.getPublicLink(uuid); + const result = await client.getPublicLink(uuid); return result.data; } @@ -620,9 +601,7 @@ export class CellsAPI { updatePassword?: string; passwordEnabled?: boolean; }): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); const requestBody: { Link: RestShareLink; @@ -646,27 +625,23 @@ export class CellsAPI { requestBody.UpdatePassword = updatePassword; } - const result = await this.client.updatePublicLink(linkUuid, requestBody); + const result = await client.updatePublicLink(linkUuid, requestBody); return result.data; } async getAllTags(): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.listNamespaceValues(USER_META_TAGS_NAMESPACE); + const result = await client.listNamespaceValues(USER_META_TAGS_NAMESPACE); return result.data; } async setNodeTags({uuid, tags}: {uuid: string; tags: string[]}): Promise { - if (!this.client || !this.storageService) { - throw new Error(CONFIGURATION_ERROR); - } + const client = this.getInitializedClient(); - const result = await this.client.patchNode(uuid, { + const result = await client.patchNode(uuid, { MetaUpdates: [ { Operation: tags.length > 0 ? 'PUT' : 'DELETE', diff --git a/libraries/api-client/src/connection/connectionApi.ts b/libraries/api-client/src/connection/connectionApi.ts index aaa7fe92e82..e5cfd6f1842 100644 --- a/libraries/api-client/src/connection/connectionApi.ts +++ b/libraries/api-client/src/connection/connectionApi.ts @@ -95,7 +95,7 @@ export class ConnectionAPI { const {connections, has_more, paging_state} = data; - if (connections.length) { + if (connections.length > 0) { allConnections = allConnections.concat(connections); } diff --git a/libraries/api-client/src/conversation/conversationApi/conversationApi.ts b/libraries/api-client/src/conversation/conversationApi/conversationApi.ts index 1f42e18f178..6505f76326a 100644 --- a/libraries/api-client/src/conversation/conversationApi/conversationApi.ts +++ b/libraries/api-client/src/conversation/conversationApi/conversationApi.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import {chunk} from '@wireapp/commons/lib/util/ArrayUtil'; import {proteus as ProtobufOTR} from '@wireapp/protocol-messaging/web/otr'; import {AxiosRequestConfig, isAxiosError} from 'axios'; @@ -128,7 +129,7 @@ export class ConversationAPI { ) {} private generateBaseConversationUrl(conversationId: QualifiedId, supportsQualifiedEndpoint: boolean = true): string { - return supportsQualifiedEndpoint && conversationId.domain + return supportsQualifiedEndpoint && is.nonEmptyString(conversationId.domain) ? `/${ConversationAPI.URL.CONVERSATIONS}/${conversationId.domain}/${conversationId.id}` : `/${ConversationAPI.URL.CONVERSATIONS}/${conversationId.id}`; } diff --git a/libraries/api-client/src/http/backendErrorMapper.ts b/libraries/api-client/src/http/backendErrorMapper.ts index fe1bee7225c..35aca1c473f 100644 --- a/libraries/api-client/src/http/backendErrorMapper.ts +++ b/libraries/api-client/src/http/backendErrorMapper.ts @@ -218,7 +218,7 @@ export function mapBackendError(error: BackendError): BackendError { // 1) Message-specific variant const messageVariantHandler = messageVariantHandlers[code]?.[label]?.[message]; - if (messageVariantHandler) { + if (messageVariantHandler !== undefined) { const mapped = messageVariantHandler({...error, label}); logger.info('Mapped backend error with message variant', {error, mapped}); return mapped; @@ -226,7 +226,7 @@ export function mapBackendError(error: BackendError): BackendError { // 2) Default fallback for this (code,label) const fallbackHandler = defaultHandlers[code]?.[label]; - if (fallbackHandler) { + if (fallbackHandler !== undefined) { const mapped = fallbackHandler({...error, label}); logger.info('Mapped backend error with default handler', {error, mapped}); return mapped; diff --git a/libraries/api-client/src/http/httpClient.ts b/libraries/api-client/src/http/httpClient.ts index 6b5fa8f69d5..c0e8168f38b 100644 --- a/libraries/api-client/src/http/httpClient.ts +++ b/libraries/api-client/src/http/httpClient.ts @@ -222,7 +222,7 @@ export class HttpClient extends EventEmitter { } public async _sendRequest({config, isFirstTry = true, abortController}: SendRequest): Promise> { - if (this.accessTokenStore.accessTokenData) { + if (this.accessTokenStore.accessTokenData !== undefined) { // TODO: remove tokenAsParam const {token_type, access_token} = this.accessTokenStore.accessTokenData; diff --git a/libraries/api-client/src/notification/notificationApi/notificationApi.ts b/libraries/api-client/src/notification/notificationApi/notificationApi.ts index 1c25de0a532..fc4e06ae0d2 100644 --- a/libraries/api-client/src/notification/notificationApi/notificationApi.ts +++ b/libraries/api-client/src/notification/notificationApi/notificationApi.ts @@ -148,13 +148,13 @@ export class NotificationAPI { const {notifications, has_more} = payload; - if (notifications?.length) { + if (notifications !== undefined && notifications.length > 0) { notificationList = notificationList.concat(notifications); } - if (has_more) { + if (has_more === true) { const lastNotification = notifications[notifications.length - 1]; - if (lastNotification) { + if (lastNotification !== undefined) { return getNotificationChunks(notificationList, currentClientId, lastNotification.id); } } diff --git a/libraries/api-client/src/shims/node/cookie.ts b/libraries/api-client/src/shims/node/cookie.ts index 71048c09b47..8014d32e17b 100644 --- a/libraries/api-client/src/shims/node/cookie.ts +++ b/libraries/api-client/src/shims/node/cookie.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import {AxiosHeaders, AxiosRequestConfig, AxiosResponse} from 'axios'; import {Cookie as ToughCookie} from 'tough-cookie'; @@ -30,10 +31,10 @@ import {ObfuscationUtil} from '../../obfuscation'; const logger = LogFactory.getLogger('@wireapp/api-client/shims/node/cookie'); export const retrieveCookie = async (response: AxiosResponse): Promise => { - if (response.headers?.['set-cookie']) { + if (response.headers !== undefined && response.headers['set-cookie'] !== undefined) { const cookies: ToughCookie[] = response.headers['set-cookie'].flatMap(cookieString => { const cookie = ToughCookie.parse(cookieString); - return cookie ? [cookie] : []; + return is.nullOrUndefined(cookie) ? [] : [cookie]; }); for (const cookie of cookies) { const cookieString = cookie.expires.toString(); @@ -56,7 +57,7 @@ export const sendRequestWithCookie = async ( config: AxiosRequestConfig, ): Promise> => { const cookie = CookieStore.getCookie(); - if (cookie && !cookie.isExpired) { + if (cookie !== undefined && !cookie.isExpired) { config.headers = new AxiosHeaders(config.headers as AxiosHeaders); config.headers.set('Cookie', `zuid=${cookie.zuid}`); config.withCredentials = true; diff --git a/libraries/api-client/src/tcp/reconnectingWebsocket.ts b/libraries/api-client/src/tcp/reconnectingWebsocket.ts index 16c1e863278..c0baf167e1b 100644 --- a/libraries/api-client/src/tcp/reconnectingWebsocket.ts +++ b/libraries/api-client/src/tcp/reconnectingWebsocket.ts @@ -113,7 +113,7 @@ export class ReconnectingWebsocket { * **/ this.stopBackFromSleepHandler = onBackFromSleep({ callback: () => { - if (!this.socket) { + if (this.socket === undefined) { this.logger.debug('WebSocket instance does not exist, skipping reconnect after sleep'); return; } @@ -130,7 +130,7 @@ export class ReconnectingWebsocket { private readonly internalOnError = (error: ErrorEvent) => { this.logger.warn('WebSocket connection error', error); - if (this.onError) { + if (this.onError !== undefined) { this.onError(error); } }; @@ -154,10 +154,10 @@ export class ReconnectingWebsocket { private readonly internalOnOpen = (event: Event) => { this.logger.info(`WebSocket opened (reconnect attempt #${this.reconnectAttemptCount})`); this.resetLongRunningRetrySequence(); - if (this.socket) { + if (this.socket !== undefined) { this.socket.binaryType = 'arraybuffer'; } - if (this.onOpen) { + if (this.onOpen !== undefined) { this.onOpen(event); } }; @@ -177,11 +177,11 @@ export class ReconnectingWebsocket { private readonly internalOnClose = (event: CloseEvent) => { this.logger.info( - `WebSocket closed — code: ${event?.code}, reason: "${event?.reason || 'none'}", wasClean: ${event?.wasClean ?? 'unknown'}`, + `WebSocket closed — code: ${event?.code}, reason: "${event?.reason ?? 'none'}", wasClean: ${event?.wasClean ?? 'unknown'}`, ); this.stopPinging(); this.resolvePendingHealthChecks(false); - if (this.onClose) { + if (this.onClose !== undefined) { this.onClose(event); } }; @@ -193,13 +193,13 @@ export class ReconnectingWebsocket { } private stopPinging(): void { - if (this.pingerId) { + if (this.pingerId !== undefined) { clearInterval(this.pingerId); } } private readonly sendPing = (): void => { - if (!this.socket) { + if (this.socket === undefined) { this.logger.debug('WebSocket instance does not exist, skipping ping'); return; } @@ -268,7 +268,7 @@ export class ReconnectingWebsocket { } public getState(): WEBSOCKET_STATE { - return this.socket ? this.socket.readyState : WEBSOCKET_STATE.CLOSED; + return this.socket !== undefined ? this.socket.readyState : WEBSOCKET_STATE.CLOSED; } /** @@ -333,7 +333,7 @@ export class ReconnectingWebsocket { public disconnect(reason = 'Closed by client'): void { this.resetLongRunningRetrySequence(); - if (this.socket) { + if (this.socket !== undefined) { this.logger.info(`Disconnecting from WebSocket (reason: "${reason}")`); this.socket.close(CloseEventCode.NORMAL_CLOSURE, reason); } diff --git a/libraries/api-client/src/tcp/webSocketClient.ts b/libraries/api-client/src/tcp/webSocketClient.ts index 7d49c1b15c0..fcf88ed3940 100644 --- a/libraries/api-client/src/tcp/webSocketClient.ts +++ b/libraries/api-client/src/tcp/webSocketClient.ts @@ -102,7 +102,7 @@ export class WebSocketClient extends EventEmitter { } private readonly onMessage = (data: string) => { - if (!data) { + if (data.length === 0) { this.logger.warn('Received empty message from WebSocket'); return; } @@ -166,7 +166,7 @@ export class WebSocketClient extends EventEmitter { this.socket.setOnLongRunningRetry(this.onLongRunningRetry); this.socket.setOnOpen(() => { this.onOpen(); - if (onConnect) { + if (onConnect !== undefined) { this.abortHandler = new AbortController(); void onConnect(this.abortHandler); } @@ -253,7 +253,7 @@ export class WebSocketClient extends EventEmitter { this.logger.warn('Reconnecting WebSocket with unset token'); } - if (!this.versionPrefix) { + if (this.versionPrefix.length === 0) { throw new Error('Missing backend API version: cannot establish WebSocket connection'); } diff --git a/libraries/api-client/src/team/invitation/teamInvitationApi.ts b/libraries/api-client/src/team/invitation/teamInvitationApi.ts index 15023fdd73c..711accc1c1c 100644 --- a/libraries/api-client/src/team/invitation/teamInvitationApi.ts +++ b/libraries/api-client/src/team/invitation/teamInvitationApi.ts @@ -61,8 +61,8 @@ export class TeamInvitationAPI { allInvitations = allInvitations.concat(invitationChunk.invitations); while (invitationChunk.has_more) { const invitations = invitationChunk.invitations; - const lastInvitation = invitations[invitations.length - 1] || {}; - const lastChunkId = (lastInvitation as TeamInvitation).id; + const lastInvitation = invitations[invitations.length - 1]; + const lastChunkId = lastInvitation?.id; invitationChunk = await this.getInvitations(teamId, lastChunkId); allInvitations = allInvitations.concat(invitationChunk.invitations); } diff --git a/libraries/api-client/src/team/member/memberApi.ts b/libraries/api-client/src/team/member/memberApi.ts index 71341d9f2b5..71f0bbfa036 100644 --- a/libraries/api-client/src/team/member/memberApi.ts +++ b/libraries/api-client/src/team/member/memberApi.ts @@ -127,7 +127,7 @@ export class MemberAPI { ): Promise { const {ids} = parameters; - if (ids.length) { + if (ids.length > 0) { const uniqueIds = ArrayUtil.removeDuplicates(ids); const idChunks = ArrayUtil.chunk(uniqueIds, limit); const resolvedTasks = await Promise.all( diff --git a/libraries/api-client/src/user/userApi.ts b/libraries/api-client/src/user/userApi.ts index 857ef688c83..d384c1a37e3 100644 --- a/libraries/api-client/src/user/userApi.ts +++ b/libraries/api-client/src/user/userApi.ts @@ -388,14 +388,14 @@ export class UserAPI { return response.data; }; - if ('handles' in parameters && parameters.handles.length) { + if ('handles' in parameters && parameters.handles.length > 0) { const uniqueHandles = ArrayUtil.removeDuplicates(parameters.handles); const handleChunks = ArrayUtil.chunk(uniqueHandles, limit); const resolvedTasks = await Promise.all(handleChunks.map(handleChunk => fetchUsers({handles: handleChunk}))); return ArrayUtil.flatten(resolvedTasks); } - if ('ids' in parameters && parameters.ids.length) { + if ('ids' in parameters && parameters.ids.length > 0) { const uniqueIds = ArrayUtil.removeDuplicates(parameters.ids); const idChunks = ArrayUtil.chunk(uniqueIds, limit); const resolvedTasks = await Promise.all(idChunks.map(idChunk => fetchUsers({ids: idChunk}))); diff --git a/libraries/api-client/src/utils/backFromSleepHandler/backFromSleepHandler.ts b/libraries/api-client/src/utils/backFromSleepHandler/backFromSleepHandler.ts index 52bf81a38f1..0785a3c6f10 100644 --- a/libraries/api-client/src/utils/backFromSleepHandler/backFromSleepHandler.ts +++ b/libraries/api-client/src/utils/backFromSleepHandler/backFromSleepHandler.ts @@ -56,12 +56,12 @@ export const onBackFromSleep = ({ lastTime = currentTime; - if (isDisconnectedCallback && isDisconnectedCallback()) { + if (isDisconnectedCallback !== undefined && isDisconnectedCallback()) { wasDisconnected = true; } if (wasAsleep) { - if (!isDisconnectedCallback || wasDisconnected) { + if (isDisconnectedCallback === undefined || wasDisconnected) { wasDisconnected = false; callback(); } diff --git a/libraries/core/src/account.ts b/libraries/core/src/account.ts index 8cde8f38129..cca8469dcfb 100644 --- a/libraries/core/src/account.ts +++ b/libraries/core/src/account.ts @@ -222,7 +222,7 @@ export class Account extends TypedEventEmitter { }); apiClient.on(APIClient.TOPIC.COOKIE_REFRESH, async (cookie?: Cookie) => { - if (cookie && this.storeEngine) { + if (cookie !== undefined && this.storeEngine !== undefined) { try { await this.persistCookie(this.storeEngine, cookie); } catch (error: unknown) { @@ -283,7 +283,7 @@ export class Account extends TypedEventEmitter { const context = this.apiClient.context; const domain = context?.domain ?? ''; - if (!this.currentClient) { + if (this.currentClient === undefined) { throw new Error('Client has not been initialized - please login first'); } @@ -366,7 +366,7 @@ export class Account extends TypedEventEmitter { entropyData?: Uint8Array, clientInfo: ClientInfo = coreDefaultClient, ): Promise => { - if (!this.service || !this.apiClient.context || !this.storeEngine) { + if (this.service === undefined || this.apiClient.context === undefined || this.storeEngine === undefined) { throw new Error('Services are not set or context not initialized.'); } @@ -404,7 +404,7 @@ export class Account extends TypedEventEmitter { * @returns The local existing client or undefined if the client does not exist or is not valid (non existing on backend) */ public initClient = async (client: RegisteredClient, mlsConfig?: InitClientOptions) => { - if (!this.service || !this.apiClient.context || !this.storeEngine) { + if (this.service === undefined || this.apiClient.context === undefined || this.storeEngine === undefined) { throw new Error('Services are not set.'); } this.apiClient.context.clientId = client.id; @@ -414,7 +414,7 @@ export class Account extends TypedEventEmitter { await this.service.proteus.initClient(this.apiClient.context); - if ((await this.isMLSActiveForClient()) && this.service.mls && mlsConfig) { + if ((await this.isMLSActiveForClient()) && this.service.mls !== undefined && mlsConfig !== undefined) { const {userId, domain = ''} = this.apiClient.context; await this.service.mls.initClient({id: userId, domain}, client, mlsConfig); // initialize schedulers for pending mls proposals once client is initialized @@ -478,9 +478,10 @@ export class Account extends TypedEventEmitter { private readonly initServices = async (context: Context): Promise => { const encryptedStoreName = this.generateEncryptedDbName(context); - this.encryptedDb = this.options.systemCrypto - ? await createCustomEncryptedStore(encryptedStoreName, this.options.systemCrypto) - : await createEncryptedStore(encryptedStoreName); + this.encryptedDb = + this.options.systemCrypto !== undefined + ? await createCustomEncryptedStore(encryptedStoreName, this.options.systemCrypto) + : await createEncryptedStore(encryptedStoreName); this.db = await openDB(this.generateCoreDbName(context)); this.storeEngine = await this.initEngine(context, this.encryptedDb); @@ -590,7 +591,7 @@ export class Account extends TypedEventEmitter { } try { - if (this.storeEngine) { + if (this.storeEngine !== undefined) { await wipeCoreCryptoDb(this.storeEngine); } } catch (error: unknown) { @@ -610,7 +611,7 @@ export class Account extends TypedEventEmitter { */ private readonly wipeAllData = async (): Promise => { try { - if (this.storeEngine) { + if (this.storeEngine !== undefined) { await deleteIdentity(this.storeEngine, false); } } catch (error: unknown) { @@ -618,7 +619,7 @@ export class Account extends TypedEventEmitter { } try { - if (this.db) { + if (this.db !== undefined) { await deleteDB(this.db); } } catch (error: unknown) { @@ -634,7 +635,7 @@ export class Account extends TypedEventEmitter { */ private readonly wipeCryptoData = async (): Promise => { try { - if (this.storeEngine) { + if (this.storeEngine !== undefined) { await deleteIdentity(this.storeEngine, true); } } catch (error: unknown) { @@ -702,7 +703,7 @@ export class Account extends TypedEventEmitter { */ dryRun?: boolean; } = {}): Promise<() => void> => { - if (!this.currentClient) { + if (this.currentClient === undefined) { throw new Error('Client has not been initialized - please login first'); } @@ -861,9 +862,8 @@ export class Account extends TypedEventEmitter { try { const start = Date.now(); const firstNotificationPayload = notification.payload[0]; - const notificationTime = firstNotificationPayload - ? this.getNotificationEventTime(firstNotificationPayload) - : null; + const notificationTime = + firstNotificationPayload !== undefined ? this.getNotificationEventTime(firstNotificationPayload) : null; this.logger.info(`Processing legacy notification "${notification.id}" at ${notificationTime}`); this.logger.info(`Total notifications queue length: ${this.notificationProcessingQueue.getLength()}`); this.logger.info(`Total pending proposals queue length: ${getProposalQueueLength()}`); @@ -975,7 +975,8 @@ export class Account extends TypedEventEmitter { const payloads = this.service!.notification.handleNotification(notification.data.event, source); const firstEventPayload = notification.data.event.payload[0]; - const notificationTime = firstEventPayload ? this.getNotificationEventTime(firstEventPayload) : null; + const notificationTime = + firstEventPayload !== undefined ? this.getNotificationEventTime(firstEventPayload) : null; if (this.connectionState !== ConnectionState.LIVE && notificationTime !== null && notificationTime.length > 0) { onNotificationStreamProgress(notificationTime); } @@ -1186,7 +1187,7 @@ export class Account extends TypedEventEmitter { }; public getClientCapabilities = () => { - return this.currentClient?.capabilities || []; + return this.currentClient?.capabilities ?? []; }; public static checkIsConsumable = ( @@ -1220,7 +1221,7 @@ export class Account extends TypedEventEmitter { const openDb = async () => { const dbKey = await generateSecretKey({keyId: 'db-key', keySize: 32, secretsDb: encryptedStore}); const initializedDb = await this.options.createStore?.(dbName, dbKey.key); - if (initializedDb) { + if (initializedDb !== undefined) { this.logger.debug(`Initialized store with existing engine "${dbName}".`); return initializedDb; } @@ -1231,7 +1232,7 @@ export class Account extends TypedEventEmitter { }; const storeEngine = await openDb(); const cookie = CookieStore.getCookie(); - if (cookie) { + if (cookie !== undefined) { await this.persistCookie(storeEngine, cookie); } return storeEngine; diff --git a/libraries/core/src/client/clientService.ts b/libraries/core/src/client/clientService.ts index ac430824143..55ac3e5b8d1 100644 --- a/libraries/core/src/client/clientService.ts +++ b/libraries/core/src/client/clientService.ts @@ -167,7 +167,7 @@ export class ClientService { {prekeys, lastPrekey}: InitialPrekeys, useLegacyNotificationStream: boolean = true, ): Promise { - if (!this.apiClient.context) { + if (this.apiClient.context === undefined) { throw new Error('Context is not set.'); } diff --git a/libraries/core/src/conversation/conversationService/conversationService.ts b/libraries/core/src/conversation/conversationService/conversationService.ts index 916aca0e725..ddd4a958e7d 100644 --- a/libraries/core/src/conversation/conversationService/conversationService.ts +++ b/libraries/core/src/conversation/conversationService/conversationService.ts @@ -105,7 +105,7 @@ export class ConversationService extends TypedEventEmitter { this.messageTimer = new MessageTimer(); // Make MLS recovery orchestrator mandatory in this service - if (!this._mlsService) { + if (this._mlsService === undefined) { throw new Error('MLSService is required to construct ConversationService with MLS capabilities'); } @@ -132,7 +132,7 @@ export class ConversationService extends TypedEventEmitter { } get mlsService(): MLSService { - if (!this._mlsService) { + if (this._mlsService === undefined) { throw new Error('Cannot do MLS operations on a non-mls environment'); } return this._mlsService; @@ -196,7 +196,7 @@ export class ConversationService extends TypedEventEmitter { } public async getConversations(conversationIds?: QualifiedId[]): Promise { - if (!conversationIds) { + if (conversationIds === undefined) { const conversationIdsToSkip = await this.coreDatabase.getAll('conversationBlacklist'); return this.apiClient.api.conversation.getConversationList(conversationIdsToSkip); } @@ -456,7 +456,7 @@ export class ConversationService extends TypedEventEmitter { const sentAt = response.time?.length > 0 ? response.time : new Date().toISOString(); const failedToSend = - response?.failed || (response?.failed_to_send ?? []).length > 0 + response.failed !== undefined || (response.failed_to_send ?? []).length > 0 ? { queued: response?.failed_to_send, failed: response?.failed, @@ -467,7 +467,7 @@ export class ConversationService extends TypedEventEmitter { id: payload.messageId, sentAt, failedToSend, - state: sentAt ? MessageSendingState.OUTGOING_SENT : MessageSendingState.CANCELED, + state: sentAt.length > 0 ? MessageSendingState.OUTGOING_SENT : MessageSendingState.CANCELED, }; } @@ -627,7 +627,7 @@ export class ConversationService extends TypedEventEmitter { const conversation = await this.getConversationByGroupId(groupId); - if (!conversation) { + if (conversation === undefined) { this.logger.warn(`No conversation found for group ${groupId}`, {error}); return; } @@ -765,7 +765,7 @@ export class ConversationService extends TypedEventEmitter { //fetch all the mls conversations from backend const conversations = await this.apiClient.api.conversation.getConversationList(); - const foundConversations = conversations.found || []; + const foundConversations = conversations.found ?? []; const mlsConversations = foundConversations.filter(isMLSConversation); @@ -1005,7 +1005,7 @@ export class ConversationService extends TypedEventEmitter { private async handleMLSMessageAddEvent(event: ConversationMLSMessageAddEvent): Promise { try { const {qualified_conversation: qualifiedConversationId, subconv} = event; - if (!qualifiedConversationId) { + if (qualifiedConversationId === undefined) { throw new Error('Qualified conversation id is missing in the MLS message-add event'); } return await this.MLSRecoveryOrchestrator.execute({ @@ -1043,7 +1043,7 @@ export class ConversationService extends TypedEventEmitter { const mlsConversation = await this.apiClient.api.conversation.getConversation(conversationId); - if (!isMLSConversation(mlsConversation)) { + if (isMLSConversation(mlsConversation) === false) { throw new Error('Conversation is not an MLS conversation'); } @@ -1079,7 +1079,7 @@ export class ConversationService extends TypedEventEmitter { private async isConversationBlacklisted(conversationId: string): Promise { const foundEntry = await this.coreDatabase.get('conversationBlacklist', conversationId); - return !!foundEntry; + return foundEntry !== undefined; } /** diff --git a/libraries/core/src/conversation/conversationService/utility/getConversationQualifiedMembers.ts b/libraries/core/src/conversation/conversationService/utility/getConversationQualifiedMembers.ts index b68675ba1b7..7b03e5c9c6d 100644 --- a/libraries/core/src/conversation/conversationService/utility/getConversationQualifiedMembers.ts +++ b/libraries/core/src/conversation/conversationService/utility/getConversationQualifiedMembers.ts @@ -33,10 +33,10 @@ const getConversationQualifiedMembers = async ({apiClient, conversationId}: Para * other clients. */ const filteredConversations = conversation.members.others - .filter(member => !!member.qualified_id) + .filter(member => member.qualified_id !== undefined) .map(member => member.qualified_id!); - if (conversation.members.self?.qualified_id) { + if (conversation.members.self?.qualified_id !== undefined) { filteredConversations.push(conversation.members.self.qualified_id); } diff --git a/libraries/core/src/conversation/message/messageToProtoMapper.ts b/libraries/core/src/conversation/message/messageToProtoMapper.ts index 92d0a6ed532..f17c17272f6 100644 --- a/libraries/core/src/conversation/message/messageToProtoMapper.ts +++ b/libraries/core/src/conversation/message/messageToProtoMapper.ts @@ -35,16 +35,18 @@ export class MessageToProtoMapper { urlOffset: linkPreview.urlOffset, }); - if (linkPreview.tweet) { + const tweetData = linkPreview.tweet; + if (tweetData !== undefined && tweetData !== null) { linkPreviewMessage.tweet = Tweet.create({ - author: linkPreview.tweet.author, - username: linkPreview.tweet.username, + author: tweetData.author, + username: tweetData.username, }); linkPreviewMessage.metaData = 'tweet'; } - if (linkPreview.imageUploaded) { - const {asset, image} = linkPreview.imageUploaded; + const uploadedImageData = linkPreview.imageUploaded; + if (uploadedImageData !== undefined && uploadedImageData !== null) { + const {asset, image} = uploadedImageData; const imageMetadata = Asset.ImageMetaData.create({ height: image.height, @@ -103,7 +105,7 @@ export class MessageToProtoMapper { textMessage.mentions = mentions.map(mention => Mention.create(mention)); } - if (quote) { + if (quote !== undefined) { textMessage.quote = Quote.create({ quotedMessageId: quote.quotedMessageId, quotedMessageSha256: quote.quotedMessageSha256, diff --git a/libraries/core/src/conversation/message/textContentBuilder.ts b/libraries/core/src/conversation/message/textContentBuilder.ts index 2a36ba957c4..599e05271bf 100644 --- a/libraries/core/src/conversation/message/textContentBuilder.ts +++ b/libraries/core/src/conversation/message/textContentBuilder.ts @@ -54,7 +54,7 @@ export class TextContentBuilder { } withQuote(quote?: QuoteContent) { - if (quote) { + if (quote !== undefined) { this.content.quote = quote as QuoteContent; } diff --git a/libraries/core/src/conversation/message/userClientsUtil.ts b/libraries/core/src/conversation/message/userClientsUtil.ts index 0fc2751a765..aaf6ab89d88 100644 --- a/libraries/core/src/conversation/message/userClientsUtil.ts +++ b/libraries/core/src/conversation/message/userClientsUtil.ts @@ -43,7 +43,7 @@ export function flattenUserMap(userMap: QualifiedUserMap): {data */ export function nestUsersList(users: {data: T; userId: QualifiedId}[]): QualifiedUserMap { return users.reduce((users, {data, userId: {domain, id}}) => { - if (!users[domain]) { + if (users[domain] === undefined) { users[domain] = {}; } users[domain][id] = data; diff --git a/libraries/core/src/conversation/subconversationService/subconversationService.ts b/libraries/core/src/conversation/subconversationService/subconversationService.ts index 560c700a68a..0367b51ee35 100644 --- a/libraries/core/src/conversation/subconversationService/subconversationService.ts +++ b/libraries/core/src/conversation/subconversationService/subconversationService.ts @@ -54,7 +54,7 @@ export class SubconversationService extends TypedEventEmitter { } get mlsService(): MLSService { - if (!this._mlsService) { + if (this._mlsService === undefined) { throw new Error('MLSService was not initialised!'); } return this._mlsService; @@ -268,7 +268,7 @@ export class SubconversationService extends TypedEventEmitter { if (groupId !== subconversationGroupId) { // if the epoch update did not happen in the subconversation directly, check if it happened in the parent conversation const parentConversationId = findConversationByGroupId(groupId); - if (!parentConversationId) { + if (parentConversationId === undefined) { this.logger.debug('Ignoring NEW_EPOCH event: could not map to parent conversation'); return; } @@ -294,7 +294,7 @@ export class SubconversationService extends TypedEventEmitter { parentConversationGroupId, ); - if (!subconversationEpochInfo) { + if (subconversationEpochInfo === null) { this.logger.debug('No subconversation epoch info available; skipping callback', { parentConversationId, parentConversationGroupId, diff --git a/libraries/core/src/cryptography/messageHashService.ts b/libraries/core/src/cryptography/messageHashService.ts index 25beb839eac..8fd7d04f5ff 100644 --- a/libraries/core/src/cryptography/messageHashService.ts +++ b/libraries/core/src/cryptography/messageHashService.ts @@ -63,7 +63,7 @@ export class MessageHashService { } private getAssetBytes(content: AssetContent): Buffer { - if (content.uploaded) { + if (content.uploaded !== undefined) { const assetId = content.uploaded.assetId; return this.convertToUtf16BE(assetId); } diff --git a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceExternal.ts b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceExternal.ts index 24231190024..8f8930c7b82 100644 --- a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceExternal.ts +++ b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceExternal.ts @@ -73,7 +73,7 @@ export class E2EIServiceExternal extends TypedEventEmitter { // If we have a handle in the local storage, we are in the enrollment process (this handle is saved before oauth redirect) public async isEnrollmentInProgress(): Promise { const data = await this.enrollmentStorage.getPendingEnrollmentData(); - return !!data; + return data !== undefined; } public clearAllProgress() { @@ -138,7 +138,7 @@ export class E2EIServiceExternal extends TypedEventEmitter { const mappedUserIdentities = new Map(); for (const userId of userIds) { - const identities = (userIdentities.get(userId.id) || []).map(identity => ({ + const identities = (userIdentities.get(userId.id) ?? []).map(identity => ({ ...identity, deviceId: parseFullQualifiedClientId(identity.clientId).client, qualifiedUserId: userId, @@ -205,7 +205,7 @@ export class E2EIServiceExternal extends TypedEventEmitter { public async isFreshMLSSelfClient(): Promise { const client = await this.clientService.loadClient(); - return !client || !this.mlsService.isInitializedMLSClient(client); + return client === undefined || !this.mlsService.isInitializedMLSClient(client); } private async registerLocalCertificateRoot(acmeService: AcmeService): Promise { @@ -233,7 +233,7 @@ export class E2EIServiceExternal extends TypedEventEmitter { } private get acmeService(): AcmeService { - if (!this._acmeService) { + if (this._acmeService === undefined) { throw new Error('AcmeService not initialized'); } return this._acmeService; @@ -263,7 +263,7 @@ export class E2EIServiceExternal extends TypedEventEmitter { const isRootRegistered = await this.coreCryptoClient.transaction(cx => cx.e2eiIsPKIEnvSetup()); // Register root certificate if not already registered - if (!isRootRegistered) { + if (isRootRegistered === false) { await this.registerLocalCertificateRoot(this.acmeService); } diff --git a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceInternal.ts b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceInternal.ts index 4d5f7d52b90..749c51d86ff 100644 --- a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceInternal.ts +++ b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/e2eiServiceInternal.ts @@ -254,7 +254,7 @@ export class E2EIServiceInternal { orderUrl: enrollmentData.orderUrl, }); - if (!finalizeOrderData.certificateUrl) { + if (finalizeOrderData.certificateUrl === undefined || finalizeOrderData.certificateUrl.length === 0) { throw new Error('Error while trying to continue OAuth flow. No certificateUrl received'); } @@ -266,7 +266,7 @@ export class E2EIServiceInternal { identity, }); - if (!certificate) { + if (certificate === undefined || certificate.length === 0) { throw new Error('Error while trying to continue OAuth flow. No certificate received'); } diff --git a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/authorization.ts b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/authorization.ts index 7ebbe3cb95d..f2240d5d45c 100644 --- a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/authorization.ts +++ b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/authorization.ts @@ -56,7 +56,7 @@ export const getAuthorizationChallenges = async ({ const {challenge: oidcChallenge} = challenges.find(challenge => challenge.type.includes('oidc')) ?? {}; const {challenge: dpopChallenge} = challenges.find(challenge => challenge.type.includes('dpop')) ?? {}; - if (!dpopChallenge || !oidcChallenge) { + if (dpopChallenge === undefined || oidcChallenge === undefined) { throw new Error('missing dpop or oidc challenge'); } // manual copy of the wasm data because of a problem while cloning it diff --git a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/certificate.ts b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/certificate.ts index d4ed6988930..22174b87990 100644 --- a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/certificate.ts +++ b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/certificate.ts @@ -17,6 +17,8 @@ * */ +import is from '@sindresorhus/is'; + import {AcmeService} from '../connection'; import {E2eiEnrollment, Nonce} from '../e2eiService.types'; @@ -31,7 +33,7 @@ export const getCertificate = async ({certificateUrl, connection, identity, nonc const certificateResponse = await connection.getCertificate(certificateUrl, reqBody); - if (certificateResponse?.data) { + if (is.nonEmptyString(certificateResponse?.data)) { return { certificate: certificateResponse.data, nonce: certificateResponse.nonce, diff --git a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/dpopChallenge/dpopChallenge.ts b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/dpopChallenge/dpopChallenge.ts index c98cc089113..05afa649d57 100644 --- a/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/dpopChallenge/dpopChallenge.ts +++ b/libraries/core/src/messagingProtocols/mls/e2eIdentityService/steps/dpopChallenge/dpopChallenge.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import {Converter} from 'bazinga64'; import {DoWireDpopChallengeParams, GetClientNonceParams} from './dpopChallenge.types'; @@ -24,7 +25,7 @@ import {DoWireDpopChallengeParams, GetClientNonceParams} from './dpopChallenge.t const getClientNonce = async ({apiClient, clientId}: GetClientNonceParams) => { try { const nonce = await apiClient.api.client.getNonce(clientId); - if (nonce) { + if (is.nonEmptyString(nonce)) { return nonce; } throw new Error('No client-nonce received'); diff --git a/libraries/core/src/messagingProtocols/mls/eventHandler/events/messageAdd/messageAdd.ts b/libraries/core/src/messagingProtocols/mls/eventHandler/events/messageAdd/messageAdd.ts index 99847beb2e3..10218f67006 100644 --- a/libraries/core/src/messagingProtocols/mls/eventHandler/events/messageAdd/messageAdd.ts +++ b/libraries/core/src/messagingProtocols/mls/eventHandler/events/messageAdd/messageAdd.ts @@ -67,14 +67,14 @@ export const handleMLSMessageAdd = async ({ qualifiedConversationId, ); - if (!decryptedMessage) { + if (decryptedMessage === undefined) { // If the message is not decrypted, we return null return null; } const {message, commitDelay, senderClientId: encodedSenderClientId} = decryptedMessage; - if (encodedSenderClientId) { + if (encodedSenderClientId !== undefined) { const decoder = new TextDecoder(); const senderClientId = decoder.decode(optionalToUint8Array(encodedSenderClientId.copyBytes())); event.senderClientId = senderClientId; @@ -95,5 +95,5 @@ export const handleMLSMessageAdd = async ({ }); } - return message ? {event, decryptedData: GenericMessage.decode(message)} : null; + return message !== undefined ? {event, decryptedData: GenericMessage.decode(message)} : null; }; diff --git a/libraries/core/src/messagingProtocols/mls/mlsService/mlsService.ts b/libraries/core/src/messagingProtocols/mls/mlsService/mlsService.ts index fd3eafa82ab..6fe88a9c158 100644 --- a/libraries/core/src/messagingProtocols/mls/mlsService/mlsService.ts +++ b/libraries/core/src/messagingProtocols/mls/mlsService/mlsService.ts @@ -172,11 +172,11 @@ export class MLSService extends TypedEventEmitter { * return true if the MLS service if configured and ready to be used */ get isEnabled() { - return !!this._config; + return this._config !== undefined; } get config() { - if (!this._config) { + if (this._config === undefined) { throw new Error('mls config is not set, did you forget to call initClient?'); } return this._config; @@ -262,12 +262,12 @@ export class MLSService extends TypedEventEmitter { const bundlePayload = new Uint8Array([ ...commit, ...groupInfo.payload.copyBytes(), - ...(welcome?.copyBytes() || []), + ...(welcome?.copyBytes() ?? []), ]); try { const response = await this.apiClient.api.conversation.postMlsCommitBundle(bundlePayload); - if (response.failed_to_send) { + if (response.failed_to_send !== undefined) { this.logger.warn(`Failed to send commit bundle to backend`); return 'retry'; } @@ -532,7 +532,7 @@ export class MLSService extends TypedEventEmitter { } private dispatchNewCrlDistributionPoints(crlNewDistributionPoints: NewCrlDistributionPoints) { - if (crlNewDistributionPoints && crlNewDistributionPoints.length > 0) { + if (crlNewDistributionPoints !== undefined && crlNewDistributionPoints.length > 0) { this.emit(MLSServiceEvents.NEW_CRL_DISTRIBUTION_POINTS, crlNewDistributionPoints); } } diff --git a/libraries/core/src/messagingProtocols/mls/recovery/mlsRecoveryOrchestrator.ts b/libraries/core/src/messagingProtocols/mls/recovery/mlsRecoveryOrchestrator.ts index 60f6aeacdcb..1a02b963887 100644 --- a/libraries/core/src/messagingProtocols/mls/recovery/mlsRecoveryOrchestrator.ts +++ b/libraries/core/src/messagingProtocols/mls/recovery/mlsRecoveryOrchestrator.ts @@ -215,7 +215,7 @@ export class MlsRecoveryOrchestratorImpl implements MlsRecoveryOrchestrator { /** Resolve the effective policy for the mapped error and operation. Supports per-operation policies. */ private getPolicyFor = (err: DomainMlsError, ctx: OperationContext): RecoveryPolicy => { const entry = this.policies[err.type]; - if (!entry) { + if (entry === undefined) { return {action: RecoveryActionKind.Unknown, retryConfig: {maxAttempts: 0}}; } @@ -238,7 +238,7 @@ export class MlsRecoveryOrchestratorImpl implements MlsRecoveryOrchestrator { recoveryKey: string, ) { const id = context.qualifiedConversationId; - if (!id) { + if (id === undefined) { const errorMessage = `Missing conversationId for recovery action ${policy.action}`; this.logger.error(errorMessage); throw new Error(errorMessage); diff --git a/libraries/core/src/messagingProtocols/proteus/eventHandler/events/otrMessageAdd/otrMessageAdd.ts b/libraries/core/src/messagingProtocols/proteus/eventHandler/events/otrMessageAdd/otrMessageAdd.ts index c607a9d8c48..e709bce122f 100644 --- a/libraries/core/src/messagingProtocols/proteus/eventHandler/events/otrMessageAdd/otrMessageAdd.ts +++ b/libraries/core/src/messagingProtocols/proteus/eventHandler/events/otrMessageAdd/otrMessageAdd.ts @@ -45,7 +45,7 @@ export const handleOtrMessageAdd = async ({ qualified_from, data: {sender: clientId, text: encodedCiphertext}, } = event; - const userId = qualified_from || {id: from, domain: ''}; + const userId = qualified_from ?? {id: from, domain: ''}; const messageBytes = Decoder.fromBase64(encodedCiphertext).asBytes; const now = Date.now(); logger.info('Decrypting OTR message', {userId, clientId}); diff --git a/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/coreCryptoWrapper/coreCryptoWrapper.ts b/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/coreCryptoWrapper/coreCryptoWrapper.ts index e6f2a5b4cb3..0f278708b45 100644 --- a/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/coreCryptoWrapper/coreCryptoWrapper.ts +++ b/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/coreCryptoWrapper/coreCryptoWrapper.ts @@ -209,7 +209,7 @@ export class CoreCryptoWrapper implements CryptoClient { } async create(nbPrekeys: number, entropy?: Uint8Array) { - if (entropy) { + if (entropy !== undefined) { await this.coreCrypto.reseedRng(entropy); } await this.init(); diff --git a/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/cryptoboxWrapper.ts b/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/cryptoboxWrapper.ts index b3a1c305cfb..d441344f618 100644 --- a/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/cryptoboxWrapper.ts +++ b/libraries/core/src/messagingProtocols/proteus/proteusService/cryptoClient/cryptoboxWrapper.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import {PreKey} from '@wireapp/api-client/lib/auth'; import {Cryptobox} from '@wireapp/cryptobox'; @@ -79,7 +80,7 @@ export class CryptoboxWrapper implements CryptoClient { } return {id: -1, key: ''}; }) - .filter(serializedPreKey => serializedPreKey.key); + .filter(serializedPreKey => is.nonEmptyString(serializedPreKey.key)); return { prekeys, diff --git a/libraries/core/src/messagingProtocols/proteus/utility/getGenericMessageParams.ts b/libraries/core/src/messagingProtocols/proteus/utility/getGenericMessageParams.ts index eaf300fe8d1..474c206819b 100644 --- a/libraries/core/src/messagingProtocols/proteus/utility/getGenericMessageParams.ts +++ b/libraries/core/src/messagingProtocols/proteus/utility/getGenericMessageParams.ts @@ -17,6 +17,7 @@ * */ +import is from '@sindresorhus/is'; import {APIClient} from '@wireapp/api-client/lib/apiClient'; import {QualifiedUserClients} from '@wireapp/api-client/lib/conversation'; import {QualifiedId, QualifiedUserPreKeyBundleMap} from '@wireapp/api-client/lib/user'; @@ -56,7 +57,7 @@ const getGenericMessageParams = async ({ apiClient, }: GetGenericMessageParamsParams): GetGenericMessageParamsReturnType => { const plainText = GenericMessage.encode(genericMessage).finish(); - if (targetMode !== MessageTargetMode.NONE && !userIds) { + if (targetMode !== MessageTargetMode.NONE && is.nullOrUndefined(userIds)) { throw new Error('Cannot send targetted message when no userIds are given'); } diff --git a/libraries/core/src/messagingProtocols/proteus/utility/sessionHandler/sessionHandler.ts b/libraries/core/src/messagingProtocols/proteus/utility/sessionHandler/sessionHandler.ts index c692db6d60c..95d5f593137 100644 --- a/libraries/core/src/messagingProtocols/proteus/utility/sessionHandler/sessionHandler.ts +++ b/libraries/core/src/messagingProtocols/proteus/utility/sessionHandler/sessionHandler.ts @@ -68,7 +68,7 @@ const parseSessionId = (sessionId: string): SessionId => { // see https://regex101.com/r/c8FtCw/1 const regex = /((?.+)@)?(?.+)@(?.+)$/g; const match = regex.exec(sessionId); - if (!match || !isSessionId(match.groups)) { + if (match === null || !isSessionId(match.groups)) { throw new Error(`given session id "${sessionId}" has wrong format`); } return match.groups; @@ -92,9 +92,10 @@ const initSession = async ( {userId, clientId, initialPrekey}: {userId: QualifiedId; clientId: string; initialPrekey?: PreKey}, {cryptoClient, apiClient}: {apiClient: APIClient; cryptoClient: CryptoClient}, ): Promise => { - const recipients = initialPrekey - ? {[userId.domain]: {[userId.id]: {[clientId]: initialPrekey}}} - : {[userId.domain]: {[userId.id]: [clientId]}}; + const recipients = + initialPrekey !== undefined + ? {[userId.domain]: {[userId.id]: {[clientId]: initialPrekey}}} + : {[userId.domain]: {[userId.id]: [clientId]}}; const {sessions} = await initSessions({ recipients, apiClient, @@ -241,7 +242,7 @@ const createSessionsFromPreKeys = async ({ const sessionId = constructSessionId({userId: {id: userId, domain}, clientId}); const prekey = userClients[clientId]; - if (!prekey) { + if (prekey === null || prekey === undefined) { const domainUnknowns = unknowns[domain] ?? {}; domainUnknowns[userId] = domainUnknowns[userId] ?? []; domainUnknowns[userId].push(clientId); diff --git a/libraries/core/src/notification/notificationService.ts b/libraries/core/src/notification/notificationService.ts index db38a2f05be..a6ecf78fd3f 100644 --- a/libraries/core/src/notification/notificationService.ts +++ b/libraries/core/src/notification/notificationService.ts @@ -104,7 +104,7 @@ export class NotificationService extends TypedEventEmitter { public async hasHistory(): Promise { const notificationEvents = await this.getNotificationEventList(); - return !!notificationEvents.length; + return notificationEvents.length > 0; } public getNotificationEventList(): Promise { diff --git a/libraries/core/src/secretStore/encryptedStore.ts b/libraries/core/src/secretStore/encryptedStore.ts index 3dbf5708452..ebdf94ddae0 100644 --- a/libraries/core/src/secretStore/encryptedStore.ts +++ b/libraries/core/src/secretStore/encryptedStore.ts @@ -62,7 +62,7 @@ export class EncryptedStore { async getSecretValue(primaryKey: string) { const result = await this.db.get('secrets', primaryKey); - if (!result) { + if (result === undefined) { return undefined; } return this.#decrypt(result); @@ -123,7 +123,7 @@ export async function createEncryptedStore(dbName: string): Promise { - if (!userIds.length) { + if (userIds.length === 0) { return []; } return this.apiClient.api.user.postListUsers({qualified_ids: userIds}); diff --git a/libraries/core/src/util/lowPrecisionTaskScheduler/lowPrecisionTaskScheduler.ts b/libraries/core/src/util/lowPrecisionTaskScheduler/lowPrecisionTaskScheduler.ts index 2f8eee0a371..bad66f1a9bf 100644 --- a/libraries/core/src/util/lowPrecisionTaskScheduler/lowPrecisionTaskScheduler.ts +++ b/libraries/core/src/util/lowPrecisionTaskScheduler/lowPrecisionTaskScheduler.ts @@ -39,11 +39,11 @@ const intervals: Record { const existingIntervalId = intervals[intervalDelay]?.timeoutId; - if (existingIntervalId) { + if (existingIntervalId !== undefined) { clearInterval(existingIntervalId); } - const tasks = intervals[intervalDelay]?.tasks || {}; + const tasks = intervals[intervalDelay]?.tasks ?? {}; tasks[key] = {firingDate, task}; diff --git a/libraries/core/src/util/taskScheduler/taskScheduler.ts b/libraries/core/src/util/taskScheduler/taskScheduler.ts index 47e91509e6e..336b528e89c 100644 --- a/libraries/core/src/util/taskScheduler/taskScheduler.ts +++ b/libraries/core/src/util/taskScheduler/taskScheduler.ts @@ -47,7 +47,7 @@ const addTask = ({task, firingDate, key, persist = false}: ScheduleTaskParams) = if (TaskSchedulerStore.has(key)) { TaskSchedulerStore.remove(key); } - if (activeTimeouts[key]) { + if (activeTimeouts[key] !== undefined) { cancelTask(key); } @@ -77,7 +77,7 @@ const addTask = ({task, firingDate, key, persist = false}: ScheduleTaskParams) = */ const cancelTask = (key: string) => { const timeout = activeTimeouts[key]; - if (timeout) { + if (timeout !== undefined) { clearTimeout(timeout); delete activeTimeouts[key]; logger.info(`Scheduled task with key "${key}" prematurely cleared`);