2023-01-03 19:55:46 +00:00
|
|
|
// Copyright 2019 Signal Messenger, LLC
|
2020-12-04 20:41:40 +00:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import { createSelector } from 'reselect';
|
2021-09-09 16:29:01 +00:00
|
|
|
import { isInteger } from 'lodash';
|
2020-12-04 20:41:40 +00:00
|
|
|
|
2021-06-01 20:45:43 +00:00
|
|
|
import { ITEM_NAME as UNIVERSAL_EXPIRE_TIMER_ITEM } from '../../util/universalExpireTimer';
|
2022-10-20 21:02:22 +00:00
|
|
|
import { innerIsBucketValueEnabled } from '../../RemoteConfig';
|
2022-01-26 23:05:26 +00:00
|
|
|
import type { ConfigKeyType, ConfigMapType } from '../../RemoteConfig';
|
2021-10-26 19:15:33 +00:00
|
|
|
import type { StateType } from '../reducer';
|
|
|
|
import type { ItemsStateType } from '../ducks/items';
|
|
|
|
import type {
|
2021-06-02 21:05:09 +00:00
|
|
|
ConversationColorType,
|
|
|
|
CustomColorType,
|
|
|
|
} from '../../types/Colors';
|
2023-08-10 16:43:33 +00:00
|
|
|
import type { AciString } from '../../types/ServiceId';
|
2021-10-26 19:15:33 +00:00
|
|
|
import { DEFAULT_CONVERSATION_COLOR } from '../../types/Colors';
|
2021-09-15 18:59:51 +00:00
|
|
|
import { getPreferredReactionEmoji as getPreferredReactionEmojiFromStoredValue } from '../../reactions/preferredReactionEmoji';
|
2023-11-01 20:35:55 +00:00
|
|
|
import { DurationInSeconds } from '../../util/durations';
|
2023-07-20 03:14:08 +00:00
|
|
|
import * as Bytes from '../../Bytes';
|
2023-11-02 19:42:31 +00:00
|
|
|
import { contactByEncryptedUsernameRoute } from '../../util/signalRoutes';
|
2020-12-04 20:41:40 +00:00
|
|
|
|
2021-10-12 23:59:08 +00:00
|
|
|
const DEFAULT_PREFERRED_LEFT_PANE_WIDTH = 320;
|
|
|
|
|
2020-12-04 20:41:40 +00:00
|
|
|
export const getItems = (state: StateType): ItemsStateType => state.items;
|
2022-05-06 19:02:44 +00:00
|
|
|
|
2021-11-30 16:29:57 +00:00
|
|
|
export const getAreWeASubscriber = createSelector(
|
|
|
|
getItems,
|
|
|
|
({ areWeASubscriber }: Readonly<ItemsStateType>): boolean =>
|
|
|
|
Boolean(areWeASubscriber)
|
|
|
|
);
|
|
|
|
|
2020-12-04 20:41:40 +00:00
|
|
|
export const getUserAgent = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): string => state.userAgent as string
|
|
|
|
);
|
|
|
|
|
|
|
|
export const getPinnedConversationIds = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): Array<string> =>
|
|
|
|
(state.pinnedConversationIds || []) as Array<string>
|
|
|
|
);
|
2021-06-01 20:45:43 +00:00
|
|
|
|
|
|
|
export const getUniversalExpireTimer = createSelector(
|
|
|
|
getItems,
|
2022-11-16 20:18:02 +00:00
|
|
|
(state: ItemsStateType): DurationInSeconds =>
|
|
|
|
DurationInSeconds.fromSeconds(state[UNIVERSAL_EXPIRE_TIMER_ITEM] || 0)
|
2021-06-01 20:45:43 +00:00
|
|
|
);
|
2021-06-02 21:05:09 +00:00
|
|
|
|
2023-04-14 18:16:28 +00:00
|
|
|
export const isRemoteConfigFlagEnabled = (
|
2022-01-26 23:05:26 +00:00
|
|
|
config: Readonly<ConfigMapType>,
|
|
|
|
key: ConfigKeyType
|
|
|
|
): boolean => Boolean(config[key]?.enabled);
|
|
|
|
|
2022-10-20 21:02:22 +00:00
|
|
|
// See isBucketValueEnabled in RemoteConfig.ts
|
2023-12-07 23:59:54 +00:00
|
|
|
export const isRemoteConfigBucketEnabled = (
|
2022-10-20 21:02:22 +00:00
|
|
|
config: Readonly<ConfigMapType>,
|
|
|
|
name: ConfigKeyType,
|
|
|
|
e164: string | undefined,
|
2023-08-16 20:54:39 +00:00
|
|
|
aci: AciString | undefined
|
2022-10-20 21:02:22 +00:00
|
|
|
): boolean => {
|
|
|
|
const flagValue = config[name]?.value;
|
2023-08-16 20:54:39 +00:00
|
|
|
return innerIsBucketValueEnabled(name, flagValue, e164, aci);
|
2022-10-20 21:02:22 +00:00
|
|
|
};
|
|
|
|
|
2023-02-28 22:17:22 +00:00
|
|
|
export const getRemoteConfig = createSelector(
|
2021-11-01 19:13:35 +00:00
|
|
|
getItems,
|
2022-01-26 23:05:26 +00:00
|
|
|
(state: ItemsStateType): ConfigMapType => state.remoteConfig || {}
|
2021-11-01 19:13:35 +00:00
|
|
|
);
|
|
|
|
|
2023-07-13 19:06:42 +00:00
|
|
|
export const getServerTimeSkew = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): number => state.serverTimeSkew || 0
|
|
|
|
);
|
|
|
|
|
2021-11-01 19:13:35 +00:00
|
|
|
export const getUsernamesEnabled = createSelector(
|
|
|
|
getRemoteConfig,
|
2022-01-26 23:05:26 +00:00
|
|
|
(remoteConfig: ConfigMapType): boolean =>
|
|
|
|
isRemoteConfigFlagEnabled(remoteConfig, 'desktop.usernames')
|
|
|
|
);
|
|
|
|
|
2023-02-13 18:51:41 +00:00
|
|
|
export const getHasCompletedUsernameOnboarding = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean =>
|
|
|
|
Boolean(state.hasCompletedUsernameOnboarding)
|
|
|
|
);
|
|
|
|
|
2023-07-20 03:14:08 +00:00
|
|
|
export const getHasCompletedUsernameLinkOnboarding = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean =>
|
|
|
|
Boolean(state.hasCompletedUsernameLinkOnboarding)
|
|
|
|
);
|
|
|
|
|
2023-11-03 22:05:11 +00:00
|
|
|
export const getUsernameCorrupted = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state.usernameCorrupted)
|
|
|
|
);
|
|
|
|
|
|
|
|
export const getUsernameLinkCorrupted = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state.usernameLinkCorrupted)
|
|
|
|
);
|
|
|
|
|
2023-07-20 03:14:08 +00:00
|
|
|
export const getUsernameLinkColor = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): number | undefined => state.usernameLinkColor
|
|
|
|
);
|
|
|
|
|
|
|
|
export const getUsernameLink = createSelector(
|
|
|
|
getItems,
|
|
|
|
({ usernameLink }: ItemsStateType): string | undefined => {
|
|
|
|
if (!usernameLink) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
const { entropy, serverId } = usernameLink;
|
|
|
|
|
|
|
|
if (!entropy.length || !serverId.length) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
const content = Bytes.concatenate([entropy, serverId]);
|
|
|
|
|
2023-11-02 19:42:31 +00:00
|
|
|
return contactByEncryptedUsernameRoute
|
|
|
|
.toWebUrl({ encryptedUsername: Bytes.toBase64(content) })
|
|
|
|
.toString();
|
2023-07-20 03:14:08 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-11-22 22:33:15 +00:00
|
|
|
export const isInternalUser = createSelector(
|
|
|
|
getRemoteConfig,
|
|
|
|
(remoteConfig: ConfigMapType): boolean => {
|
|
|
|
return isRemoteConfigFlagEnabled(remoteConfig, 'desktop.internalUser');
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2022-10-20 21:02:22 +00:00
|
|
|
// Note: ts/util/stories is the other place this check is done
|
2022-03-04 21:14:52 +00:00
|
|
|
export const getStoriesEnabled = createSelector(
|
2022-07-20 00:47:05 +00:00
|
|
|
getItems,
|
2023-12-07 23:59:54 +00:00
|
|
|
(state: ItemsStateType): boolean => !state.hasStoriesDisabled
|
2022-03-04 21:14:52 +00:00
|
|
|
);
|
|
|
|
|
2021-06-02 21:05:09 +00:00
|
|
|
export const getDefaultConversationColor = createSelector(
|
|
|
|
getItems,
|
|
|
|
(
|
|
|
|
state: ItemsStateType
|
|
|
|
): {
|
|
|
|
color: ConversationColorType;
|
|
|
|
customColorData?: {
|
|
|
|
id: string;
|
|
|
|
value: CustomColorType;
|
|
|
|
};
|
2021-06-08 21:31:35 +00:00
|
|
|
} => state.defaultConversationColor ?? DEFAULT_CONVERSATION_COLOR
|
2021-06-02 21:05:09 +00:00
|
|
|
);
|
2021-08-18 20:08:14 +00:00
|
|
|
|
|
|
|
export const getCustomColors = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): Record<string, CustomColorType> | undefined =>
|
|
|
|
state.customColors?.colors
|
|
|
|
);
|
2021-09-09 16:29:01 +00:00
|
|
|
|
|
|
|
export const getEmojiSkinTone = createSelector(
|
|
|
|
getItems,
|
|
|
|
({ skinTone }: Readonly<ItemsStateType>): number =>
|
|
|
|
typeof skinTone === 'number' &&
|
|
|
|
isInteger(skinTone) &&
|
|
|
|
skinTone >= 0 &&
|
|
|
|
skinTone <= 5
|
|
|
|
? skinTone
|
|
|
|
: 0
|
|
|
|
);
|
|
|
|
|
2021-10-12 23:59:08 +00:00
|
|
|
export const getPreferredLeftPaneWidth = createSelector(
|
|
|
|
getItems,
|
|
|
|
({ preferredLeftPaneWidth }: Readonly<ItemsStateType>): number =>
|
|
|
|
typeof preferredLeftPaneWidth === 'number' &&
|
|
|
|
Number.isInteger(preferredLeftPaneWidth)
|
|
|
|
? preferredLeftPaneWidth
|
|
|
|
: DEFAULT_PREFERRED_LEFT_PANE_WIDTH
|
|
|
|
);
|
|
|
|
|
2021-09-09 16:29:01 +00:00
|
|
|
export const getPreferredReactionEmoji = createSelector(
|
|
|
|
getItems,
|
2021-09-09 23:47:30 +00:00
|
|
|
getEmojiSkinTone,
|
|
|
|
(state: Readonly<ItemsStateType>, skinTone: number): Array<string> =>
|
|
|
|
getPreferredReactionEmojiFromStoredValue(
|
|
|
|
state.preferredReactionEmoji,
|
|
|
|
skinTone
|
|
|
|
)
|
2021-09-09 16:29:01 +00:00
|
|
|
);
|
2022-06-20 18:26:31 +00:00
|
|
|
|
|
|
|
export const getHideMenuBar = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state['hide-menu-bar'])
|
|
|
|
);
|
2022-08-23 17:24:55 +00:00
|
|
|
|
|
|
|
export const getHasSetMyStoriesPrivacy = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state.hasSetMyStoriesPrivacy)
|
|
|
|
);
|
2022-08-31 16:11:14 +00:00
|
|
|
|
|
|
|
export const getHasReadReceiptSetting = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state['read-receipt-setting'])
|
|
|
|
);
|
2022-10-25 22:18:42 +00:00
|
|
|
|
|
|
|
export const getHasStoryViewReceiptSetting = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean =>
|
|
|
|
Boolean(
|
|
|
|
state.storyViewReceiptsEnabled ?? state['read-receipt-setting'] ?? false
|
|
|
|
)
|
|
|
|
);
|
2023-01-18 23:31:10 +00:00
|
|
|
|
|
|
|
export const getRemoteBuildExpiration = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): number | undefined =>
|
2023-02-01 18:29:38 +00:00
|
|
|
state.remoteBuildExpiration === undefined
|
|
|
|
? undefined
|
|
|
|
: Number(state.remoteBuildExpiration)
|
2023-01-18 23:31:10 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
export const getAutoDownloadUpdate = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean =>
|
|
|
|
Boolean(state['auto-download-update'] ?? true)
|
|
|
|
);
|
2023-04-14 18:16:28 +00:00
|
|
|
|
|
|
|
export const getTextFormattingEnabled = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state.textFormatting ?? true)
|
|
|
|
);
|
2023-08-09 00:53:06 +00:00
|
|
|
|
|
|
|
export const getNavTabsCollapsed = createSelector(
|
|
|
|
getItems,
|
|
|
|
(state: ItemsStateType): boolean => Boolean(state.navTabsCollapsed ?? false)
|
|
|
|
);
|