Remove isDeepEqual and import directly from Lodash

This commit is contained in:
Flavien Bonvin 2025-12-30 15:09:31 +01:00
parent 3f39b5966c
commit ea85ff5585
52 changed files with 82 additions and 76 deletions

View file

@ -2,6 +2,7 @@ import type { MutableRefObject, ReactNode, RefObject } from 'react';
import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
import isDeepEqual from 'lodash/isEqual';
import merge from 'lodash/merge';
import type { ChallengeRef, ChallengeResult } from '@proton/components/containers/challenge/interface';
@ -19,7 +20,6 @@ import {
usernameLengthValidator,
usernameStartCharacterValidator,
} from '@proton/shared/lib/helpers/formValidators';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isTruthy from '@proton/utils/isTruthy';
import noop from '@proton/utils/noop';

View file

@ -2,6 +2,7 @@ import type { ComponentProps, Dispatch, MutableRefObject, ReactNode, SetStateAct
import { Fragment, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { Button } from '@proton/atoms/Button/Button';
@ -69,7 +70,6 @@ import { getSilentApi } from '@proton/shared/lib/api/helpers/customConfig';
import { TelemetryAccountSignupEvents } from '@proton/shared/lib/api/telemetry';
import type { ActiveSession } from '@proton/shared/lib/authentication/persistedSessionHelper';
import { APPS, BRAND_NAME, DRIVE_APP_NAME, PASS_APP_NAME, SSO_PATHS } from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { getPrivacyPolicyURL } from '@proton/shared/lib/helpers/url';
import type { Api, VPNServersCountData } from '@proton/shared/lib/interfaces';
import { Audience } from '@proton/shared/lib/interfaces';

View file

@ -1,3 +1,5 @@
import isDeepEqual from 'lodash/isEqual';
import { canBuyPassLifetime } from '@proton/components/containers/payments/subscription/subscriptionEligbility';
import {
type ADDON_NAMES,
@ -52,7 +54,6 @@ import { partnerWhitelist } from '@proton/shared/lib/api/partner';
import type { ResumedSessionResult } from '@proton/shared/lib/authentication/persistedSessionHelper';
import type { APP_NAMES } from '@proton/shared/lib/constants';
import { APPS } from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { Api, Organization, User } from '@proton/shared/lib/interfaces';
import { Audience } from '@proton/shared/lib/interfaces';
import { getOrganization } from '@proton/shared/lib/organization/api';

View file

@ -1,6 +1,7 @@
import type { Dispatch, ReactElement, ReactNode, SetStateAction } from 'react';
import { Fragment, useEffect, useRef, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c, msgid } from 'ttag';
import { Button } from '@proton/atoms/Button/Button';
@ -81,7 +82,6 @@ import {
VPN_CONNECTIONS,
VPN_SHORT_APP_NAME,
} from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { captureMessage } from '@proton/shared/lib/helpers/sentry';
import { getSentryError } from '@proton/shared/lib/keys';
import { generatePassword } from '@proton/shared/lib/password';

View file

@ -1,6 +1,7 @@
import type { Dispatch, ReactElement, ReactNode, SetStateAction } from 'react';
import { Fragment, useEffect, useRef, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c, msgid } from 'ttag';
import { Button } from '@proton/atoms/Button/Button';
@ -81,7 +82,6 @@ import {
VPN_CONNECTIONS,
VPN_SHORT_APP_NAME,
} from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { captureMessage } from '@proton/shared/lib/helpers/sentry';
import { getSentryError } from '@proton/shared/lib/keys';
import { generatePassword } from '@proton/shared/lib/password';

View file

@ -1,5 +1,6 @@
import { useLocation } from 'react-router-dom';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { orderAddresses } from '@proton/account/addresses/actions';
@ -10,7 +11,6 @@ import SelectTwo from '@proton/components/components/selectTwo/SelectTwo';
import { getStatus } from '@proton/components/containers/addresses/helper';
import { useLoading } from '@proton/hooks/index';
import { useDispatch } from '@proton/redux-shared-store/sharedProvider';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { Address } from '@proton/shared/lib/interfaces';
import { getIsNonDefault, sortAddresses } from '@proton/shared/lib/mail/addresses';
import { isMailVersionOlderThan } from '@proton/shared/lib/mobile/isMailVersionOlderThan';

View file

@ -1,10 +1,11 @@
import isDeepEqual from 'lodash/isEqual';
import { ICAL_METHOD } from '@proton/shared/lib/calendar/constants';
import { getBase64SharedSessionKey } from '@proton/shared/lib/calendar/crypto/keys/helpers';
import { getSupportedStringValue } from '@proton/shared/lib/calendar/icsSurgery/vcal';
import { getInviteVeventWithUpdatedParstats } from '@proton/shared/lib/calendar/mailIntegration/invite';
import { getPropertyTzid } from '@proton/shared/lib/calendar/vcalHelper';
import { getIsAllDay } from '@proton/shared/lib/calendar/veventHelper';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { omit } from '@proton/shared/lib/helpers/object';
import type { RequireSome } from '@proton/shared/lib/interfaces';
import type { SyncMultipleApiResponse, VcalVeventComponent } from '@proton/shared/lib/interfaces/calendar';

View file

@ -68,6 +68,7 @@
"history": "^4.10.1",
"idb": "^8.0.3",
"idb-keyval": "^6.2.2",
"lodash": "^4.17.21",
"mime-types": "^2.1.35",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@ -90,6 +91,7 @@
"@testing-library/react": "^15.0.7",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.6.1",
"@types/lodash": "^4.17.21",
"@types/mime-types": "^2.1.4",
"@types/react": "^18.3.27",
"@types/react-dom": "^18.3.7",

View file

@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import isEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useSubscription } from '@proton/account/subscription/hooks';
@ -25,7 +26,6 @@ import {
sendRequestCollapsibleSidebarReport,
useLeftSidebarButton,
} from '@proton/shared/lib/helpers/collapsibleSidebar';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { getCanAddStorage } from '@proton/shared/lib/user/storage';
import clsx from '@proton/utils/clsx';

View file

@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import isEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useShallow } from 'zustand/react/shallow';
@ -26,7 +27,6 @@ import {
sendRequestCollapsibleSidebarReport,
useLeftSidebarButton,
} from '@proton/shared/lib/helpers/collapsibleSidebar';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { getCanAddStorage } from '@proton/shared/lib/user/storage';
import clsx from '@proton/utils/clsx';

View file

@ -1,6 +1,7 @@
import { useCallback, useEffect, useState } from 'react';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isEqual from 'lodash/isEqual';
import { getItem, removeItem, setItem } from '@proton/shared/lib/helpers/storage';
import { VolumeType } from '@proton/shared/lib/interfaces/drive/volume';

View file

@ -55,14 +55,14 @@ import { waitForSpace } from './spaces';
function* saveDirtyConversation(serializedConversation: SerializedConversation): SagaIterator {
console.log('Saga triggered: saveDirtyConversation', serializedConversation);
// Check if this is a ghost conversation - if so, skip saving to IndexedDB
const conversation: Conversation | undefined = yield select(selectConversationById(serializedConversation.id));
if (conversation?.ghost) {
console.log('saveDirtyConversation: Ghost conversation detected, skipping IndexedDB persistence');
return;
}
const dbApi: DbApi = yield getContext('dbApi');
yield call([dbApi, dbApi.updateConversation], serializedConversation, {
dirty: true,
@ -265,7 +265,7 @@ export function* pushConversation({ payload }: { payload: PushConversationReques
const type: ResourceType = 'conversation';
const { id: localId } = payload;
const priority = payload.priority || 'urgent';
// Check if phantom chat mode is enabled - if so, skip remote persistence
const isGhostChatMode: boolean = yield select((state: LumoState) => state.ghostChat?.isGhostChatMode || false);
if (isGhostChatMode) {

View file

@ -47,14 +47,14 @@ import { waitForSpace } from './spaces';
export function* saveDirtyMessage(serializedMessage: SerializedMessage): SagaIterator {
console.log('Saga triggered: saveDirtyMessage', serializedMessage);
// Check if this message belongs to a ghost conversation - if so, skip saving to IndexedDB
const conversation: Conversation | undefined = yield select(selectConversationById(serializedMessage.conversationId));
if (conversation?.ghost) {
console.log('saveDirtyMessage: Message belongs to ghost conversation, skipping IndexedDB persistence');
return;
}
const dbApi: DbApi = yield getContext('dbApi');
yield call([dbApi, dbApi.updateMessage], serializedMessage, {
dirty: true,

View file

@ -1,6 +1,7 @@
import type { ChangeEvent } from 'react';
import { useEffect, useMemo, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useUser } from '@proton/account/user/hooks';
@ -22,7 +23,6 @@ import { isCustomLabel } from '@proton/mail/helpers/location';
import { getRandomAccentColor } from '@proton/shared/lib/colors';
import { LABEL_TYPE, MAILBOX_LABEL_IDS, MAIL_UPSELL_PATHS } from '@proton/shared/lib/constants';
import { hasReachedLabelLimit } from '@proton/shared/lib/helpers/folder';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { normalize } from '@proton/shared/lib/helpers/string';
import type { Label } from '@proton/shared/lib/interfaces/Label';
import clsx from '@proton/utils/clsx';

View file

@ -4,6 +4,7 @@ import { useHistory } from 'react-router-dom';
import { add, fromUnixTime, getUnixTime, isAfter, isBefore, isEqual, sub } from 'date-fns';
import type { History } from 'history';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useAddresses } from '@proton/account/addresses/hooks';
@ -17,7 +18,6 @@ import { getHumanLabelID } from '@proton/mail/helpers/location';
import { useMailSettings } from '@proton/mail/store/mailSettings/hooks';
import { MAILBOX_LABEL_IDS } from '@proton/shared/lib/constants';
import { validateEmailAddress } from '@proton/shared/lib/helpers/email';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { omit } from '@proton/shared/lib/helpers/object';
import { changeSearchParams, getSearchParams } from '@proton/shared/lib/helpers/url';
import type { Recipient } from '@proton/shared/lib/interfaces/Address';

View file

@ -1,11 +1,12 @@
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import isDeepEqual from 'lodash/isEqual';
import { useConversationCounts, useGetConversationCounts, useGetMessageCounts, useMessageCounts } from '@proton/mail';
import { useFolders, useLabels } from '@proton/mail/store/labels/hooks';
import { CacheType } from '@proton/redux-utilities';
import { MAILBOX_LABEL_IDS } from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { omit } from '@proton/shared/lib/helpers/object';
import { captureMessage } from '@proton/shared/lib/helpers/sentry';
import type { Label, MailSettings } from '@proton/shared/lib/interfaces';

View file

@ -1,7 +1,8 @@
import type { MutableRefObject } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isEqual from 'lodash/isEqual';
import type { Filter, Sort } from '@proton/shared/lib/mail/search';
export interface MailboxFocusProps {

View file

@ -1,7 +1,7 @@
import type { DependencyList } from 'react';
import { useMemo, useRef } from 'react';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isDeepEqual from 'lodash/isEqual';
/**
* Special performance oriented useMemo which will store the previous value,

View file

@ -1,8 +1,8 @@
import type { PayloadAction } from '@reduxjs/toolkit';
import type { Draft } from 'immer';
import isDeepEqual from 'lodash/isEqual';
import { safeDecreaseCount, safeIncreaseCount } from '@proton/redux-utilities';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { toMap } from '@proton/shared/lib/helpers/object';
import type { Folder, Label } from '@proton/shared/lib/interfaces';
import type { Message, MessageMetadata } from '@proton/shared/lib/interfaces/mail/Message';

View file

@ -1,7 +1,8 @@
import isDeepEqual from 'lodash/isEqual';
import { getConversation, queryConversations } from '@proton/shared/lib/api/conversations';
import type { MailboxItemsQueryParams } from '@proton/shared/lib/api/mailbox';
import { getMessage, queryMessageMetadata } from '@proton/shared/lib/api/messages';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { omit, pick } from '@proton/shared/lib/helpers/object';
import type { Api } from '@proton/shared/lib/interfaces';
import { CUSTOM_VIEWS_LABELS } from '@proton/shared/lib/mail/constants';

View file

@ -1,6 +1,7 @@
import { flushSync } from 'react-dom';
import type { History, LocationDescriptorObject } from 'history';
import isDeepEqual from 'lodash/isEqual';
import type { ServiceWorkerClient } from 'proton-pass-web/app/ServiceWorker/client/client';
import { store } from 'proton-pass-web/app/Store/store';
import { B2BEvents } from 'proton-pass-web/lib/b2b';
@ -51,7 +52,6 @@ import {
} from '@proton/shared/lib/authentication/pathnameHelper';
import { APPS } from '@proton/shared/lib/constants';
import { stringToUint8Array } from '@proton/shared/lib/helpers/encoding';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { omit } from '@proton/shared/lib/helpers/object';
import { wait } from '@proton/shared/lib/helpers/promise';
import { setUID as setSentryUID } from '@proton/shared/lib/helpers/sentry';

View file

@ -1,7 +1,8 @@
import { useEffect } from 'react';
import isDeepEqual from 'lodash/isEqual';
import usePrevious from '@proton/hooks/usePrevious';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
export const useItemEffect = <T>(effect: (item: T) => void | (() => void), items: T[], otherDeps?: any[]) => {
const previous = usePrevious(items);

View file

@ -1,7 +1,8 @@
import { useEffect, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import usePrevious from '@proton/hooks/usePrevious';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
export const useAsyncValue = <T extends unknown>(promise: Promise<T>, dv: T) => {
const [v, s] = useState(dv);

View file

@ -1,7 +1,8 @@
import isDeepEqual from 'lodash/isEqual';
import { CryptoProxy } from '@proton/crypto';
import type { SharedStartListening } from '@proton/redux-shared-store-types';
import { CacheType } from '@proton/redux-utilities';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { Address, DecryptedAddressKey, DecryptedKey, Key } from '@proton/shared/lib/interfaces';
import noop from '@proton/utils/noop';

View file

@ -1,11 +1,11 @@
import { createSlice } from '@reduxjs/toolkit';
import isDeepEqual from 'lodash/isEqual';
import type { ProtonThunkArguments } from '@proton/redux-shared-store-types';
import { createAsyncModelThunk, handleAsyncModel, previousSelector } from '@proton/redux-utilities';
import { getIsMissingScopeError } from '@proton/shared/lib/api/helpers/apiErrorHelper';
import { getOrganizationKeys } from '@proton/shared/lib/api/organization';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { CachedOrganizationKey, Organization, OrganizationKey, UserModel } from '@proton/shared/lib/interfaces';
import type { CachedOrganizationKey, Organization, OrganizationKey, UserModel } from '@proton/shared/lib/interfaces';
import { getCachedOrganizationKey } from '@proton/shared/lib/keys';
import type { AddressKeysState } from '../addressKeys';
@ -20,12 +20,7 @@ import { type UserKeysState, userKeysThunk } from '../userKeys';
const name = 'organizationKey' as const;
export interface OrganizationKeyState
extends UserState,
OrganizationState,
UserKeysState,
AddressesState,
AddressKeysState,
MembersState {
extends UserState, OrganizationState, UserKeysState, AddressesState, AddressKeysState, MembersState {
[name]: ModelState<CachedOrganizationKey>;
}

View file

@ -1,9 +1,10 @@
import type { FormEvent } from 'react';
import { useMemo, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { getMailMappingErrors } from '@proton/activation/src/helpers/getMailMappingErrors';
import { useFolders, useLabels } from '@proton/mail';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { FolderMapItem, MailImportFields } from './CustomizeMailImportModal.interface';

View file

@ -1,6 +1,7 @@
import { useMemo, useState } from 'react';
import cloneDeep from 'lodash/cloneDeep';
import isDeepEqual from 'lodash/isEqual';
import { GMAIL_CATEGORIES, IMAPS } from '@proton/activation/src/constants';
import type { MailImportFolder } from '@proton/activation/src/helpers/MailImportFoldersParser/MailImportFoldersParser';
@ -17,7 +18,6 @@ import {
import { selectImapDraftMailImport } from '@proton/activation/src/logic/draft/imapDraft/imapDraft.selector';
import { useEasySwitchDispatch, useEasySwitchSelector } from '@proton/activation/src/logic/store';
import { useFolders, useLabels } from '@proton/mail';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { Address, Label, UserModel } from '@proton/shared/lib/interfaces';
import type { MailImportFields } from '../../../CustomizeMailImportModal/CustomizeMailImportModal.interface';

View file

@ -1,3 +1,4 @@
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { startImportTask } from '@proton/activation/src/api';
@ -12,7 +13,6 @@ import { createCalendar, updateCalendarUserSettings } from '@proton/shared/lib/a
import { setupCalendarKey } from '@proton/shared/lib/calendar/crypto/keys/setupCalendarKeys';
import { getRandomAccentColor } from '@proton/shared/lib/colors';
import { getTimezone } from '@proton/shared/lib/date/timezone';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { captureMessage } from '@proton/shared/lib/helpers/sentry';
import type { Address, Api } from '@proton/shared/lib/interfaces';
import type { Calendar } from '@proton/shared/lib/interfaces/calendar';

View file

@ -3,8 +3,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { Placement, Strategy, VirtualElement } from '@floating-ui/dom';
import { autoUpdate, computePosition, flip, hide, offset, shift, size } from '@floating-ui/dom';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isDeepEqual from 'lodash/isEqual';
import type { PopperArrow, PopperPlacement, PopperPosition } from './interface';
import { allPopperPlacements, arrowOffset, getClickRect, getFallbackPlacements, rtlPlacement } from './utils';

View file

@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c, msgid } from 'ttag';
import { orderAddresses } from '@proton/account/addresses/actions';
@ -39,7 +40,6 @@ import {
} from '@proton/shared/lib/constants';
import { getIsBYOEOnlyAccount } from '@proton/shared/lib/helpers/address';
import { textToClipboard } from '@proton/shared/lib/helpers/browser';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { getUpsellRef } from '@proton/shared/lib/helpers/upsell';
import { getKnowledgeBaseUrl } from '@proton/shared/lib/helpers/url';
import type { Address, CachedOrganizationKey, Member, UserModel } from '@proton/shared/lib/interfaces';

View file

@ -1,5 +1,7 @@
import { type ReactNode, useEffect, useRef, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { type ApiStatusState, apiStatusActions, defaultApiStatus } from '@proton/account/apiStatus';
import { selectUser } from '@proton/account/user';
import useAuthentication from '@proton/components/hooks/useAuthentication';
@ -8,7 +10,6 @@ import useNotifications from '@proton/components/hooks/useNotifications';
import { useDispatch, useStore } from '@proton/redux-shared-store/sharedProvider';
import type { ApiListenerCallback, ApiWithListener } from '@proton/shared/lib/api/createApi';
import { handleInvalidSession } from '@proton/shared/lib/authentication/logout';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import ApiModals from './ApiModals';
import ApiContext from './apiContext';

View file

@ -1,6 +1,7 @@
import type { ChangeEvent, FormEvent, ReactNode } from 'react';
import { useEffect, useMemo, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useUser } from '@proton/account/user/hooks';
@ -22,7 +23,6 @@ import {
hasReachedContactGroupMembersLimit,
} from '@proton/shared/lib/contacts/helpers/contactGroup';
import { validateEmailAddress } from '@proton/shared/lib/helpers/email';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { normalize } from '@proton/shared/lib/helpers/string';
import type { ContactEmail, ContactGroup } from '@proton/shared/lib/interfaces/contacts/Contact';
import clsx from '@proton/utils/clsx';

View file

@ -1,5 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import Form from '@proton/components/components/form/Form';
@ -19,7 +20,6 @@ import { useFolders, useLabels } from '@proton/mail';
import { addFilter, updateFilter } from '@proton/mail/store/filters/actions';
import { useFilters } from '@proton/mail/store/filters/hooks';
import { applyFilters } from '@proton/shared/lib/api/filters';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { AUTO_REPLY_CHARACTER_COUNT_LIMIT } from '@proton/shared/lib/mail/constants';
import { removeImagesFromContent } from '@proton/shared/lib/sanitize/purify';
import generateUID from '@proton/utils/generateUID';

View file

@ -1,4 +1,5 @@
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isDeepEqual from 'lodash/isEqual';
import { toMap } from '@proton/shared/lib/helpers/object';
import { fromSieveTree, toSieveTree } from '@proton/sieve';

View file

@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useUser } from '@proton/account/user/hooks';
@ -21,7 +22,6 @@ import { CacheType } from '@proton/redux-utilities';
import { orderLabels } from '@proton/shared/lib/api/labels';
import { MAIL_UPSELL_PATHS } from '@proton/shared/lib/constants';
import { hasReachedLabelLimit } from '@proton/shared/lib/helpers/folder';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { Label } from '@proton/shared/lib/interfaces';
import move from '@proton/utils/move';

View file

@ -1,8 +1,9 @@
import type { ReactNode, Reducer } from 'react';
import { useEffect, useReducer, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import useInstance from '@proton/hooks/useInstance';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import NotificationsChildrenContext from './childrenContext';
import type { Notification, NotificationOffset } from './interfaces';

View file

@ -1,6 +1,7 @@
import type { FormEvent, ReactNode, RefObject } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import isEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useUser } from '@proton/account/user/hooks';
@ -93,7 +94,6 @@ import type { ProductParam } from '@proton/shared/lib/apps/product';
import { getShouldCalendarPreventSubscripitionChange } from '@proton/shared/lib/calendar/plans';
import { APPS, type APP_NAMES } from '@proton/shared/lib/constants';
import { API_CUSTOM_ERROR_CODES } from '@proton/shared/lib/errors';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { captureMessage } from '@proton/shared/lib/helpers/sentry';
import type { Organization, RequireOnly, UserModel } from '@proton/shared/lib/interfaces';
import { getSentryError } from '@proton/shared/lib/keys';

View file

@ -1,6 +1,8 @@
import type { ReactNode } from 'react';
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState } from 'react';
import isDeepEqual from 'lodash/isEqual';
import type { APP_NAMES } from '@proton/shared/lib/constants';
import { APPS } from '@proton/shared/lib/constants';
import {
@ -13,7 +15,6 @@ import { clearBit, hasBit, setBit } from '@proton/shared/lib/helpers/bitset';
import { getCookie, setCookie } from '@proton/shared/lib/helpers/cookies';
import { isElectronMail, isElectronOnSupportedApps } from '@proton/shared/lib/helpers/desktop';
import { updateElectronThemeModeClassnames } from '@proton/shared/lib/helpers/initElectronClassnames';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import createListeners from '@proton/shared/lib/helpers/listeners';
import { getSecondLevelDomain } from '@proton/shared/lib/helpers/url';
import {

View file

@ -1,8 +1,9 @@
import type { DependencyList, RefObject } from 'react';
import { useRef } from 'react';
import isDeepEqual from 'lodash/isEqual';
import { isMac } from '@proton/shared/lib/helpers/browser';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import type { KeyboardKeyType } from '@proton/shared/lib/interfaces';
import { KeyboardKey } from '@proton/shared/lib/interfaces';
import isTruthy from '@proton/utils/isTruthy';

View file

@ -1,6 +1,7 @@
import { useCallback, useEffect, useState } from 'react';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isEqual from 'lodash/isEqual';
import { getItem, removeItem, setItem } from '@proton/shared/lib/helpers/storage';
import { VolumeType } from '@proton/shared/lib/interfaces/drive/volume';

View file

@ -1,6 +1,7 @@
import { useEffect, useMemo, useRef } from 'react';
import type { IDBPDatabase } from 'idb';
import isDeepEqual from 'lodash/isEqual';
import { c } from 'ttag';
import { useUser } from '@proton/account/user/hooks';
@ -13,7 +14,6 @@ import { storeESUserChoiceInboxDesktop } from '@proton/shared/lib/desktop/encryp
import { hasBit } from '@proton/shared/lib/helpers/bitset';
import { isFirefox } from '@proton/shared/lib/helpers/browser';
import { isElectronApp } from '@proton/shared/lib/helpers/desktop';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { wait } from '@proton/shared/lib/helpers/promise';
import {

View file

@ -1,5 +1,6 @@
import isDeepEqual from 'lodash/isEqual';
import type { ItemContent, ItemExtraField, ItemRevision, ItemType, Metadata } from '@proton/pass/types';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
type Diff<T extends object> = Partial<Record<keyof T, boolean>>;

View file

@ -1,6 +1,5 @@
import type { Selector } from '@reduxjs/toolkit';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isDeepEqual from 'lodash/isEqual';
import type {
MaybeOptimisticStateObject,

View file

@ -1,6 +1,6 @@
import type { Selector } from '@reduxjs/toolkit';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isDeepEqual from 'lodash/isEqual';
import type {
MaybeOptimisticStateObject,

View file

@ -1,3 +1,4 @@
import isDeepEqual from 'lodash/isEqual';
import type { Reducer } from 'redux';
import {
@ -12,25 +13,14 @@ import {
userEvent,
userRefresh,
} from '@proton/pass/store/actions';
import {
confirmPendingAuthDevice,
getAuthDevices,
rejectPendingAuthDevice,
} from '@proton/pass/store/actions/creators/sso';
import type {
BitField,
MaybeNull,
PassPlanResponse,
RequiredNonNull,
UserMonitorStatusResponse,
} from '@proton/pass/types';
import { confirmPendingAuthDevice, getAuthDevices, rejectPendingAuthDevice } from '@proton/pass/store/actions/creators/sso';
import type { BitField, MaybeNull, PassPlanResponse, RequiredNonNull, UserMonitorStatusResponse } from '@proton/pass/types';
import { EventActions } from '@proton/pass/types';
import type { PassFeature } from '@proton/pass/types/api/features';
import { or } from '@proton/pass/utils/fp/predicates';
import { objectDelete } from '@proton/pass/utils/object/delete';
import { merge, partialMerge } from '@proton/pass/utils/object/merge';
import { SETTINGS_PROTON_SENTINEL_STATE } from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import updateCollection from '@proton/shared/lib/helpers/updateCollection';
import type { Address, SETTINGS_PASSWORD_MODE, SETTINGS_STATUS, User } from '@proton/shared/lib/interfaces';
import type { AuthDeviceOutput } from '@proton/shared/lib/keys/device';
@ -119,8 +109,7 @@ const reducer: Reducer<UserState> = (state = getInitialState(), action) => {
: state.userSettings;
const addresses = Addresses.reduce(
(acc, { Action, ID, Address }) =>
Action === EventActions.DELETE ? objectDelete(acc, ID) : merge(acc, { [ID]: Address }),
(acc, { Action, ID, Address }) => (Action === EventActions.DELETE ? objectDelete(acc, ID) : merge(acc, { [ID]: Address })),
state.addresses
);

View file

@ -1,9 +1,9 @@
import { addWeeks, fromUnixTime, isAfter, isBefore, subWeeks } from 'date-fns';
import isDeepEqual from 'lodash/isEqual';
import type { ProductParam } from '@proton/shared/lib/apps/product';
import { APPS } from '@proton/shared/lib/constants';
import { hasBit } from '@proton/shared/lib/helpers/bitset';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { Audience, type Organization, type UserModel } from '@proton/shared/lib/interfaces';
import {

View file

@ -1,4 +1,5 @@
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import isEqual from 'lodash/isEqual';
import isFunction from '@proton/utils/isFunction';
import isTruthy from '@proton/utils/isTruthy';

View file

@ -1,8 +1,9 @@
import isDeepEqual from 'lodash/isEqual';
import shallowEqual from '@proton/utils/shallowEqual';
import { isSameDay } from '../../date-fns-utc';
import { toUTCDate } from '../../date/timezone';
import isDeepEqual from '../../helpers/isDeepEqual';
import { omit } from '../../helpers/object';
import type {
VcalDateOrDateTimeValue,

View file

@ -1,6 +1,6 @@
import isDeepEqual from 'lodash/isEqual';
import { addLocale as ttagAddLocale, useLocale as ttagUseLocale } from 'ttag';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { pick } from '@proton/shared/lib/helpers/object';
import { DEFAULT_LOCALE } from '../constants';

View file

@ -1,11 +1,11 @@
import { useCallback, useEffect } from 'react';
import { createSelector } from '@reduxjs/toolkit';
import isEqual from 'lodash/isEqual';
import usePrevious from '@proton/hooks/usePrevious';
import { baseUseSelector } from '@proton/react-redux-store';
import { createHooks } from '@proton/redux-utilities';
import isEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { pick } from '@proton/shared/lib/helpers/object';
import { apiWalletTransactionDataThunk, selectApiWalletTransactionData } from '../slices';

View file

@ -1,6 +1,7 @@
import isDeepEqual from 'lodash/isEqual';
import { CacheType } from '@proton/redux-utilities';
import { MINUTE } from '@proton/shared/lib/constants';
import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
import { wait } from '@proton/shared/lib/helpers/promise';
import { selectExchangeRate } from '../slices';

View file

@ -37931,6 +37931,7 @@ __metadata:
"@testing-library/react": "npm:^15.0.7"
"@testing-library/react-hooks": "npm:^8.0.1"
"@testing-library/user-event": "npm:^14.6.1"
"@types/lodash": "npm:^4.17.21"
"@types/mime-types": "npm:^2.1.4"
"@types/react": "npm:^18.3.27"
"@types/react-dom": "npm:^18.3.7"
@ -37955,6 +37956,7 @@ __metadata:
jest: "npm:^30.2.0"
jest-junit: "npm:^16.0.0"
jest-when: "npm:3.7.0"
lodash: "npm:^4.17.21"
mime-types: "npm:^2.1.35"
path-browserify: "npm:^1.0.1"
prettier: "npm:^3.7.4"