mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
chore: add a redux settings slice, currently outdated banner inc
This commit is contained in:
parent
0080254286
commit
0e286142f1
33 changed files with 232 additions and 132 deletions
|
@ -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 (
|
||||
|
|
|
@ -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<TypingBubbleProps>`
|
|||
`;
|
||||
|
||||
export const TypingBubble = (props: TypingBubbleProps) => {
|
||||
if (isOpenOrClosedGroup(props.conversationType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!props.isTyping) {
|
||||
const isOpenOrClosedGroup = useSelectedIsGroup();
|
||||
if (!isOpenOrClosedGroup || !props.isTyping) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Props, State> {
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -52,7 +52,7 @@ const ChangeItemLeft = (left: Array<string>): 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 || '']);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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...');
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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: {
|
|||
/>
|
||||
<SessionToggleWithDescription
|
||||
onClickToggle={async () => {
|
||||
await toggleLinkPreviews(forceUpdate);
|
||||
await toggleLinkPreviews(isLinkPreviewsOn, forceUpdate);
|
||||
}}
|
||||
title={window.i18n('linkPreviewsTitle')}
|
||||
description={window.i18n('linkPreviewDescription')}
|
||||
|
|
|
@ -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<void> {
|
||||
// Stop accepting new SQL jobs, flush outstanding queue
|
||||
|
@ -654,7 +650,7 @@ async function getSnodePoolFromDb(): Promise<Array<Snode> | null> {
|
|||
}
|
||||
|
||||
async function updateSnodePoolOnDb(snodesAsJsonString: string): Promise<void> {
|
||||
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<void> {
|
||||
const { id } = data;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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(<SessionRegistrationView />, 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', () => {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export type StorageItem = {
|
||||
id: string;
|
||||
value: any;
|
||||
timestamp?: number;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<string, any>) {
|
||||
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<string, any>) {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T extends TypeOfPersistedData> {
|
|||
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<T>) {
|
||||
|
|
|
@ -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<number | undefined> =>
|
|||
(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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
73
ts/state/ducks/settings.tsx
Normal file
73
ts/state/ducks/settings.tsx
Normal file
|
@ -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<typeof SettingsBoolsKeyTrackedInRedux[number], boolean>;
|
||||
};
|
||||
|
||||
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<string>) {
|
||||
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;
|
|
@ -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
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
19
ts/state/selectors/settings.ts
Normal file
19
ts/state/selectors/settings.ts
Normal file
|
@ -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);
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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<string>): Promise<void> {
|
||||
await Data.createOrUpdateItem({
|
||||
id,
|
||||
value: [...numbers],
|
||||
});
|
||||
await Storage.put(id, [...numbers]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string>;
|
||||
type InsertedValueType = { id: string; value: ValueType };
|
||||
let items: Record<string, InsertedValueType>;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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`);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue