mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
feat: add block msg requests from sogs
This commit is contained in:
parent
e4a4945dd4
commit
84deed19f9
28 changed files with 347 additions and 42 deletions
|
@ -442,6 +442,8 @@
|
|||
"clearAll": "Clear All",
|
||||
"clearDataSettingsTitle": "Clear Data",
|
||||
"messageRequests": "Message Requests",
|
||||
"blindedMsgReqsSettingTitle": "Community Message Requests",
|
||||
"blindedMsgReqsSettingDesc": "Allow message requests from Community conversations.",
|
||||
"requestsSubtitle": "Pending Requests",
|
||||
"requestsPlaceholder": "No requests",
|
||||
"hideRequestBannerDescription": "Hide the Message Request banner until you receive a new message request.",
|
||||
|
@ -489,6 +491,7 @@
|
|||
"clearAllConfirmationTitle": "Clear All Message Requests",
|
||||
"clearAllConfirmationBody": "Are you sure you want to clear all message requests?",
|
||||
"noMessagesInReadOnly": "There are no messages in <b>$name$</b>.",
|
||||
"noMessagesInBlindedDisabledMsgRequests": "<b>$name$</b> has message requests from Community conversations turned off, so you cannot send them a message.",
|
||||
"noMessagesInNoteToSelf": "You have no messages in <b>$name$</b>.",
|
||||
"noMessagesInEverythingElse": "You have no messages from <b>$name$</b>. Send a message to start the conversation!",
|
||||
"hideBanner": "Hide",
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
"glob": "7.1.2",
|
||||
"image-type": "^4.1.0",
|
||||
"ip2country": "1.0.1",
|
||||
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.5/libsession_util_nodejs-v0.2.5.tar.gz",
|
||||
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.6/libsession_util_nodejs-v0.2.6.tar.gz",
|
||||
"libsodium-wrappers-sumo": "^0.7.9",
|
||||
"linkify-it": "^4.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
|
@ -224,6 +224,7 @@ message DataMessage {
|
|||
optional OpenGroupInvitation openGroupInvitation = 102;
|
||||
optional ClosedGroupControlMessage closedGroupControlMessage = 104;
|
||||
optional string syncTarget = 105;
|
||||
optional bool blocksCommunityMessageRequests = 106;
|
||||
// optional GroupMessage groupMessage = 120;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
import {
|
||||
getSelectedCanWrite,
|
||||
useSelectedConversationKey,
|
||||
useSelectedHasDisabledBlindedMsgRequests,
|
||||
useSelectedNicknameOrProfileNameOrShortenedPubkey,
|
||||
useSelectedisNoteToSelf,
|
||||
} from '../../state/selectors/selectedConversation';
|
||||
|
@ -61,6 +62,7 @@ export const NoMessageInConversation = () => {
|
|||
|
||||
const isMe = useSelectedisNoteToSelf();
|
||||
const canWrite = useSelector(getSelectedCanWrite);
|
||||
const privateBlindedAndBlockingMsgReqs = useSelectedHasDisabledBlindedMsgRequests();
|
||||
// TODOLATER use this selector accross the whole application (left pane excluded)
|
||||
const nameToRender = useSelectedNicknameOrProfileNameOrShortenedPubkey();
|
||||
|
||||
|
@ -69,7 +71,11 @@ export const NoMessageInConversation = () => {
|
|||
}
|
||||
let localizedKey: LocalizerKeys = 'noMessagesInEverythingElse';
|
||||
if (!canWrite) {
|
||||
localizedKey = 'noMessagesInReadOnly';
|
||||
if (privateBlindedAndBlockingMsgReqs) {
|
||||
localizedKey = 'noMessagesInBlindedDisabledMsgRequests';
|
||||
} else {
|
||||
localizedKey = 'noMessagesInReadOnly';
|
||||
}
|
||||
} else if (isMe) {
|
||||
localizedKey = 'noMessagesInNoteToSelf';
|
||||
}
|
||||
|
|
|
@ -441,7 +441,7 @@ class CompositionBoxInner extends React.Component<Props, State> {
|
|||
{typingEnabled && (
|
||||
<ToggleEmojiButton ref={this.emojiPanelButton} onClick={this.toggleEmojiPanel} />
|
||||
)}
|
||||
<SendMessageButton onClick={this.onSendMessage} />
|
||||
{typingEnabled && <SendMessageButton onClick={this.onSendMessage} />}
|
||||
{typingEnabled && showEmojiPanel && (
|
||||
<StyledEmojiPanelContainer role="button">
|
||||
<SessionEmojiPanel
|
||||
|
|
|
@ -45,6 +45,7 @@ import {
|
|||
import { isDarkTheme } from '../../state/selectors/theme';
|
||||
import { ThemeStateType } from '../../themes/constants/colors';
|
||||
import { switchThemeTo } from '../../themes/switchTheme';
|
||||
import { ConfigurationSync } from '../../session/utils/job_runners/jobs/ConfigurationSyncJob';
|
||||
|
||||
const Section = (props: { type: SectionType }) => {
|
||||
const ourNumber = useSelector(getOurNumber);
|
||||
|
@ -212,6 +213,11 @@ const doAppStartUp = async () => {
|
|||
// this call does nothing except calling the constructor, which will continue sending message in the pipeline
|
||||
void getMessageQueue().processAllPending();
|
||||
}, 3000);
|
||||
|
||||
global.setTimeout(() => {
|
||||
// Schedule a confSyncJob in some time to let anything incoming from the network be applied and see if there is a push needed
|
||||
void ConfigurationSync.queueNewJobIfNeeded();
|
||||
}, 20000);
|
||||
};
|
||||
|
||||
async function fetchReleaseFromFSAndUpdateMain() {
|
||||
|
|
|
@ -9,10 +9,16 @@ import { SessionButtonColor } from '../../basic/SessionButton';
|
|||
import { SpacerLG } from '../../basic/Text';
|
||||
import { TypingBubble } from '../../conversation/TypingBubble';
|
||||
|
||||
import { UserUtils } from '../../../session/utils';
|
||||
import { ConfigurationSync } from '../../../session/utils/job_runners/jobs/ConfigurationSyncJob';
|
||||
import { SessionUtilUserProfile } from '../../../session/utils/libsession/libsession_utils_user_profile';
|
||||
import {
|
||||
useHasBlindedMsgRequestsEnabled,
|
||||
useHasLinkPreviewEnabled,
|
||||
} from '../../../state/selectors/settings';
|
||||
import { Storage } from '../../../util/storage';
|
||||
import { SessionSettingButtonItem, SessionToggleWithDescription } from '../SessionSettingListItem';
|
||||
import { displayPasswordModal } from '../SessionSettings';
|
||||
import { Storage } from '../../../util/storage';
|
||||
import { useHasLinkPreviewEnabled } from '../../../state/selectors/settings';
|
||||
|
||||
async function toggleLinkPreviews(isToggleOn: boolean, forceUpdate: () => void) {
|
||||
if (!isToggleOn) {
|
||||
|
@ -50,6 +56,7 @@ export const SettingsCategoryPrivacy = (props: {
|
|||
}) => {
|
||||
const forceUpdate = useUpdate();
|
||||
const isLinkPreviewsOn = useHasLinkPreviewEnabled();
|
||||
const areBlindedRequestsEnabled = useHasBlindedMsgRequestsEnabled();
|
||||
|
||||
if (props.hasPassword !== null) {
|
||||
return (
|
||||
|
@ -84,6 +91,20 @@ export const SettingsCategoryPrivacy = (props: {
|
|||
description={window.i18n('linkPreviewDescription')}
|
||||
active={isLinkPreviewsOn}
|
||||
/>
|
||||
<SessionToggleWithDescription
|
||||
onClickToggle={async () => {
|
||||
const toggledValue = !areBlindedRequestsEnabled;
|
||||
await window.setSettingValue(SettingsKey.hasBlindedMsgRequestsEnabled, toggledValue);
|
||||
await SessionUtilUserProfile.insertUserProfileIntoWrapper(
|
||||
UserUtils.getOurPubKeyStrFromCache()
|
||||
);
|
||||
await ConfigurationSync.queueNewJobIfNeeded();
|
||||
forceUpdate();
|
||||
}}
|
||||
title={window.i18n('blindedMsgReqsSettingTitle')}
|
||||
description={window.i18n('blindedMsgReqsSettingDesc')}
|
||||
active={areBlindedRequestsEnabled}
|
||||
/>
|
||||
|
||||
{!props.hasPassword && (
|
||||
<SessionSettingButtonItem
|
||||
|
|
|
@ -5,6 +5,7 @@ const settingsAutoUpdate = 'auto-update';
|
|||
const settingsMenuBar = 'hide-menu-bar';
|
||||
const settingsSpellCheck = 'spell-check';
|
||||
const settingsLinkPreview = 'link-preview-setting';
|
||||
const hasBlindedMsgRequestsEnabled = 'hasBlindedMsgRequestsEnabled';
|
||||
const settingsStartInTray = 'start-in-tray-setting';
|
||||
const settingsOpengroupPruning = 'prune-setting';
|
||||
const settingsNotification = 'notification-setting';
|
||||
|
@ -28,6 +29,7 @@ export const SettingsKey = {
|
|||
settingsLinkPreview,
|
||||
settingsStartInTray,
|
||||
settingsOpengroupPruning,
|
||||
hasBlindedMsgRequestsEnabled,
|
||||
settingsNotification,
|
||||
settingsAudioNotification,
|
||||
someDeviceOutdatedSyncing,
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
isEmpty,
|
||||
isEqual,
|
||||
isFinite,
|
||||
isNil,
|
||||
isNumber,
|
||||
isString,
|
||||
sortBy,
|
||||
|
@ -291,6 +292,11 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
toRet.isMarkedUnread = !!this.get('markedAsUnread');
|
||||
}
|
||||
|
||||
const blocksSogsMsgReqsTimestamp = this.get('blocksSogsMsgReqsTimestamp');
|
||||
if (blocksSogsMsgReqsTimestamp) {
|
||||
toRet.blocksSogsMsgReqsTimestamp = blocksSogsMsgReqsTimestamp;
|
||||
}
|
||||
|
||||
if (isPrivate) {
|
||||
toRet.isPrivate = true;
|
||||
if (this.typingTimer) {
|
||||
|
@ -1243,6 +1249,35 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
return !!this.get('markedAsUnread');
|
||||
}
|
||||
|
||||
public async updateBlocksSogsMsgReqsTimestamp(
|
||||
blocksSogsMsgReqsTimestamp: number,
|
||||
shouldCommit: boolean = true
|
||||
) {
|
||||
if (!PubKey.isBlinded(this.id)) {
|
||||
return; // this thing only applies to sogs blinded conversations
|
||||
}
|
||||
|
||||
if (
|
||||
(isNil(this.get('blocksSogsMsgReqsTimestamp')) && !isNil(blocksSogsMsgReqsTimestamp)) ||
|
||||
(blocksSogsMsgReqsTimestamp === 0 && this.get('blocksSogsMsgReqsTimestamp') !== 0) ||
|
||||
blocksSogsMsgReqsTimestamp > this.get('blocksSogsMsgReqsTimestamp')
|
||||
) {
|
||||
this.set({
|
||||
blocksSogsMsgReqsTimestamp,
|
||||
});
|
||||
if (shouldCommit) {
|
||||
await this.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public blocksSogsMsgReqsTimestamp(): number {
|
||||
if (!PubKey.isBlinded(this.id)) {
|
||||
return 0; // this thing only applies to sogs blinded conversations
|
||||
}
|
||||
return this.get('blocksSogsMsgReqsTimestamp') || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a private conversation as approved to the specified value.
|
||||
* Does not do anything on non private chats.
|
||||
|
|
|
@ -101,6 +101,8 @@ export interface ConversationAttributes {
|
|||
didApproveMe: boolean; // if our message request was approved already (or they've sent us a message request/message themselves). If isApproved & didApproveMe, a message request becomes a contact
|
||||
|
||||
markedAsUnread: boolean; // Force the conversation as unread even if all the messages are read. Used to highlight a conversation the user wants to check again later, synced.
|
||||
|
||||
blocksSogsMsgReqsTimestamp: number; // if that convo is a blinded one and that user denied be contacted through sogs, this field will be set to his latest message timestamp
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,6 +135,7 @@ export const fillConvoAttributesWithDefaults = (
|
|||
left: false,
|
||||
priority: CONVERSATION_PRIORITIES.default,
|
||||
markedAsUnread: false,
|
||||
blocksSogsMsgReqsTimestamp: 0,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { difference, omit, pick } from 'lodash';
|
||||
import { difference, isNumber, omit, pick } from 'lodash';
|
||||
import * as BetterSqlite3 from '@signalapp/better-sqlite3';
|
||||
import {
|
||||
ConversationAttributes,
|
||||
|
@ -72,6 +72,7 @@ const allowedKeysFormatRowOfConversation = [
|
|||
'displayNameInProfile',
|
||||
'conversationIdOrigin',
|
||||
'markedAsUnread',
|
||||
'blocksSogsMsgReqsTimestamp',
|
||||
'priority',
|
||||
];
|
||||
|
||||
|
@ -138,6 +139,10 @@ export function formatRowOfConversation(
|
|||
convo.lastMessageStatus = undefined;
|
||||
}
|
||||
|
||||
if (!isNumber(convo.blocksSogsMsgReqsTimestamp)) {
|
||||
convo.blocksSogsMsgReqsTimestamp = 0;
|
||||
}
|
||||
|
||||
if (!convo.triggerNotificationsFor) {
|
||||
convo.triggerNotificationsFor = 'all';
|
||||
}
|
||||
|
@ -189,6 +194,7 @@ const allowedKeysOfConversationAttributes = [
|
|||
'displayNameInProfile',
|
||||
'conversationIdOrigin',
|
||||
'markedAsUnread',
|
||||
'blocksSogsMsgReqsTimestamp',
|
||||
'priority',
|
||||
];
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
UserConfigWrapperNode,
|
||||
UserGroupsWrapperNode,
|
||||
} from 'libsession_util_nodejs';
|
||||
import { compact, isArray, isEmpty, isFinite, isNumber, isString, map, pick } from 'lodash';
|
||||
import { compact, isArray, isEmpty, isFinite, isNil, isNumber, isString, map, pick } from 'lodash';
|
||||
import {
|
||||
CONVERSATION_PRIORITIES,
|
||||
ConversationAttributes,
|
||||
|
@ -15,6 +15,7 @@ import { HexKeyPair } from '../../receiver/keypairs';
|
|||
import { fromHexToArray } from '../../session/utils/String';
|
||||
import {
|
||||
CONFIG_DUMP_TABLE,
|
||||
ConfigDumpRow,
|
||||
getCommunityInfoFromDBValues,
|
||||
getContactInfoFromDBValues,
|
||||
getLegacyGroupInfoFromDBValues,
|
||||
|
@ -35,6 +36,7 @@ import {
|
|||
|
||||
import { getIdentityKeys, sqlNode } from '../sql';
|
||||
import { sleepFor } from '../../session/utils/Promise';
|
||||
import { SettingsKey } from '../../data/settings-key';
|
||||
|
||||
const hasDebugEnvVariable = Boolean(process.env.SESSION_DEBUG);
|
||||
|
||||
|
@ -103,6 +105,7 @@ const LOKI_SCHEMA_VERSIONS = [
|
|||
updateToSessionSchemaVersion30,
|
||||
updateToSessionSchemaVersion31,
|
||||
updateToSessionSchemaVersion32,
|
||||
updateToSessionSchemaVersion33,
|
||||
];
|
||||
|
||||
function updateToSessionSchemaVersion1(currentVersion: number, db: BetterSqlite3.Database) {
|
||||
|
@ -1582,6 +1585,24 @@ function updateToSessionSchemaVersion30(currentVersion: number, db: BetterSqlite
|
|||
console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the logged in user conversation attributes and the keys.
|
||||
* If the keys exists but a conversation for that pubkey does not exist yet, the keys are still returned
|
||||
*/
|
||||
function getLoggedInUserConvoDuringMigration(db: BetterSqlite3.Database) {
|
||||
const ourKeys = getIdentityKeys(db);
|
||||
|
||||
if (!ourKeys || !ourKeys.publicKeyHex || !ourKeys.privateEd25519) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const ourConversation = db.prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`).get({
|
||||
id: ourKeys.publicKeyHex,
|
||||
}) as Record<string, any> | null;
|
||||
|
||||
return { ourKeys, ourConversation: ourConversation || null };
|
||||
}
|
||||
|
||||
function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite3.Database) {
|
||||
const targetVersion = 31;
|
||||
if (currentVersion >= targetVersion) {
|
||||
|
@ -1593,16 +1614,13 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite
|
|||
// In the migration 30, we made all the changes which didn't require the user to be logged in yet.
|
||||
// in this one, we check if a user is logged in, and if yes we build and save the config dumps for the current state of the database.
|
||||
try {
|
||||
const keys = getIdentityKeys(db);
|
||||
const loggedInUser = getLoggedInUserConvoDuringMigration(db);
|
||||
|
||||
const userAlreadyCreated = !!keys && !isEmpty(keys.privateEd25519);
|
||||
|
||||
if (!userAlreadyCreated) {
|
||||
if (!loggedInUser || !loggedInUser.ourKeys) {
|
||||
throw new Error('privateEd25519 was empty. Considering no users are logged in');
|
||||
}
|
||||
const blockedNumbers = getBlockedNumbersDuringMigration(db);
|
||||
|
||||
const { privateEd25519, publicKeyHex } = keys;
|
||||
const { privateEd25519, publicKeyHex } = loggedInUser.ourKeys;
|
||||
const userProfileWrapper = new UserConfigWrapperNode(privateEd25519, null);
|
||||
const contactsConfigWrapper = new ContactsConfigWrapperNode(privateEd25519, null);
|
||||
const userGroupsConfigWrapper = new UserGroupsWrapperNode(privateEd25519, null);
|
||||
|
@ -1612,11 +1630,7 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite
|
|||
* Setup up the User profile wrapper with what is stored in our own conversation
|
||||
*/
|
||||
|
||||
const ourConversation = db
|
||||
.prepare(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`)
|
||||
.get({
|
||||
id: publicKeyHex,
|
||||
}) as Record<string, any> | undefined;
|
||||
const { ourConversation } = loggedInUser;
|
||||
|
||||
if (!ourConversation) {
|
||||
throw new Error('Failed to find our logged in conversation while migrating');
|
||||
|
@ -1636,7 +1650,7 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite
|
|||
url: ourDbProfileUrl,
|
||||
key: ourDbProfileKey,
|
||||
}
|
||||
// ourConvoExpire
|
||||
// ourConvoExpire,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1854,6 +1868,91 @@ function updateToSessionSchemaVersion32(currentVersion: number, db: BetterSqlite
|
|||
console.log(`updateToSessionSchemaVersion${targetVersion}: success!`);
|
||||
}
|
||||
|
||||
function fetchUserConfigDump(
|
||||
db: BetterSqlite3.Database,
|
||||
userPubkeyhex: string
|
||||
): ConfigDumpRow | null {
|
||||
const userConfigWrapperDumps = db
|
||||
.prepare(
|
||||
`SELECT * FROM ${CONFIG_DUMP_TABLE} WHERE variant = $variant AND publicKey = $publicKey;`
|
||||
)
|
||||
.all({ variant: 'UserConfig', publicKey: userPubkeyhex }) as Array<ConfigDumpRow>;
|
||||
|
||||
if (!userConfigWrapperDumps || !userConfigWrapperDumps.length) {
|
||||
return null;
|
||||
}
|
||||
// we can only have one dump with the "UserConfig" variant and our pubkey
|
||||
return userConfigWrapperDumps[0];
|
||||
}
|
||||
|
||||
function writeUserConfigDump(db: BetterSqlite3.Database, userPubkeyhex: string, dump: Uint8Array) {
|
||||
db.prepare(
|
||||
`INSERT OR REPLACE INTO ${CONFIG_DUMP_TABLE} (
|
||||
publicKey,
|
||||
variant,
|
||||
data
|
||||
) values (
|
||||
$publicKey,
|
||||
$variant,
|
||||
$data
|
||||
);`
|
||||
).run({
|
||||
publicKey: userPubkeyhex,
|
||||
variant: 'UserConfig',
|
||||
data: dump,
|
||||
});
|
||||
}
|
||||
|
||||
function updateToSessionSchemaVersion33(currentVersion: number, db: BetterSqlite3.Database) {
|
||||
const targetVersion = 33;
|
||||
if (currentVersion >= targetVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`updateToSessionSchemaVersion${targetVersion}: starting...`);
|
||||
db.transaction(() => {
|
||||
db.exec(`ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN blocksSogsMsgReqsTimestamp INTEGER;`);
|
||||
|
||||
const loggedInUser = getLoggedInUserConvoDuringMigration(db);
|
||||
|
||||
if (!loggedInUser?.ourKeys) {
|
||||
// no user loggedin was empty. Considering no users are logged in
|
||||
writeSessionSchemaVersion(targetVersion, db);
|
||||
return;
|
||||
}
|
||||
// a user is logged in, we want to enable the 'inbox' polling for sogs, only if the current userwrapper for that field is undefined
|
||||
const { privateEd25519, publicKeyHex } = loggedInUser.ourKeys;
|
||||
|
||||
// Get existing config wrapper dump and update it
|
||||
const userConfigWrapperDump = fetchUserConfigDump(db, publicKeyHex);
|
||||
|
||||
if (!userConfigWrapperDump) {
|
||||
writeSessionSchemaVersion(targetVersion, db);
|
||||
return;
|
||||
}
|
||||
const userConfigData = userConfigWrapperDump.data;
|
||||
const userProfileWrapper = new UserConfigWrapperNode(privateEd25519, userConfigData);
|
||||
|
||||
let blindedReqEnabled = userProfileWrapper.getEnableBlindedMsgRequest();
|
||||
|
||||
// if the value stored in the wrapper is undefined, we want to have blinded request enabled
|
||||
if (isNil(blindedReqEnabled)) {
|
||||
// this change will be part of the next ConfSyncJob (one is always made on app startup)
|
||||
userProfileWrapper.setEnableBlindedMsgRequest(true);
|
||||
writeUserConfigDump(db, publicKeyHex, userProfileWrapper.dump());
|
||||
}
|
||||
blindedReqEnabled = userProfileWrapper.getEnableBlindedMsgRequest();
|
||||
|
||||
// update the item stored in the DB with that value too
|
||||
sqlNode.createOrUpdateItem(
|
||||
{ id: SettingsKey.hasBlindedMsgRequestsEnabled, value: blindedReqEnabled },
|
||||
db
|
||||
);
|
||||
|
||||
writeSessionSchemaVersion(targetVersion, db);
|
||||
})();
|
||||
}
|
||||
|
||||
export function printTableColumns(table: string, db: BetterSqlite3.Database) {
|
||||
console.info(db.pragma(`table_info('${table}');`));
|
||||
}
|
||||
|
|
|
@ -442,6 +442,7 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
|
|||
conversationIdOrigin,
|
||||
priority,
|
||||
markedAsUnread,
|
||||
blocksSogsMsgReqsTimestamp,
|
||||
} = formatted;
|
||||
|
||||
const omited = omit(formatted);
|
||||
|
@ -491,6 +492,7 @@ function saveConversation(data: ConversationAttributes): SaveConversationReturn
|
|||
displayNameInProfile,
|
||||
conversationIdOrigin,
|
||||
markedAsUnread: toSqliteBoolean(markedAsUnread),
|
||||
blocksSogsMsgReqsTimestamp,
|
||||
});
|
||||
|
||||
return fetchConvoMemoryDetails(id);
|
||||
|
|
|
@ -45,7 +45,7 @@ export const configDumpData: ConfigDumpDataNode = {
|
|||
getByVariantAndPubkey: (variant: ConfigWrapperObjectTypes, publicKey: string) => {
|
||||
const rows = assertGlobalInstance()
|
||||
.prepare(
|
||||
`SELECT publicKey, variant, data from ${CONFIG_DUMP_TABLE} WHERE variant = $variant AND publicKey = $publicKey;`
|
||||
`SELECT publicKey, variant, data FROM ${CONFIG_DUMP_TABLE} WHERE variant = $variant AND publicKey = $publicKey;`
|
||||
)
|
||||
.all({
|
||||
publicKey,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-await-in-loop */
|
||||
import { ContactInfo } from 'libsession_util_nodejs';
|
||||
import { compact, difference, isEmpty, isNumber, toNumber } from 'lodash';
|
||||
import { compact, difference, isEmpty, isNil, isNumber, toNumber } from 'lodash';
|
||||
import { ConfigDumpData } from '../data/configDump/configDump';
|
||||
import { Data } from '../data/data';
|
||||
import { SettingsKey } from '../data/settings-key';
|
||||
|
@ -39,8 +39,9 @@ import {
|
|||
isSignInByLinking,
|
||||
setLastProfileUpdateTimestamp,
|
||||
} from '../util/storage';
|
||||
import { deleteAllMessagesByConvoIdNoConfirmation } from '../interactions/conversationInteractions';
|
||||
// eslint-disable-next-line import/no-unresolved, import/extensions
|
||||
import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions';
|
||||
import { ConfigWrapperObjectTypes } from '../../ts/webworker/workers/browser/libsession_worker_functions';
|
||||
import {
|
||||
ContactsWrapperActions,
|
||||
ConvoInfoVolatileWrapperActions,
|
||||
|
@ -53,7 +54,6 @@ import { addKeyPairToCacheAndDBIfNeeded, handleNewClosedGroup } from './closedGr
|
|||
import { HexKeyPair } from './keypairs';
|
||||
import { queueAllCachedFromSource } from './receiver';
|
||||
import { EnvelopePlus } from './types';
|
||||
import { deleteAllMessagesByConvoIdNoConfirmation } from '../interactions/conversationInteractions';
|
||||
|
||||
function groupByVariant(
|
||||
incomingConfigs: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
|
||||
|
@ -197,8 +197,16 @@ async function handleUserProfileUpdate(result: IncomingConfResult): Promise<Inco
|
|||
if (!updateUserInfo) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const currentBlindedMsgRequest = Storage.get(SettingsKey.hasBlindedMsgRequestsEnabled);
|
||||
const newBlindedMsgRequest = await UserConfigWrapperActions.getEnableBlindedMsgRequest();
|
||||
if (!isNil(newBlindedMsgRequest) && newBlindedMsgRequest !== currentBlindedMsgRequest) {
|
||||
await window.setSettingValue(SettingsKey.hasBlindedMsgRequestsEnabled, newBlindedMsgRequest); // this does the dispatch to redux
|
||||
}
|
||||
|
||||
const picUpdate = !isEmpty(updateUserInfo.key) && !isEmpty(updateUserInfo.url);
|
||||
|
||||
// NOTE: if you do any changes to the settings of a user which are synced, it should be done above the `updateOurProfileLegacyOrViaLibSession` call
|
||||
await updateOurProfileLegacyOrViaLibSession(
|
||||
result.latestEnvelopeTimestamp,
|
||||
updateUserInfo.name,
|
||||
|
|
|
@ -50,14 +50,14 @@ const handleOpenGroupMessage = async (
|
|||
|
||||
const dataUint = new Uint8Array(removeMessagePadding(arr));
|
||||
|
||||
const decoded = SignalService.Content.decode(dataUint);
|
||||
const decodedContent = SignalService.Content.decode(dataUint);
|
||||
|
||||
const conversationId = getOpenGroupV2ConversationId(serverUrl, roomId);
|
||||
if (!conversationId) {
|
||||
window?.log?.error('We cannot handle a message without a conversationId');
|
||||
return;
|
||||
}
|
||||
const idataMessage = decoded?.dataMessage;
|
||||
const idataMessage = decodedContent?.dataMessage;
|
||||
if (!idataMessage) {
|
||||
window?.log?.error('Invalid decoded opengroup message: no dataMessage');
|
||||
return;
|
||||
|
@ -101,7 +101,9 @@ const handleOpenGroupMessage = async (
|
|||
await handleMessageJob(
|
||||
msgModel,
|
||||
groupConvo,
|
||||
toRegularMessage(cleanIncomingDataMessage(decoded?.dataMessage as SignalService.DataMessage)),
|
||||
toRegularMessage(
|
||||
cleanIncomingDataMessage(decodedContent?.dataMessage as SignalService.DataMessage)
|
||||
),
|
||||
noop,
|
||||
sender,
|
||||
''
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import _ from 'lodash';
|
||||
import _, { isNumber } from 'lodash';
|
||||
|
||||
import { queueAttachmentDownloads } from './attachments';
|
||||
|
||||
|
@ -18,6 +18,7 @@ import { GoogleChrome } from '../util';
|
|||
import { LinkPreviews } from '../util/linkPreviews';
|
||||
import { ReleasedFeatures } from '../util/releaseFeature';
|
||||
import { PropsForMessageWithoutConvoProps, lookupQuote } from '../state/ducks/conversations';
|
||||
import { PubKey } from '../session/types';
|
||||
|
||||
function contentTypeSupported(type: string): boolean {
|
||||
const Chrome = GoogleChrome;
|
||||
|
@ -198,6 +199,7 @@ export type RegularMessageType = Pick<
|
|||
| 'profile'
|
||||
| 'profileKey'
|
||||
| 'expireTimer'
|
||||
| 'blocksCommunityMessageRequests'
|
||||
> & { isRegularMessage: true };
|
||||
|
||||
/**
|
||||
|
@ -216,6 +218,7 @@ export function toRegularMessage(rawDataMessage: SignalService.DataMessage): Reg
|
|||
'quote',
|
||||
'profile',
|
||||
'expireTimer',
|
||||
'blocksCommunityMessageRequests',
|
||||
]),
|
||||
isRegularMessage: true,
|
||||
};
|
||||
|
@ -254,6 +257,18 @@ async function handleRegularMessage(
|
|||
message.set({ expireTimer: existingExpireTimer });
|
||||
}
|
||||
|
||||
const serverTimestamp = message.get('serverTimestamp');
|
||||
if (
|
||||
conversation.isPublic() &&
|
||||
PubKey.isBlinded(sendingDeviceConversation.id) &&
|
||||
isNumber(serverTimestamp)
|
||||
) {
|
||||
const updateBlockTimestamp = !rawDataMessage.blocksCommunityMessageRequests
|
||||
? 0
|
||||
: serverTimestamp;
|
||||
await sendingDeviceConversation.updateBlocksSogsMsgReqsTimestamp(updateBlockTimestamp, false);
|
||||
}
|
||||
|
||||
// Expire timer updates are now explicit.
|
||||
// We don't handle an expire timer from a incoming message except if it is an ExpireTimerUpdate message.
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ import {
|
|||
openConversationWithMessages,
|
||||
} from '../../../../state/ducks/conversations';
|
||||
import { roomHasBlindEnabled } from '../../../../types/sqlSharedTypes';
|
||||
import { Storage } from '../../../../util/storage';
|
||||
import { SettingsKey } from '../../../../data/settings-key';
|
||||
|
||||
export type OpenGroupMessageV4 = {
|
||||
/** AFAIK: indicates the number of the message in the group. e.g. 2nd message will be 1 or 2 */
|
||||
|
@ -244,12 +246,14 @@ export class OpenGroupServerPoller {
|
|||
if (roomHasBlindEnabled(rooms[0])) {
|
||||
const maxInboxId = Math.max(...rooms.map(r => r.lastInboxIdFetched || 0));
|
||||
|
||||
// This only works for servers with blinding capabilities
|
||||
// adding inbox subrequest info
|
||||
subrequestOptions.push({
|
||||
type: 'inbox',
|
||||
inboxSince: { id: isNumber(maxInboxId) && maxInboxId > 0 ? maxInboxId : undefined },
|
||||
});
|
||||
if (Storage.get(SettingsKey.hasBlindedMsgRequestsEnabled)) {
|
||||
// This only works for servers with blinding capabilities
|
||||
// adding inbox subrequest info
|
||||
subrequestOptions.push({
|
||||
type: 'inbox',
|
||||
inboxSince: { id: isNumber(maxInboxId) && maxInboxId > 0 ? maxInboxId : undefined },
|
||||
});
|
||||
}
|
||||
|
||||
const maxOutboxId = Math.max(...rooms.map(r => r.lastOutboxIdFetched || 0));
|
||||
|
||||
|
|
|
@ -1,3 +1,25 @@
|
|||
import { VisibleMessage } from './VisibleMessage';
|
||||
import { SettingsKey } from '../../../../data/settings-key';
|
||||
import { SignalService } from '../../../../protobuf';
|
||||
import { Storage } from '../../../../util/storage';
|
||||
import { VisibleMessage, VisibleMessageParams } from './VisibleMessage';
|
||||
|
||||
export class OpenGroupVisibleMessage extends VisibleMessage {}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export type OpenGroupVisibleMessageParams = VisibleMessageParams & {};
|
||||
|
||||
export class OpenGroupVisibleMessage extends VisibleMessage {
|
||||
private readonly blocksCommunityMessageRequests: boolean;
|
||||
|
||||
constructor(params: OpenGroupVisibleMessageParams) {
|
||||
super(params);
|
||||
// they are the opposite of each others
|
||||
this.blocksCommunityMessageRequests = !Storage.get(SettingsKey.hasBlindedMsgRequestsEnabled);
|
||||
}
|
||||
|
||||
public dataProto(): SignalService.DataMessage {
|
||||
const dataMessage = super.dataProto();
|
||||
|
||||
dataMessage.blocksCommunityMessageRequests = this.blocksCommunityMessageRequests;
|
||||
|
||||
return dataMessage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import { UserConfigWrapperActions } from '../../../webworker/workers/browser/lib
|
|||
import { getConversationController } from '../../conversations';
|
||||
import { fromHexToArray } from '../String';
|
||||
import { CONVERSATION_PRIORITIES } from '../../../models/conversationAttributes';
|
||||
import { Storage } from '../../../util/storage';
|
||||
import { SettingsKey } from '../../../data/settings-key';
|
||||
|
||||
async function insertUserProfileIntoWrapper(convoId: string) {
|
||||
if (!isUserProfileToStoreInWrapper(convoId)) {
|
||||
|
@ -21,10 +23,12 @@ async function insertUserProfileIntoWrapper(convoId: string) {
|
|||
const dbProfileKey = fromHexToArray(ourConvo.get('profileKey') || '');
|
||||
const priority = ourConvo.get('priority') || CONVERSATION_PRIORITIES.default;
|
||||
|
||||
const areBlindedMsgRequestEnabled = !!Storage.get(SettingsKey.hasBlindedMsgRequestsEnabled);
|
||||
|
||||
window.log.debug(
|
||||
`inserting into userprofile wrapper: username:"${dbName}", priority:${priority} image:${JSON.stringify(
|
||||
{ url: dbProfileUrl, key: dbProfileKey }
|
||||
)} `
|
||||
)}, settings: ${JSON.stringify({ areBlindedMsgRequestEnabled })}`
|
||||
);
|
||||
// const expirySeconds = ourConvo.get('expireTimer') || 0;
|
||||
if (dbProfileUrl && !isEmpty(dbProfileKey)) {
|
||||
|
@ -40,6 +44,7 @@ async function insertUserProfileIntoWrapper(convoId: string) {
|
|||
} else {
|
||||
await UserConfigWrapperActions.setUserInfo(dbName, priority, null); // expirySeconds
|
||||
}
|
||||
await UserConfigWrapperActions.setEnableBlindedMsgRequest(areBlindedMsgRequestEnabled);
|
||||
}
|
||||
|
||||
function isUserProfileToStoreInWrapper(convoId: string) {
|
||||
|
|
|
@ -258,6 +258,8 @@ export interface ReduxConversationType {
|
|||
didApproveMe?: boolean;
|
||||
|
||||
isMarkedUnread?: boolean;
|
||||
|
||||
blocksSogsMsgReqsTimestamp?: number; // undefined means 0
|
||||
}
|
||||
|
||||
export interface NotificationForConvoOption {
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Storage } from '../../util/storage';
|
|||
const SettingsBoolsKeyTrackedInRedux = [
|
||||
SettingsKey.someDeviceOutdatedSyncing,
|
||||
SettingsKey.settingsLinkPreview,
|
||||
SettingsKey.hasBlindedMsgRequestsEnabled,
|
||||
] as const;
|
||||
|
||||
export type SettingsState = {
|
||||
|
@ -18,6 +19,7 @@ export function getSettingsInitialState() {
|
|||
settingsBools: {
|
||||
someDeviceOutdatedSyncing: false,
|
||||
'link-preview-setting': false, // this is the value of SettingsKey.settingsLinkPreview
|
||||
hasBlindedMsgRequestsEnabled: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -41,10 +43,17 @@ const settingsSlice = createSlice({
|
|||
updateAllOnStorageReady(state) {
|
||||
const linkPreview = Storage.get(SettingsKey.settingsLinkPreview, false);
|
||||
const outdatedSync = Storage.get(SettingsKey.someDeviceOutdatedSyncing, false);
|
||||
const hasBlindedMsgRequestsEnabled = Storage.get(
|
||||
SettingsKey.hasBlindedMsgRequestsEnabled,
|
||||
false
|
||||
);
|
||||
state.settingsBools.someDeviceOutdatedSyncing = isBoolean(outdatedSync)
|
||||
? outdatedSync
|
||||
: false;
|
||||
state.settingsBools['link-preview-setting'] = isBoolean(linkPreview) ? linkPreview : false; // this is the value of SettingsKey.settingsLinkPreview
|
||||
state.settingsBools.hasBlindedMsgRequestsEnabled = isBoolean(hasBlindedMsgRequestsEnabled)
|
||||
? hasBlindedMsgRequestsEnabled
|
||||
: false;
|
||||
return state;
|
||||
},
|
||||
updateSettingsBoolValue(state, action: PayloadAction<{ id: string; value: boolean }>) {
|
||||
|
|
|
@ -80,7 +80,35 @@ export function getSelectedCanWrite(state: StateType) {
|
|||
const canWriteSogs = getCanWrite(state, selectedConvoPubkey);
|
||||
const { isBlocked, isKickedFromGroup, left, isPublic } = selectedConvo;
|
||||
|
||||
return !(isBlocked || isKickedFromGroup || left || (isPublic && !canWriteSogs));
|
||||
const readOnlySogs = isPublic && !canWriteSogs;
|
||||
|
||||
const isBlindedAndDisabledMsgRequests = getSelectedBlindedDisabledMsgRequests(state); // true if isPrivate, blinded and explicitely disabled msgreq
|
||||
|
||||
return !(
|
||||
isBlocked ||
|
||||
isKickedFromGroup ||
|
||||
left ||
|
||||
readOnlySogs ||
|
||||
isBlindedAndDisabledMsgRequests
|
||||
);
|
||||
}
|
||||
|
||||
function getSelectedBlindedDisabledMsgRequests(state: StateType) {
|
||||
const selectedConvoPubkey = getSelectedConversationKey(state);
|
||||
if (!selectedConvoPubkey) {
|
||||
return false;
|
||||
}
|
||||
const selectedConvo = getSelectedConversation(state);
|
||||
if (!selectedConvo) {
|
||||
return false;
|
||||
}
|
||||
const { blocksSogsMsgReqsTimestamp, isPrivate } = selectedConvo;
|
||||
|
||||
const isBlindedAndDisabledMsgRequests = Boolean(
|
||||
isPrivate && PubKey.isBlinded(selectedConvoPubkey) && blocksSogsMsgReqsTimestamp
|
||||
);
|
||||
|
||||
return isBlindedAndDisabledMsgRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,6 +184,10 @@ export function useSelectedApprovedMe() {
|
|||
return useSelector(getSelectedApprovedMe);
|
||||
}
|
||||
|
||||
export function useSelectedHasDisabledBlindedMsgRequests() {
|
||||
return useSelector(getSelectedBlindedDisabledMsgRequests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given arguments corresponds to a private contact which is approved both sides. i.e. a friend.
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,9 @@ const getLinkPreviewEnabled = (state: StateType) =>
|
|||
const getHasDeviceOutdatedSyncing = (state: StateType) =>
|
||||
state.settings.settingsBools[SettingsKey.someDeviceOutdatedSyncing];
|
||||
|
||||
const getHasBlindedMsgRequestsEnabled = (state: StateType) =>
|
||||
state.settings.settingsBools[SettingsKey.hasBlindedMsgRequestsEnabled];
|
||||
|
||||
export const useHasLinkPreviewEnabled = () => {
|
||||
const value = useSelector(getLinkPreviewEnabled);
|
||||
return Boolean(value);
|
||||
|
@ -17,3 +20,8 @@ export const useHasDeviceOutdatedSyncing = () => {
|
|||
const value = useSelector(getHasDeviceOutdatedSyncing);
|
||||
return Boolean(value);
|
||||
};
|
||||
|
||||
export const useHasBlindedMsgRequestsEnabled = () => {
|
||||
const value = useSelector(getHasBlindedMsgRequestsEnabled);
|
||||
return Boolean(value);
|
||||
};
|
||||
|
|
|
@ -442,6 +442,8 @@ export type LocalizerKeys =
|
|||
| 'clearAll'
|
||||
| 'clearDataSettingsTitle'
|
||||
| 'messageRequests'
|
||||
| 'blindedMsgReqsSettingTitle'
|
||||
| 'blindedMsgReqsSettingDesc'
|
||||
| 'requestsSubtitle'
|
||||
| 'requestsPlaceholder'
|
||||
| 'hideRequestBannerDescription'
|
||||
|
@ -489,6 +491,7 @@ export type LocalizerKeys =
|
|||
| 'clearAllConfirmationTitle'
|
||||
| 'clearAllConfirmationBody'
|
||||
| 'noMessagesInReadOnly'
|
||||
| 'noMessagesInBlindedDisabledMsgRequests'
|
||||
| 'noMessagesInNoteToSelf'
|
||||
| 'noMessagesInEverythingElse'
|
||||
| 'hideBanner'
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/* eslint-disable import/extensions */
|
||||
/* eslint-disable import/no-unresolved */
|
||||
// eslint-disable-next-line camelcase
|
||||
import { ContactInfoSet, LegacyGroupInfo, LegacyGroupMemberInfo } from 'libsession_util_nodejs';
|
||||
import { from_hex } from 'libsodium-wrappers-sumo';
|
||||
import { isArray, isEmpty, isEqual } from 'lodash';
|
||||
import { ContactInfoSet, LegacyGroupInfo, LegacyGroupMemberInfo } from 'libsession_util_nodejs';
|
||||
import { OpenGroupV2Room } from '../data/opengroups';
|
||||
import { ConversationAttributes } from '../models/conversationAttributes';
|
||||
import { OpenGroupRequestCommonType } from '../session/apis/open_group_api/opengroupV2/ApiUtil';
|
||||
|
|
|
@ -107,7 +107,7 @@ export const UserConfigWrapperActions: UserConfigWrapperActionsCalls = {
|
|||
name: string,
|
||||
priority: number,
|
||||
profilePic: { url: string; key: Uint8Array } | null
|
||||
// expireSeconds: number
|
||||
// expireSeconds: number,
|
||||
) =>
|
||||
callLibSessionWorker([
|
||||
'UserConfig',
|
||||
|
@ -117,6 +117,17 @@ export const UserConfigWrapperActions: UserConfigWrapperActionsCalls = {
|
|||
profilePic,
|
||||
// expireSeconds,
|
||||
]) as Promise<ReturnType<UserConfigWrapperActionsCalls['setUserInfo']>>,
|
||||
|
||||
getEnableBlindedMsgRequest: async () =>
|
||||
callLibSessionWorker(['UserConfig', 'getEnableBlindedMsgRequest']) as Promise<
|
||||
ReturnType<UserConfigWrapperActionsCalls['getEnableBlindedMsgRequest']>
|
||||
>,
|
||||
setEnableBlindedMsgRequest: async (blindedMsgRequests: boolean) =>
|
||||
callLibSessionWorker([
|
||||
'UserConfig',
|
||||
'setEnableBlindedMsgRequest',
|
||||
blindedMsgRequests,
|
||||
]) as Promise<ReturnType<UserConfigWrapperActionsCalls['setEnableBlindedMsgRequest']>>,
|
||||
};
|
||||
|
||||
export const ContactsWrapperActions: ContactsWrapperActionsCalls = {
|
||||
|
|
|
@ -4793,9 +4793,9 @@ levn@~0.3.0:
|
|||
prelude-ls "~1.1.2"
|
||||
type-check "~0.3.2"
|
||||
|
||||
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.5/libsession_util_nodejs-v0.2.5.tar.gz":
|
||||
version "0.2.5"
|
||||
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.5/libsession_util_nodejs-v0.2.5.tar.gz#e1db928eaca58f22c66494c6179e13bdd4e38cd4"
|
||||
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.6/libsession_util_nodejs-v0.2.6.tar.gz":
|
||||
version "0.2.6"
|
||||
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.2.6/libsession_util_nodejs-v0.2.6.tar.gz#79574dac7d24957c44376397201fc6fa1d4a45ee"
|
||||
dependencies:
|
||||
cmake-js "^7.2.1"
|
||||
node-addon-api "^6.1.0"
|
||||
|
|
Loading…
Reference in a new issue