You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
8.5 KiB
TypeScript
287 lines
8.5 KiB
TypeScript
/* eslint-disable import/extensions */
|
|
/* eslint-disable import/no-unresolved */
|
|
// eslint-disable-next-line camelcase
|
|
import {
|
|
ContactInfoSet,
|
|
DisappearingMessageConversationModeType,
|
|
LegacyGroupInfo,
|
|
LegacyGroupMemberInfo,
|
|
} from 'libsession_util_nodejs';
|
|
import { from_hex } from 'libsodium-wrappers-sumo';
|
|
import { isArray, isEmpty, isEqual } from 'lodash';
|
|
import { fromHexToArray } from '../session/utils/String';
|
|
import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions';
|
|
import { OpenGroupRequestCommonType, OpenGroupV2Room } from '../data/types';
|
|
|
|
/**
|
|
* This wrapper can be used to make a function type not async, asynced.
|
|
* We use it in the typing of the database communication, because the data calls (renderer side) have essentially the same signature of the sql calls (node side), with an added `await`
|
|
*/
|
|
export type AsyncWrapper<T extends (...args: any) => any> = (
|
|
...args: Parameters<T>
|
|
) => Promise<ReturnType<T>>;
|
|
|
|
/**
|
|
* This type is used to build from an objectType filled with functions, a new object type where all the functions their async equivalent
|
|
*/
|
|
export type AsyncObjectWrapper<Type extends Record<string, (...args: any) => any>> = {
|
|
[Property in keyof Type]: AsyncWrapper<Type[Property]>;
|
|
};
|
|
|
|
export type MsgDuplicateSearchOpenGroup = Array<{
|
|
sender: string;
|
|
serverTimestamp: number;
|
|
// senderBlinded?: string; // for a message we sent, we need a blinded id and an unblinded one
|
|
}>;
|
|
|
|
export type UpdateLastHashType = {
|
|
convoId: string;
|
|
snode: string;
|
|
hash: string;
|
|
expiresAt: number;
|
|
namespace: number;
|
|
};
|
|
|
|
export type ConfigDumpRow = {
|
|
variant: ConfigWrapperObjectTypes; // the variant this entry is about. (user pr, contacts, ...)
|
|
publicKey: string; // either our pubkey if a dump for our own swarm or the closed group pubkey
|
|
data: Uint8Array; // the blob returned by libsession.dump() call
|
|
};
|
|
|
|
export type ConfigDumpRowWithoutData = Pick<ConfigDumpRow, 'publicKey' | 'variant'>;
|
|
|
|
export const CONFIG_DUMP_TABLE = 'configDump';
|
|
|
|
// ========== configdump
|
|
|
|
export type ConfigDumpDataNode = {
|
|
getByVariantAndPubkey: (
|
|
variant: ConfigWrapperObjectTypes,
|
|
publicKey: string
|
|
) => Array<ConfigDumpRow>;
|
|
saveConfigDump: (dump: ConfigDumpRow) => void;
|
|
|
|
getAllDumpsWithData: () => Array<ConfigDumpRow>;
|
|
getAllDumpsWithoutData: () => Array<ConfigDumpRowWithoutData>;
|
|
};
|
|
|
|
// ========== unprocessed
|
|
|
|
export type UnprocessedParameter = {
|
|
id: string;
|
|
version: number;
|
|
envelope: string;
|
|
timestamp: number;
|
|
// serverTimestamp: number;
|
|
attempts: number;
|
|
messageHash: string;
|
|
senderIdentity?: string;
|
|
decrypted?: string; // added once the envelopes's content is decrypted with updateCacheWithDecryptedContent
|
|
source?: string; // added once the envelopes's content is decrypted with updateCacheWithDecryptedContent
|
|
};
|
|
|
|
export type UnprocessedDataNode = {
|
|
saveUnprocessed: (data: UnprocessedParameter) => void;
|
|
updateUnprocessedAttempts: (id: string, attempts: number) => void;
|
|
updateUnprocessedWithData: (id: string, data: UnprocessedParameter) => void;
|
|
getUnprocessedById: (id: string) => UnprocessedParameter | undefined;
|
|
getUnprocessedCount: () => number;
|
|
getAllUnprocessed: () => Array<UnprocessedParameter>;
|
|
removeUnprocessed: (id: string) => void;
|
|
removeAllUnprocessed: () => void;
|
|
};
|
|
|
|
// ======== attachment downloads
|
|
|
|
export type AttachmentDownloadMessageDetails = {
|
|
messageId: string;
|
|
type: 'preview' | 'quote' | 'attachment';
|
|
index: number;
|
|
isOpenGroupV2: boolean;
|
|
openGroupV2Details: OpenGroupRequestCommonType | undefined;
|
|
};
|
|
|
|
export type SaveConversationReturn = {
|
|
unreadCount: number;
|
|
mentionedUs: boolean;
|
|
lastReadTimestampMessage: number | null;
|
|
} | null;
|
|
|
|
/**
|
|
* NOTE This code should always match the last known version of the same function used in a libsession migration (V34)
|
|
*
|
|
* This function returns a contactInfo for the wrapper to understand from the DB values.
|
|
* Created in this file so we can reuse it during the migration (node side), and from the renderer side
|
|
*/
|
|
export function getContactInfoFromDBValues({
|
|
id,
|
|
dbApproved,
|
|
dbApprovedMe,
|
|
dbBlocked,
|
|
dbName,
|
|
dbNickname,
|
|
priority,
|
|
dbProfileUrl,
|
|
dbProfileKey,
|
|
dbCreatedAtSeconds,
|
|
expirationMode,
|
|
expireTimer,
|
|
}: {
|
|
id: string;
|
|
dbApproved: boolean;
|
|
dbApprovedMe: boolean;
|
|
dbBlocked: boolean;
|
|
dbNickname: string | undefined;
|
|
dbName: string | undefined;
|
|
priority: number;
|
|
dbProfileUrl: string | undefined;
|
|
dbProfileKey: string | undefined;
|
|
dbCreatedAtSeconds: number;
|
|
expirationMode: DisappearingMessageConversationModeType | undefined;
|
|
expireTimer: number | undefined;
|
|
}): ContactInfoSet {
|
|
const wrapperContact: ContactInfoSet = {
|
|
id,
|
|
approved: !!dbApproved,
|
|
approvedMe: !!dbApprovedMe,
|
|
blocked: !!dbBlocked,
|
|
priority,
|
|
nickname: dbNickname,
|
|
name: dbName,
|
|
createdAtSeconds: dbCreatedAtSeconds,
|
|
expirationMode,
|
|
expirationTimerSeconds: !!expireTimer && expireTimer > 0 ? expireTimer : 0,
|
|
};
|
|
|
|
if (
|
|
wrapperContact.profilePicture?.url !== dbProfileUrl ||
|
|
!isEqual(wrapperContact.profilePicture?.key, dbProfileKey)
|
|
) {
|
|
wrapperContact.profilePicture = {
|
|
url: dbProfileUrl || null,
|
|
key: dbProfileKey && !isEmpty(dbProfileKey) ? fromHexToArray(dbProfileKey) : null,
|
|
};
|
|
}
|
|
|
|
return wrapperContact;
|
|
}
|
|
|
|
export type CommunityInfoFromDBValues = {
|
|
priority: number;
|
|
fullUrl: string;
|
|
};
|
|
|
|
/**
|
|
* NOTE This code should always match the last known version of the same function used in a libsession migration (V31)
|
|
*
|
|
* This function returns a CommunityInfo for the wrapper to understand from the DB values.
|
|
* It is created in this file so we can reuse it during the migration (node side), and from the renderer side
|
|
*/
|
|
export function getCommunityInfoFromDBValues({
|
|
priority,
|
|
fullUrl,
|
|
}: {
|
|
priority: number;
|
|
fullUrl: string;
|
|
}): CommunityInfoFromDBValues {
|
|
const community = {
|
|
fullUrl,
|
|
priority: priority || 0,
|
|
};
|
|
|
|
return community;
|
|
}
|
|
|
|
export function maybeArrayJSONtoArray(arr: string | Array<string>): Array<string> {
|
|
try {
|
|
if (isArray(arr)) {
|
|
return arr;
|
|
}
|
|
|
|
const parsed = JSON.parse(arr);
|
|
if (isArray(parsed)) {
|
|
return parsed;
|
|
}
|
|
return [];
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* NOTE This code should always match the last known version of the same function used in a libsession migration (V34)
|
|
*/
|
|
export function getLegacyGroupInfoFromDBValues({
|
|
id,
|
|
priority,
|
|
members: maybeMembers,
|
|
displayNameInProfile,
|
|
expirationMode,
|
|
expireTimer,
|
|
encPubkeyHex,
|
|
encSeckeyHex,
|
|
groupAdmins: maybeAdmins,
|
|
lastJoinedTimestamp,
|
|
}: {
|
|
id: string;
|
|
priority: number;
|
|
displayNameInProfile: string | undefined;
|
|
expirationMode: DisappearingMessageConversationModeType | undefined;
|
|
expireTimer: number | undefined;
|
|
encPubkeyHex: string;
|
|
encSeckeyHex: string;
|
|
members: string | Array<string>;
|
|
groupAdmins: string | Array<string>;
|
|
lastJoinedTimestamp: number;
|
|
}) {
|
|
const admins: Array<string> = maybeArrayJSONtoArray(maybeAdmins);
|
|
const members: Array<string> = maybeArrayJSONtoArray(maybeMembers);
|
|
|
|
const wrappedMembers: Array<LegacyGroupMemberInfo> = (members || []).map(m => {
|
|
return {
|
|
isAdmin: admins.includes(m),
|
|
pubkeyHex: m,
|
|
};
|
|
});
|
|
|
|
const legacyGroup: LegacyGroupInfo = {
|
|
pubkeyHex: id,
|
|
name: displayNameInProfile || '',
|
|
priority: priority || 0,
|
|
members: wrappedMembers,
|
|
disappearingTimerSeconds:
|
|
expirationMode && expirationMode !== 'off' && !!expireTimer && expireTimer > 0
|
|
? expireTimer
|
|
: 0,
|
|
encPubkey: !isEmpty(encPubkeyHex) ? from_hex(encPubkeyHex) : new Uint8Array(),
|
|
encSeckey: !isEmpty(encSeckeyHex) ? from_hex(encSeckeyHex) : new Uint8Array(),
|
|
joinedAtSeconds: Math.floor(lastJoinedTimestamp / 1000),
|
|
};
|
|
|
|
return legacyGroup;
|
|
}
|
|
|
|
/**
|
|
* This function can be used to make sure all the possible values as input of a switch as taken care off, without having a default case.
|
|
*
|
|
*/
|
|
export function assertUnreachable(_x: never, message: string): never {
|
|
const msg = `assertUnreachable: Didn't expect to get here with "${message}"`;
|
|
// eslint:disable: no-console
|
|
// eslint-disable-next-line no-console
|
|
console.info(msg);
|
|
throw new Error(msg);
|
|
}
|
|
|
|
export function roomHasBlindEnabled(openGroup?: OpenGroupV2Room) {
|
|
return capabilitiesListHasBlindEnabled(openGroup?.capabilities);
|
|
}
|
|
|
|
export function capabilitiesListHasBlindEnabled(caps?: Array<string> | null) {
|
|
return Boolean(caps?.includes('blind'));
|
|
}
|
|
|
|
export function roomHasReactionsEnabled(openGroup?: OpenGroupV2Room) {
|
|
return Boolean(openGroup?.capabilities?.includes('reactions'));
|
|
}
|