From 0e286142f1e21c1d4b71a1b1abf60df55f697436 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 24 Apr 2023 14:34:04 +1000 Subject: [PATCH] chore: add a redux settings slice, currently outdated banner inc --- ts/components/SessionInboxView.tsx | 24 ++---- ts/components/conversation/TypingBubble.tsx | 10 +-- .../composition/CompositionBox.tsx | 3 +- .../message-content/MessageContent.tsx | 10 +-- .../message/message-content/MessageText.tsx | 3 +- .../message-item/GroupUpdateMessage.tsx | 2 +- ts/components/dialog/EditProfileDialog.tsx | 2 + ts/components/leftpane/ActionsPanel.tsx | 12 ++- ts/components/lightbox/LightboxGallery.tsx | 2 +- .../registration/RegistrationStages.tsx | 7 +- .../settings/section/CategoryPrivacy.tsx | 12 +-- ts/data/data.ts | 10 +-- ts/data/settings-key.ts | 10 ++- ts/interactions/conversationInteractions.ts | 19 +++-- ts/mains/main_renderer.tsx | 14 ++-- ts/node/migration/sessionMigrations.ts | 9 +-- ts/node/storage_item.ts | 1 - ts/receiver/configMessage.ts | 31 +++++--- .../open_group_api/sogsv3/knownBlindedkeys.ts | 6 +- ts/session/apis/snode_api/hfHandling.ts | 32 +++++--- .../ExpirationTimerUpdateMessage.ts | 2 +- ts/session/sending/PendingMessageCache.ts | 6 +- ts/session/utils/job_runners/JobRunner.ts | 6 +- ts/session/utils/sync/syncUtils.ts | 4 +- ts/state/ducks/settings.tsx | 73 +++++++++++++++++++ ts/state/reducer.ts | 3 + ts/state/selectors/conversations.ts | 6 ++ ts/state/selectors/selectedConversation.ts | 4 +- ts/state/selectors/settings.ts | 19 +++++ ts/util/accountManager.ts | 4 +- ts/util/blockedNumberController.ts | 6 +- ts/util/storage.ts | 11 ++- .../node/libsession/libsession.worker.ts | 1 - 33 files changed, 232 insertions(+), 132 deletions(-) create mode 100644 ts/state/ducks/settings.tsx create mode 100644 ts/state/selectors/settings.ts diff --git a/ts/components/SessionInboxView.tsx b/ts/components/SessionInboxView.tsx index df5126228..2c5ecb383 100644 --- a/ts/components/SessionInboxView.tsx +++ b/ts/components/SessionInboxView.tsx @@ -43,10 +43,11 @@ moment.locale((window.i18n as any).getLocale()); // Workaround: A react component's required properties are filtering up through connect() // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 import useUpdate from 'react-use/lib/useUpdate'; -import useInterval from 'react-use/lib/useInterval'; import { SettingsKey } from '../data/settings-key'; import { NoticeBanner } from './NoticeBanner'; import { Flex } from './basic/Flex'; +import { useHasDeviceOutdatedSyncing } from '../state/selectors/settings'; +import { getSettingsInitialState, updateAllOnStorageReady } from '../state/ducks/settings'; const StyledGutter = styled.div` width: 380px !important; @@ -60,7 +61,6 @@ function createSessionInboxStore() { .map(conversation => conversation.getConversationModelProps()); const timerOptions: TimerOptionsArray = ExpirationTimerOptions.getTimerSecondsWithName(); - const initialState: StateType = { conversations: { ...getEmptyConversationState(), @@ -83,6 +83,7 @@ function createSessionInboxStore() { stagedAttachments: getEmptyStagedAttachmentsState(), call: initialCallState, sogsRoomInfo: initialSogsRoomInfoState, + settings: getSettingsInitialState(), }; return createStore(initialState); @@ -91,29 +92,18 @@ function createSessionInboxStore() { function setupLeftPane(forceUpdateInboxComponent: () => void) { window.openConversationWithMessages = openConversationWithMessages; window.inboxStore = createSessionInboxStore(); + window.inboxStore.dispatch(updateAllOnStorageReady()); forceUpdateInboxComponent(); } const SomeDeviceOutdatedSyncingNotice = () => { - const forceUpdate = useUpdate(); - const isShown = Boolean(window.getSettingValue(SettingsKey.someDeviceOutdatedSyncing)); - - // it would be nice to get the settings into a redux slice in addition to their Storage location and keep them in sync. - // So we could just use a selector here. - useInterval(() => { - const shouldBeShown = Storage.get(SettingsKey.someDeviceOutdatedSyncing); - - if (!isShown && shouldBeShown) { - forceUpdate(); - } - }, 1000); + const outdatedBannerShouldBeShown = useHasDeviceOutdatedSyncing(); const dismiss = async () => { - await window.setSettingValue(SettingsKey.someDeviceOutdatedSyncing, false); - forceUpdate(); + await Storage.put(SettingsKey.someDeviceOutdatedSyncing, false); }; - if (!isShown) { + if (!outdatedBannerShouldBeShown) { return null; } return ( diff --git a/ts/components/conversation/TypingBubble.tsx b/ts/components/conversation/TypingBubble.tsx index 18e2e2059..d3dd01360 100644 --- a/ts/components/conversation/TypingBubble.tsx +++ b/ts/components/conversation/TypingBubble.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { TypingAnimation } from './TypingAnimation'; import styled from 'styled-components'; -import { ConversationTypeEnum, isOpenOrClosedGroup } from '../../models/conversationAttributes'; +import { ConversationTypeEnum } from '../../models/conversationAttributes'; +import { useSelectedIsGroup } from '../../state/selectors/selectedConversation'; interface TypingBubbleProps { conversationType: ConversationTypeEnum; @@ -22,11 +23,8 @@ const TypingBubbleContainer = styled.div` `; export const TypingBubble = (props: TypingBubbleProps) => { - if (isOpenOrClosedGroup(props.conversationType)) { - return null; - } - - if (!props.isTyping) { + const isOpenOrClosedGroup = useSelectedIsGroup(); + if (!isOpenOrClosedGroup || !props.isTyping) { return null; } diff --git a/ts/components/conversation/composition/CompositionBox.tsx b/ts/components/conversation/composition/CompositionBox.tsx index 53227762f..7a0547e5e 100644 --- a/ts/components/conversation/composition/CompositionBox.tsx +++ b/ts/components/conversation/composition/CompositionBox.tsx @@ -56,6 +56,7 @@ import { getSelectedConversation, getSelectedConversationKey, } from '../../../state/selectors/selectedConversation'; +import { SettingsKey } from '../../../data/settings-key'; export interface ReplyingToMessageProps { convoId: string; @@ -601,7 +602,7 @@ class CompositionBoxInner extends React.Component { private renderStagedLinkPreview(): JSX.Element | null { // Don't generate link previews if user has turned them off - if (!(window.getSettingValue('link-preview-setting') || false)) { + if (!(window.getSettingValue(SettingsKey.settingsLinkPreview) || false)) { return null; } diff --git a/ts/components/conversation/message/message-content/MessageContent.tsx b/ts/components/conversation/message/message-content/MessageContent.tsx index ecebf8408..13f23cc23 100644 --- a/ts/components/conversation/message/message-content/MessageContent.tsx +++ b/ts/components/conversation/message/message-content/MessageContent.tsx @@ -8,9 +8,9 @@ import styled, { css } from 'styled-components'; import { MessageModelType, MessageRenderingProps } from '../../../../models/messageType'; import { getMessageContentSelectorProps, - getMessageTextProps, getQuotedMessageToAnimate, getShouldHighlightMessage, + useMessageIsDeleted, } from '../../../../state/selectors/conversations'; import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer'; import { MessageAttachment } from './MessageAttachment'; @@ -96,6 +96,7 @@ export const MessageContent = (props: Props) => { const contentProps = useSelector(state => getMessageContentSelectorProps(state as any, props.messageId) ); + const isDeleted = useMessageIsDeleted(props.messageId); const [isMessageVisible, setMessageIsVisible] = useState(false); const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); @@ -149,13 +150,6 @@ export const MessageContent = (props: Props) => { const { direction, text, timestamp, serverTimestamp, previews } = contentProps; - const selectedMsg = useSelector(state => getMessageTextProps(state as any, props.messageId)); - - let isDeleted = false; - if (selectedMsg && selectedMsg.isDeleted !== undefined) { - isDeleted = selectedMsg.isDeleted; - } - const hasContentAfterAttachmentAndQuote = !isEmpty(previews) || !isEmpty(text); const toolTipTitle = moment(serverTimestamp || timestamp).format('llll'); diff --git a/ts/components/conversation/message/message-content/MessageText.tsx b/ts/components/conversation/message/message-content/MessageText.tsx index d84d1bfac..15ef56bd1 100644 --- a/ts/components/conversation/message/message-content/MessageText.tsx +++ b/ts/components/conversation/message/message-content/MessageText.tsx @@ -9,6 +9,7 @@ import { } from '../../../../state/selectors/conversations'; import { SessionIcon } from '../../../icon'; import { MessageBody } from './MessageBody'; +import { StateType } from '../../../../state/reducer'; type Props = { messageId: string; @@ -20,7 +21,7 @@ export type MessageTextSelectorProps = Pick< >; export const MessageText = (props: Props) => { - const selected = useSelector(state => getMessageTextProps(state as any, props.messageId)); + const selected = useSelector((state: StateType) => getMessageTextProps(state, props.messageId)); const multiSelectMode = useSelector(isMessageSelectionMode); if (!selected) { diff --git a/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx b/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx index 723555936..92d6939a3 100644 --- a/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx +++ b/ts/components/conversation/message/message-item/GroupUpdateMessage.tsx @@ -52,7 +52,7 @@ const ChangeItemLeft = (left: Array): string => { // tslint:disable-next-line: cyclomatic-complexity const ChangeItem = (change: PropsForGroupUpdateType): string => { - const type = change.type; + const { type } = change; switch (type) { case 'name': return window.i18n('titleIsNow', [change.newName || '']); diff --git a/ts/components/dialog/EditProfileDialog.tsx b/ts/components/dialog/EditProfileDialog.tsx index 2a2bb6e4f..cae2efedc 100644 --- a/ts/components/dialog/EditProfileDialog.tsx +++ b/ts/components/dialog/EditProfileDialog.tsx @@ -24,6 +24,7 @@ import { SessionWrapperModal } from '../SessionWrapperModal'; import { SessionButton, SessionButtonType } from '../basic/SessionButton'; import { SessionSpinner } from '../basic/SessionSpinner'; import { SessionIconButton } from '../icon'; +import { ConfigurationDumpSync } from '../../session/utils/job_runners/jobs/ConfigurationSyncDumpJob'; const handleSaveQRCode = (event: MouseEvent) => { event.preventDefault(); @@ -360,6 +361,7 @@ async function commitProfileEdits(newName: string, scaledAvatarUrl: string | nul if (window.sessionFeatureFlags.useSharedUtilForUserConfig) { await ConfigurationSync.queueNewJobIfNeeded(); + await ConfigurationDumpSync.queueNewJobIfNeeded(); await setLastProfileUpdateTimestamp(Date.now()); } else { await setLastProfileUpdateTimestamp(Date.now()); diff --git a/ts/components/leftpane/ActionsPanel.tsx b/ts/components/leftpane/ActionsPanel.tsx index 1a5cebc09..bb3169e6d 100644 --- a/ts/components/leftpane/ActionsPanel.tsx +++ b/ts/components/leftpane/ActionsPanel.tsx @@ -3,11 +3,7 @@ import { getConversationController } from '../../session/conversations'; import { syncConfigurationIfNeeded } from '../../session/utils/sync/syncUtils'; import { useDispatch, useSelector } from 'react-redux'; -import { - Data, - hasSyncedInitialConfigurationItem, - lastAvatarUploadTimestamp, -} from '../../data/data'; +import { Data } from '../../data/data'; import { getMessageQueue } from '../../session/sending'; // tslint:disable: no-submodule-imports import useInterval from 'react-use/lib/useInterval'; @@ -47,6 +43,7 @@ import { forceRefreshRandomSnodePool } from '../../session/apis/snode_api/snodeP import { isDarkTheme } from '../../state/selectors/theme'; import { ThemeStateType } from '../../themes/constants/colors'; import { switchThemeTo } from '../../themes/switchTheme'; +import { SettingsKey } from '../../data/settings-key'; const Section = (props: { type: SectionType }) => { const ourNumber = useSelector(getOurNumber); @@ -172,14 +169,15 @@ const triggerSyncIfNeeded = async () => { .get(us) .setIsApproved(true, true); const didWeHandleAConfigurationMessageAlready = - (await Data.getItemById(hasSyncedInitialConfigurationItem))?.value || false; + (await Data.getItemById(SettingsKey.hasSyncedInitialConfigurationItem))?.value || false; if (didWeHandleAConfigurationMessageAlready) { await syncConfigurationIfNeeded(); } }; const triggerAvatarReUploadIfNeeded = async () => { - const lastTimeStampAvatarUpload = (await Data.getItemById(lastAvatarUploadTimestamp))?.value || 0; + const lastTimeStampAvatarUpload = + (await Data.getItemById(SettingsKey.lastAvatarUploadTimestamp))?.value || 0; if (Date.now() - lastTimeStampAvatarUpload > DURATION.DAYS * 14) { window.log.info('Reuploading avatar...'); diff --git a/ts/components/lightbox/LightboxGallery.tsx b/ts/components/lightbox/LightboxGallery.tsx index f4441b95d..6361cd819 100644 --- a/ts/components/lightbox/LightboxGallery.tsx +++ b/ts/components/lightbox/LightboxGallery.tsx @@ -36,7 +36,7 @@ export const LightboxGallery = (props: Props) => { const selectedConversation = useSelectedConversationKey(); if (!selectedConversation) { - throw new Error('LightboxGallery: selectedConversation is undefined'); + return null; } const dispatch = useDispatch(); diff --git a/ts/components/registration/RegistrationStages.tsx b/ts/components/registration/RegistrationStages.tsx index 9985552a2..a8f422bfe 100644 --- a/ts/components/registration/RegistrationStages.tsx +++ b/ts/components/registration/RegistrationStages.tsx @@ -16,6 +16,7 @@ import { } from '../../util/accountManager'; import { fromHex } from '../../session/utils/String'; import { setSignInByLinking, setSignWithRecoveryPhrase, Storage } from '../../util/storage'; +import { SettingsKey } from '../../data/settings-key'; // tslint:disable: use-simple-attributes @@ -59,11 +60,7 @@ export async function signUp(signUpDetails: { try { await resetRegistration(); await registerSingleDevice(generatedRecoveryPhrase, 'english', trimName); - await Data.createOrUpdateItem({ - id: 'hasSyncedInitialConfigurationItem', - value: true, - timestamp: Date.now(), - }); + await Storage.put(SettingsKey.hasSyncedInitialConfigurationItem, Date.now()); await setSignWithRecoveryPhrase(false); trigger('openInbox'); } catch (e) { diff --git a/ts/components/settings/section/CategoryPrivacy.tsx b/ts/components/settings/section/CategoryPrivacy.tsx index b936ce390..d2490dad9 100644 --- a/ts/components/settings/section/CategoryPrivacy.tsx +++ b/ts/components/settings/section/CategoryPrivacy.tsx @@ -1,7 +1,6 @@ import React from 'react'; // tslint:disable-next-line: no-submodule-imports import useUpdate from 'react-use/lib/useUpdate'; -import { Data, hasLinkPreviewPopupBeenDisplayed } from '../../../data/data'; import { SettingsKey } from '../../../data/settings-key'; import { ConversationTypeEnum } from '../../../models/conversationAttributes'; import { updateConfirmModal } from '../../../state/ducks/modalDialog'; @@ -11,9 +10,10 @@ import { TypingBubble } from '../../conversation/TypingBubble'; import { SessionSettingButtonItem, SessionToggleWithDescription } from '../SessionSettingListItem'; import { displayPasswordModal } from '../SessionSettings'; +import { Storage } from '../../../util/storage'; +import { useHasLinkPreviewEnabled } from '../../../state/selectors/settings'; -async function toggleLinkPreviews(forceUpdate: () => void) { - const isToggleOn = Boolean(window.getSettingValue(SettingsKey.settingsLinkPreview)); +async function toggleLinkPreviews(isToggleOn: boolean, forceUpdate: () => void) { if (!isToggleOn) { window.inboxStore?.dispatch( updateConfirmModal({ @@ -29,7 +29,7 @@ async function toggleLinkPreviews(forceUpdate: () => void) { ); } else { await window.setSettingValue(SettingsKey.settingsLinkPreview, false); - await Data.createOrUpdateItem({ id: hasLinkPreviewPopupBeenDisplayed, value: false }); + await Storage.put(SettingsKey.hasLinkPreviewPopupBeenDisplayed, false); forceUpdate(); } } @@ -48,7 +48,7 @@ export const SettingsCategoryPrivacy = (props: { onPasswordUpdated: (action: string) => void; }) => { const forceUpdate = useUpdate(); - const isLinkPreviewsOn = Boolean(window.getSettingValue(SettingsKey.settingsLinkPreview)); + const isLinkPreviewsOn = useHasLinkPreviewEnabled(); if (props.hasPassword !== null) { return ( @@ -76,7 +76,7 @@ export const SettingsCategoryPrivacy = (props: { /> { - await toggleLinkPreviews(forceUpdate); + await toggleLinkPreviews(isLinkPreviewsOn, forceUpdate); }} title={window.i18n('linkPreviewsTitle')} description={window.i18n('linkPreviewDescription')} diff --git a/ts/data/data.ts b/ts/data/data.ts index 110eed4fd..d4e9ab035 100644 --- a/ts/data/data.ts +++ b/ts/data/data.ts @@ -53,10 +53,6 @@ export type SwarmNode = Snode & { address: string; }; -export const hasSyncedInitialConfigurationItem = 'hasSyncedInitialConfigurationItem'; -export const lastAvatarUploadTimestamp = 'lastAvatarUploadTimestamp'; -export const hasLinkPreviewPopupBeenDisplayed = 'hasLinkPreviewPopupBeenDisplayed'; - // Basic async function shutdown(): Promise { // Stop accepting new SQL jobs, flush outstanding queue @@ -654,7 +650,7 @@ async function getSnodePoolFromDb(): Promise | null> { } async function updateSnodePoolOnDb(snodesAsJsonString: string): Promise { - await Data.createOrUpdateItem({ id: SNODE_POOL_ITEM_ID, value: snodesAsJsonString }); + await Storage.put(SNODE_POOL_ITEM_ID, snodesAsJsonString); } function keysToArrayBuffer(keys: any, data: any) { @@ -692,8 +688,8 @@ const ITEM_KEYS: Object = { }; /** - * Note: In the app, you should always call createOrUpdateItem through Data.createOrUpdateItem (from the data.ts file). - * This is to ensure testing and stubbbing works as expected + * For anything related to the UI and redux, do not use `createOrUpdateItem` directly. Instead use Storage.put (from the utils folder). + * `Storage.put` will update the settings redux slice if needed but createOrUpdateItem will not. */ export async function createOrUpdateItem(data: StorageItem): Promise { const { id } = data; diff --git a/ts/data/settings-key.ts b/ts/data/settings-key.ts index 794cdc65c..67f2291d1 100644 --- a/ts/data/settings-key.ts +++ b/ts/data/settings-key.ts @@ -9,7 +9,10 @@ const settingsStartInTray = 'start-in-tray-setting'; const settingsOpengroupPruning = 'prune-setting'; const settingsNotification = 'notification-setting'; const settingsAudioNotification = 'audio-notification-setting'; -const someDeviceOutdatedSyncing = 'some-device-outdated-syncing'; +const someDeviceOutdatedSyncing = 'someDeviceOutdatedSyncing'; +const hasSyncedInitialConfigurationItem = 'hasSyncedInitialConfigurationItem'; +const lastAvatarUploadTimestamp = 'lastAvatarUploadTimestamp'; +const hasLinkPreviewPopupBeenDisplayed = 'hasLinkPreviewPopupBeenDisplayed'; export const SettingsKey = { settingsReadReceipt, @@ -23,7 +26,10 @@ export const SettingsKey = { settingsNotification, settingsAudioNotification, someDeviceOutdatedSyncing, -}; + hasSyncedInitialConfigurationItem, + lastAvatarUploadTimestamp, + hasLinkPreviewPopupBeenDisplayed, +} as const; export const KNOWN_BLINDED_KEYS_ITEM = 'KNOWN_BLINDED_KEYS_ITEM'; export const SNODE_POOL_ITEM_ID = 'SNODE_POOL_ITEM_ID'; diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index cc0353f8a..c5f8254fc 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -6,7 +6,7 @@ import { CallManager, SyncUtils, ToastUtils, UserUtils } from '../session/utils' import { SessionButtonColor } from '../components/basic/SessionButton'; import { getCallMediaPermissionsSettings } from '../components/settings/SessionSettings'; -import { Data, hasLinkPreviewPopupBeenDisplayed, lastAvatarUploadTimestamp } from '../data/data'; +import { Data } from '../data/data'; import { uploadFileToFsWithOnionV4 } from '../session/apis/file_server_api/FileServerApi'; import { getConversationController } from '../session/conversations'; import { getSodiumRenderer } from '../session/crypto'; @@ -37,11 +37,13 @@ import { processNewAttachment } from '../types/MessageAttachment'; import { IMAGE_JPEG } from '../types/MIME'; import { BlockedNumberController } from '../util/blockedNumberController'; import { encryptProfile } from '../util/crypto/profileEncrypter'; -import { setLastProfileUpdateTimestamp } from '../util/storage'; +import { Storage, setLastProfileUpdateTimestamp } from '../util/storage'; import { OpenGroupUtils } from '../session/apis/open_group_api/utils'; import { SessionUtilUserGroups } from '../session/utils/libsession/libsession_utils_user_groups'; import { leaveClosedGroup } from '../session/group/closed-group'; import { SessionUtilContact } from '../session/utils/libsession/libsession_utils_contacts'; +import { SettingsKey } from '../data/settings-key'; +import { ConfigurationDumpSync } from '../session/utils/job_runners/jobs/ConfigurationSyncDumpJob'; export function copyPublicKeyByConvoId(convoId: string) { if (OpenGroupUtils.isOpenGroupV2(convoId)) { @@ -457,12 +459,13 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) { avatarImageId: fileId, }); const newTimestampReupload = Date.now(); - await Data.createOrUpdateItem({ id: lastAvatarUploadTimestamp, value: newTimestampReupload }); + await Storage.put(SettingsKey.lastAvatarUploadTimestamp, newTimestampReupload); if (newAvatarDecrypted) { await setLastProfileUpdateTimestamp(Date.now()); if (window.sessionFeatureFlags.useSharedUtilForUserConfig) { await ConfigurationSync.queueNewJobIfNeeded(); + await ConfigurationDumpSync.queueNewJobIfNeeded(); } else { await SyncUtils.forceSyncConfigurationNowIfNeeded(true); } @@ -502,22 +505,22 @@ export async function replyToMessage(messageId: string) { */ export async function showLinkSharingConfirmationModalDialog(e: any) { const pastedText = e.clipboardData.getData('text'); - if (isURL(pastedText) && !window.getSettingValue('link-preview-setting', false)) { + if (isURL(pastedText) && !window.getSettingValue(SettingsKey.settingsLinkPreview, false)) { const alreadyDisplayedPopup = - (await Data.getItemById(hasLinkPreviewPopupBeenDisplayed))?.value || false; + (await Data.getItemById(SettingsKey.hasLinkPreviewPopupBeenDisplayed))?.value || false; if (!alreadyDisplayedPopup) { window.inboxStore?.dispatch( updateConfirmModal({ shouldShowConfirm: - !window.getSettingValue('link-preview-setting') && !alreadyDisplayedPopup, + !window.getSettingValue(SettingsKey.settingsLinkPreview) && !alreadyDisplayedPopup, title: window.i18n('linkPreviewsTitle'), message: window.i18n('linkPreviewsConfirmMessage'), okTheme: SessionButtonColor.Danger, onClickOk: async () => { - await window.setSettingValue('link-preview-setting', true); + await window.setSettingValue(SettingsKey.settingsLinkPreview, true); }, onClickClose: async () => { - await Data.createOrUpdateItem({ id: hasLinkPreviewPopupBeenDisplayed, value: true }); + await Storage.put(SettingsKey.hasLinkPreviewPopupBeenDisplayed, true); }, }) ); diff --git a/ts/mains/main_renderer.tsx b/ts/mains/main_renderer.tsx index f7a553aaf..6aebb329f 100644 --- a/ts/mains/main_renderer.tsx +++ b/ts/mains/main_renderer.tsx @@ -24,6 +24,7 @@ import { initialiseEmojiData } from '../util/emoji'; import { switchPrimaryColorTo } from '../themes/switchPrimaryColor'; import { LibSessionUtil } from '../session/utils/libsession/libsession_utils'; import { runners } from '../session/utils/job_runners/JobRunner'; +import { SettingsKey } from '../data/settings-key'; // tslint:disable: max-classes-per-file // Globally disable drag and drop @@ -163,7 +164,7 @@ Storage.onready(async () => { // Stop background processing AttachmentDownloads.stop(); // Stop processing incoming messages - // FIXME audric stop polling opengroupv2 and swarm nodes + // TODO stop polling opengroupv2 and swarm nodes // Shut down the data interface cleanly await Data.shutdown(); @@ -262,11 +263,6 @@ async function start() { WhisperEvents.on('registration_done', async () => { window.log.info('handling registration event'); - // Disable link previews as default per Kee - Storage.onready(async () => { - await Storage.put('link-preview-setting', false); - }); - await connect(); }); @@ -287,7 +283,7 @@ async function start() { }); } - function openStandAlone() { + function showRegistrationView() { ReactDOM.render(, document.getElementById('root')); } ExpirationTimerOptions.initExpiringMessageListener(); @@ -298,7 +294,7 @@ async function start() { } else { const primaryColor = window.Events.getPrimaryColorSetting(); await switchPrimaryColorTo(primaryColor); - openStandAlone(); + showRegistrationView(); } window.addEventListener('focus', () => { @@ -386,7 +382,7 @@ async function start() { if (launchCount === 1) { // Initialise default settings await window.setSettingValue('hide-menu-bar', true); - await window.setSettingValue('link-preview-setting', false); + await window.setSettingValue(SettingsKey.settingsLinkPreview, false); } WhisperEvents.on('openInbox', () => { diff --git a/ts/node/migration/sessionMigrations.ts b/ts/node/migration/sessionMigrations.ts index 05f45bc6d..b39a9638e 100644 --- a/ts/node/migration/sessionMigrations.ts +++ b/ts/node/migration/sessionMigrations.ts @@ -1219,7 +1219,7 @@ function insertContactIntoContactWrapper( const dbApproved = !!contact.isApproved || false; const dbApprovedMe = !!contact.didApproveMe || false; const dbBlocked = blockedNumbers.includes(contact.id); - const priority = contact.priority || 0; + const priority = contact.priority || CONVERSATION_PRIORITIES.default; const expirationTimerSeconds = contact.expireTimer || 0; const wrapperContact = getContactInfoFromDBValues({ @@ -1581,12 +1581,7 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite ourDbProfileKey ); } else { - userProfileWrapper.setUserInfo( - ourDbName, - ourConvoPriority, // consider that the Note to self is hidden on a fresh account (without avatar set) - '', - new Uint8Array() - ); + userProfileWrapper.setUserInfo(ourDbName, ourConvoPriority, '', new Uint8Array()); } insertContactIntoContactWrapper( diff --git a/ts/node/storage_item.ts b/ts/node/storage_item.ts index 19e276992..5be670a9e 100644 --- a/ts/node/storage_item.ts +++ b/ts/node/storage_item.ts @@ -1,5 +1,4 @@ export type StorageItem = { id: string; value: any; - timestamp?: number; }; diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 5b0e17ce3..b2b42c3ad 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -1,6 +1,6 @@ -import { compact, isEmpty, toNumber } from 'lodash'; +import { compact, isEmpty, isNumber, toNumber } from 'lodash'; import { ConfigDumpData } from '../data/configDump/configDump'; -import { Data, hasSyncedInitialConfigurationItem } from '../data/data'; +import { Data } from '../data/data'; import { ConversationInteraction } from '../interactions'; import { ConversationTypeEnum } from '../models/conversationAttributes'; import { SignalService } from '../protobuf'; @@ -27,7 +27,11 @@ import { configurationMessageReceived, trigger } from '../shims/events'; import { assertUnreachable } from '../types/sqlSharedTypes'; import { BlockedNumberController } from '../util'; import { Registration } from '../util/registration'; -import { getLastProfileUpdateTimestamp, setLastProfileUpdateTimestamp } from '../util/storage'; +import { + Storage, + getLastProfileUpdateTimestamp, + setLastProfileUpdateTimestamp, +} from '../util/storage'; import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions'; import { ContactsWrapperActions, @@ -703,8 +707,19 @@ async function handleGroupsAndContactsFromConfigMessageLegacy( return; } const envelopeTimestamp = toNumber(envelope.timestamp); - const lastConfigUpdate = await Data.getItemById(hasSyncedInitialConfigurationItem); - const lastConfigTimestamp = lastConfigUpdate?.timestamp; + + // at some point, we made the hasSyncedInitialConfigurationItem item to have a value=true and a timestamp set. + // we can actually just use the timestamp as a boolean, as if it is set, we know we have synced the initial config + // but we still need to handle the case where the timestamp was set when the value is true (for backwards compatiblity, until we get rid of the config message legacy) + const lastConfigUpdate = await Data.getItemById(SettingsKey.hasSyncedInitialConfigurationItem); + + let lastConfigTimestamp: number | undefined; + if (isNumber(lastConfigUpdate?.value)) { + lastConfigTimestamp = lastConfigUpdate?.value; + } else if (isNumber((lastConfigUpdate as any)?.timestamp)) { + lastConfigTimestamp = (lastConfigUpdate as any)?.timestamp; // ugly, but we can remove it once we dropped support for legacy config message, see comment above + } + const isNewerConfig = !lastConfigTimestamp || (lastConfigTimestamp && lastConfigTimestamp < envelopeTimestamp); @@ -713,11 +728,7 @@ async function handleGroupsAndContactsFromConfigMessageLegacy( return; } - await Data.createOrUpdateItem({ - id: 'hasSyncedInitialConfigurationItem', - value: true, - timestamp: envelopeTimestamp, - }); + await Storage.put(SettingsKey.hasSyncedInitialConfigurationItem, envelopeTimestamp); // we only want to apply changes to closed groups if we never got them // new opengroups get added when we get a new closed group message from someone, or a sync'ed message from outself creating the group diff --git a/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts b/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts index 96d32ed5e..bc23882ce 100644 --- a/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts +++ b/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts @@ -13,6 +13,7 @@ import { SogsBlinding } from './sogsBlinding'; import { fromHexToArray } from '../../../utils/String'; import { KNOWN_BLINDED_KEYS_ITEM } from '../../../../data/settings-key'; import { roomHasBlindEnabled } from '../../../../types/sqlSharedTypes'; +import { Storage } from '../../../../util/storage'; export type BlindedIdMapping = { blindedId: string; @@ -65,10 +66,7 @@ export async function loadKnownBlindedKeys() { */ export async function writeKnownBlindedKeys() { if (cachedKnownMapping && cachedKnownMapping.length) { - await Data.createOrUpdateItem({ - id: KNOWN_BLINDED_KEYS_ITEM, - value: JSON.stringify(cachedKnownMapping), - }); + await Storage.put(KNOWN_BLINDED_KEYS_ITEM, JSON.stringify(cachedKnownMapping)); } } diff --git a/ts/session/apis/snode_api/hfHandling.ts b/ts/session/apis/snode_api/hfHandling.ts index 933beea00..dcd7565b6 100644 --- a/ts/session/apis/snode_api/hfHandling.ts +++ b/ts/session/apis/snode_api/hfHandling.ts @@ -1,9 +1,13 @@ import { isNumber } from 'lodash'; import { Data } from '../../../data/data'; +import { Storage } from '../../../util/storage'; let hasSeenHardfork190: boolean | undefined; let hasSeenHardfork191: boolean | undefined; +const hasSeenHardfork190ItemId = 'hasSeenHardfork190'; +const hasSeenHardfork191ItemId = 'hasSeenHardfork191'; + /** * this is only intended for testing. Do not call this in production. */ @@ -11,13 +15,16 @@ export function resetHardForkCachedValues() { hasSeenHardfork190 = hasSeenHardfork191 = undefined; } +/** + * Not used anymore, but keeping those here in case we ever need to do hardfork enabling of features again + */ export async function getHasSeenHF190() { if (hasSeenHardfork190 === undefined) { // read values from db and cache them as it looks like we did not - const oldHhasSeenHardfork190 = (await Data.getItemById('hasSeenHardfork190'))?.value; + const oldHhasSeenHardfork190 = (await Data.getItemById(hasSeenHardfork190ItemId))?.value; // values do not exist in the db yet. Let's store false for now in the db and update our cached value. if (oldHhasSeenHardfork190 === undefined) { - await Data.createOrUpdateItem({ id: 'hasSeenHardfork190', value: false }); + await Storage.put(hasSeenHardfork190ItemId, false); hasSeenHardfork190 = false; } else { hasSeenHardfork190 = oldHhasSeenHardfork190; @@ -26,14 +33,17 @@ export async function getHasSeenHF190() { return hasSeenHardfork190; } +/** + * Not used anymore, but keeping those here in case we ever need to do hardfork enabling of features again + */ export async function getHasSeenHF191() { if (hasSeenHardfork191 === undefined) { // read values from db and cache them as it looks like we did not - const oldHhasSeenHardfork191 = (await Data.getItemById('hasSeenHardfork191'))?.value; + const oldHhasSeenHardfork191 = (await Data.getItemById(hasSeenHardfork191ItemId))?.value; // values do not exist in the db yet. Let's store false for now in the db and update our cached value. if (oldHhasSeenHardfork191 === undefined) { - await Data.createOrUpdateItem({ id: 'hasSeenHardfork191', value: false }); + await Storage.put(hasSeenHardfork191ItemId, false); hasSeenHardfork191 = false; } else { hasSeenHardfork191 = oldHhasSeenHardfork191; @@ -45,18 +55,18 @@ export async function getHasSeenHF191() { export async function handleHardforkResult(json: Record) { if (hasSeenHardfork190 === undefined || hasSeenHardfork191 === undefined) { // read values from db and cache them as it looks like we did not - const oldHhasSeenHardfork190 = (await Data.getItemById('hasSeenHardfork190'))?.value; - const oldHasSeenHardfork191 = (await Data.getItemById('hasSeenHardfork191'))?.value; + const oldHhasSeenHardfork190 = (await Data.getItemById(hasSeenHardfork190ItemId))?.value; + const oldHasSeenHardfork191 = (await Data.getItemById(hasSeenHardfork191ItemId))?.value; // values do not exist in the db yet. Let's store false for now in the db and update our cached value. if (oldHhasSeenHardfork190 === undefined) { - await Data.createOrUpdateItem({ id: 'hasSeenHardfork190', value: false }); + await Storage.put(hasSeenHardfork190ItemId, false); hasSeenHardfork190 = false; } else { hasSeenHardfork190 = oldHhasSeenHardfork190; } if (oldHasSeenHardfork191 === undefined) { - await Data.createOrUpdateItem({ id: 'hasSeenHardfork191', value: false }); + await Storage.put(hasSeenHardfork191ItemId, false); hasSeenHardfork191 = false; } else { hasSeenHardfork191 = oldHasSeenHardfork191; @@ -78,13 +88,11 @@ export async function handleHardforkResult(json: Record) { isNumber(json.hf[1]) ) { if (!hasSeenHardfork190 && json.hf[0] >= 19 && json.hf[1] >= 0) { - // window.log.info('[HF]: We just detected HF 19.0 on "retrieve"'); - await Data.createOrUpdateItem({ id: 'hasSeenHardfork190', value: true }); + await Storage.put(hasSeenHardfork190ItemId, true); hasSeenHardfork190 = true; } if (!hasSeenHardfork191 && json.hf[0] >= 19 && json.hf[1] >= 1) { - // window.log.info('[HF]: We just detected HF 19.1 on "retrieve"'); - await Data.createOrUpdateItem({ id: 'hasSeenHardfork191', value: true }); + await Storage.put(hasSeenHardfork191ItemId, true); hasSeenHardfork191 = true; } } diff --git a/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts b/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts index 64e0c8d1b..05ad7d874 100644 --- a/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts +++ b/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts @@ -29,7 +29,7 @@ export class ExpirationTimerUpdateMessage extends DataMessage { data.flags = SignalService.DataMessage.Flags.EXPIRATION_TIMER_UPDATE; - // FIXME we shouldn't need this once android recieving refactor is done. + // TODO we shouldn't need this once android recieving refactor is done. // the envelope stores the groupId for a closed group already. if (this.groupId) { const groupMessage = new SignalService.GroupContext(); diff --git a/ts/session/sending/PendingMessageCache.ts b/ts/session/sending/PendingMessageCache.ts index 5ded9c477..10b3739aa 100644 --- a/ts/session/sending/PendingMessageCache.ts +++ b/ts/session/sending/PendingMessageCache.ts @@ -5,6 +5,7 @@ import { ContentMessage } from '../messages/outgoing'; import { PubKey } from '../types'; import { MessageUtils } from '../utils'; import { SnodeNamespaces } from '../apis/snode_api/namespaces'; +import { Storage } from '../../util/storage'; // This is an abstraction for storing pending messages. // Ideally we want to store pending messages in the database so that @@ -140,9 +141,6 @@ export class PendingMessageCache { }); const encodedPendingMessages = JSON.stringify(encodedCache) || '[]'; - await Data.createOrUpdateItem({ - id: 'pendingMessages', - value: encodedPendingMessages, - }); + await Storage.put('pendingMessages', encodedPendingMessages); } } diff --git a/ts/session/utils/job_runners/JobRunner.ts b/ts/session/utils/job_runners/JobRunner.ts index d9a2b7b67..c6acbbe6f 100644 --- a/ts/session/utils/job_runners/JobRunner.ts +++ b/ts/session/utils/job_runners/JobRunner.ts @@ -11,6 +11,7 @@ import { RunJobResult, TypeOfPersistedData, } from './PersistedJob'; +import { Storage } from '../../../util/storage'; /** * 'job_in_progress' if there is already a job in progress @@ -177,10 +178,7 @@ export class PersistedJobRunner { private async writeJobsToDB() { const serialized = this.getSerializedJobs(); window.log.debug(`writing to db for "${this.jobRunnerType}": `, serialized); - await Data.createOrUpdateItem({ - id: this.getJobRunnerItemId(), - value: JSON.stringify(serialized), - }); + await Storage.put(this.getJobRunnerItemId(), JSON.stringify(serialized)); } private async addJobUnchecked(job: PersistedJob) { diff --git a/ts/session/utils/sync/syncUtils.ts b/ts/session/utils/sync/syncUtils.ts index 5d61eafee..6bfd32cac 100644 --- a/ts/session/utils/sync/syncUtils.ts +++ b/ts/session/utils/sync/syncUtils.ts @@ -31,6 +31,7 @@ import { ConfigurationDumpSync } from '../job_runners/jobs/ConfigurationSyncDump import { ConfigurationSync } from '../job_runners/jobs/ConfigurationSyncJob'; import { fromBase64ToArray, fromHexToArray } from '../String'; import { getCompleteUrlFromRoom } from '../../apis/open_group_api/utils/OpenGroupUtils'; +import { Storage } from '../../../util/storage'; const ITEM_ID_LAST_SYNC_TIMESTAMP = 'lastSyncedTimestamp'; @@ -38,7 +39,7 @@ const getLastSyncTimestampFromDb = async (): Promise => (await Data.getItemById(ITEM_ID_LAST_SYNC_TIMESTAMP))?.value; const writeLastSyncTimestampToDb = async (timestamp: number) => - Data.createOrUpdateItem({ id: ITEM_ID_LAST_SYNC_TIMESTAMP, value: timestamp }); + Storage.put(ITEM_ID_LAST_SYNC_TIMESTAMP, timestamp); /** * Conditionally Syncs user configuration with other devices linked. @@ -73,6 +74,7 @@ export const syncConfigurationIfNeeded = async () => { } else { await ConfigurationDumpSync.queueNewJobIfNeeded(); await ConfigurationSync.queueNewJobIfNeeded(); + await ConfigurationDumpSync.queueNewJobIfNeeded(); } }; diff --git a/ts/state/ducks/settings.tsx b/ts/state/ducks/settings.tsx new file mode 100644 index 000000000..eda23a6e6 --- /dev/null +++ b/ts/state/ducks/settings.tsx @@ -0,0 +1,73 @@ +import { PayloadAction, createSlice } from '@reduxjs/toolkit'; +import { SettingsKey } from '../../data/settings-key'; +import { isBoolean } from 'lodash'; +import { Storage } from '../../util/storage'; + +const SettingsBoolsKeyTrackedInRedux = [ + SettingsKey.someDeviceOutdatedSyncing, + SettingsKey.settingsLinkPreview, +] as const; + +export type SettingsState = { + settingsBools: Record; +}; + +export function getSettingsInitialState() { + return { + settingsBools: { + someDeviceOutdatedSyncing: false, + 'link-preview-setting': false, // this is the value of SettingsKey.settingsLinkPreview + }, + }; +} + +function isTrackedBoolean(key: string): key is typeof SettingsBoolsKeyTrackedInRedux[number] { + return SettingsBoolsKeyTrackedInRedux.indexOf(key as any) !== -1; +} + +/** + * This slice is the one holding the settings of the currently logged in user in redux. + * This is in addition to the settings stored in the Storage class but is a memory only representation of them. + * You should not try to make changes here, but instead through the Storage class. + * What you can do with this slice, is to create selectors and hooks to keep your UI in sync with the state in whatever is Storage. + */ +const settingsSlice = createSlice({ + name: 'settings', + // when this createSlice gets invoke, the storage is not ready, but redux still wants a state so we just avoid hitting the storage. + // Once the storage is ready, + initialState: getSettingsInitialState(), + reducers: { + updateAllOnStorageReady(state) { + const linkPreview = Storage.get(SettingsKey.settingsLinkPreview, false); + const outdatedSync = Storage.get(SettingsKey.someDeviceOutdatedSyncing, false); + state.settingsBools.someDeviceOutdatedSyncing = isBoolean(outdatedSync) + ? outdatedSync + : false; + state.settingsBools['link-preview-setting'] = isBoolean(linkPreview) ? linkPreview : false; // this is the value of SettingsKey.settingsLinkPreview + return state; + }, + updateSettingsBoolValue(state, action: PayloadAction<{ id: string; value: boolean }>) { + const { id, value } = action.payload; + + if (!isTrackedBoolean(id) || !isBoolean(value)) return state; + + state.settingsBools[id] = value; + + return state; + }, + deleteSettingsBoolValue(state, action: PayloadAction) { + if (!isTrackedBoolean(action.payload)) return state; + + delete state.settingsBools[action.payload]; + return state; + }, + }, +}); + +const { actions, reducer } = settingsSlice; +export const { + updateSettingsBoolValue, + deleteSettingsBoolValue, + updateAllOnStorageReady, +} = actions; +export const settingsReducer = reducer; diff --git a/ts/state/reducer.ts b/ts/state/reducer.ts index da394d25f..b81405f70 100644 --- a/ts/state/reducer.ts +++ b/ts/state/reducer.ts @@ -19,6 +19,7 @@ import { StagedAttachmentsStateType, } from './ducks/stagedAttachments'; import { PrimaryColorStateType, ThemeStateType } from '../themes/constants/colors'; +import { settingsReducer, SettingsState } from './ducks/settings'; export type StateType = { search: SearchStateType; @@ -35,6 +36,7 @@ export type StateType = { stagedAttachments: StagedAttachmentsStateType; call: CallStateType; sogsRoomInfo: SogsRoomInfoState; + settings: SettingsState; }; export const reducers = { @@ -52,6 +54,7 @@ export const reducers = { stagedAttachments, call, sogsRoomInfo: ReduxSogsRoomInfos.sogsRoomInfoReducer, + settings: settingsReducer, }; // Making this work would require that our reducer signature supported AnyAction, not diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 88396aa9b..a3e5a7bbb 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -42,6 +42,7 @@ import { filter, isEmpty, isNumber, pick, sortBy } from 'lodash'; import { MessageReactsSelectorProps } from '../../components/conversation/message/message-content/MessageReactions'; import { getModeratorsOutsideRedux } from './sogsRoomInfo'; import { getSelectedConversation, getSelectedConversationKey } from './selectedConversation'; +import { useSelector } from 'react-redux'; export const getConversations = (state: StateType): ConversationsStateType => state.conversations; @@ -882,6 +883,11 @@ export const getMessageTextProps = createSelector(getMessagePropsByMessageId, (p return msgProps; }); +export const useMessageIsDeleted = (messageId: string): boolean => { + const props = useSelector((state: StateType) => getMessagePropsByMessageId(state, messageId)); + return props?.propsForMessage.isDeleted || false; +}; + export const getMessageContextMenuProps = createSelector(getMessagePropsByMessageId, (props): | MessageContextMenuSelectorProps | undefined => { diff --git a/ts/state/selectors/selectedConversation.ts b/ts/state/selectors/selectedConversation.ts index 46ffb800f..759ab8cbf 100644 --- a/ts/state/selectors/selectedConversation.ts +++ b/ts/state/selectors/selectedConversation.ts @@ -80,10 +80,10 @@ export function getSelectedCanWrite(state: StateType) { if (!selectedConvo) { return false; } - const canWrite = getCanWrite(state, selectedConvoPubkey); + const canWriteSogs = getCanWrite(state, selectedConvoPubkey); const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo; - return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWrite)); + return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWriteSogs)); } /** diff --git a/ts/state/selectors/settings.ts b/ts/state/selectors/settings.ts new file mode 100644 index 000000000..ffca0da3e --- /dev/null +++ b/ts/state/selectors/settings.ts @@ -0,0 +1,19 @@ +import { useSelector } from 'react-redux'; +import { SettingsKey } from '../../data/settings-key'; +import { StateType } from '../reducer'; + +const getLinkPreviewEnabled = (state: StateType) => + state.settings.settingsBools[SettingsKey.settingsLinkPreview]; + +const getHasDeviceOutdatedSyncing = (state: StateType) => + state.settings.settingsBools[SettingsKey.someDeviceOutdatedSyncing]; + +export const useHasLinkPreviewEnabled = () => { + const value = useSelector(getLinkPreviewEnabled); + return Boolean(value); +}; + +export const useHasDeviceOutdatedSyncing = () => { + const value = useSelector(getHasDeviceOutdatedSyncing); + return Boolean(value); +}; diff --git a/ts/util/accountManager.ts b/ts/util/accountManager.ts index 8ed77e10d..62c0d2cfb 100644 --- a/ts/util/accountManager.ts +++ b/ts/util/accountManager.ts @@ -197,8 +197,10 @@ async function registrationDone(ourPubkey: string, displayName: string) { await conversation.setIsApproved(true, false); await conversation.setDidApproveMe(true, false); - + // when onboarding, hide the note to self by default. + await conversation.setHidden(true); await conversation.commit(); + const user = { ourNumber: getOurPubKeyStrFromCache(), ourPrimary: ourPubkey, diff --git a/ts/util/blockedNumberController.ts b/ts/util/blockedNumberController.ts index 0202b4814..d65a3a331 100644 --- a/ts/util/blockedNumberController.ts +++ b/ts/util/blockedNumberController.ts @@ -1,6 +1,7 @@ import { Data } from '../data/data'; import { commitConversationAndRefreshWrapper } from '../models/conversation'; import { PubKey } from '../session/types'; +import { Storage } from './storage'; const BLOCKED_NUMBERS_ID = 'blocked'; @@ -112,9 +113,6 @@ export class BlockedNumberController { } private static async saveToDB(id: string, numbers: Set): Promise { - await Data.createOrUpdateItem({ - id, - value: [...numbers], - }); + await Storage.put(id, [...numbers]); } } diff --git a/ts/util/storage.ts b/ts/util/storage.ts index 853325dc6..c864b1d47 100644 --- a/ts/util/storage.ts +++ b/ts/util/storage.ts @@ -1,10 +1,12 @@ import { Data } from '../data/data'; import { SessionKeyPair } from '../receiver/keypairs'; import { DEFAULT_RECENT_REACTS } from '../session/constants'; +import { deleteSettingsBoolValue, updateSettingsBoolValue } from '../state/ducks/settings'; +import { isBoolean } from 'lodash'; let ready = false; -type ValueType = string | number | boolean | SessionKeyPair; +type ValueType = string | number | boolean | SessionKeyPair | Array; type InsertedValueType = { id: string; value: ValueType }; let items: Record; let callbacks: Array<() => void> = []; @@ -23,6 +25,10 @@ async function put(key: string, value: ValueType) { items[key] = data; await Data.createOrUpdateItem(data); + + if (isBoolean(value)) { + window?.inboxStore?.dispatch(updateSettingsBoolValue({ id: key, value })); + } } function get(key: string, defaultValue?: ValueType) { @@ -45,6 +51,9 @@ async function remove(key: string) { // tslint:disable-next-line: no-dynamic-delete delete items[key]; + + window?.inboxStore?.dispatch(deleteSettingsBoolValue(key)); + await Data.removeItemById(key); } diff --git a/ts/webworker/workers/node/libsession/libsession.worker.ts b/ts/webworker/workers/node/libsession/libsession.worker.ts index b56eb7737..ea918e7be 100644 --- a/ts/webworker/workers/node/libsession/libsession.worker.ts +++ b/ts/webworker/workers/node/libsession/libsession.worker.ts @@ -90,7 +90,6 @@ function initUserWrapper( const userType = assertUserWrapperType(wrapperType); const wrapper = getUserWrapper(wrapperType); - console.warn('initUserWrapper: ', wrapperType, options); if (wrapper) { throw new Error(`${wrapperType} already init`); }