diff --git a/include/groups/meta_group_wrapper.hpp b/include/groups/meta_group_wrapper.hpp index 9b53ec0..dead88e 100644 --- a/include/groups/meta_group_wrapper.hpp +++ b/include/groups/meta_group_wrapper.hpp @@ -62,7 +62,6 @@ class MetaGroupWrapper : public Napi::ObjectWrap { Napi::Value memberGetOrConstruct(const Napi::CallbackInfo& info); Napi::Value memberConstructAndSet(const Napi::CallbackInfo& info); - void memberSetNameTruncated(const Napi::CallbackInfo& info); void memberSetInviteFailed(const Napi::CallbackInfo& info); void memberSetInviteSent(const Napi::CallbackInfo& info); void memberSetInviteNotSent(const Napi::CallbackInfo& info); @@ -71,7 +70,7 @@ class MetaGroupWrapper : public Napi::ObjectWrap { void memberSetPromotionSent(const Napi::CallbackInfo& info); void memberSetPromotionFailed(const Napi::CallbackInfo& info); void memberSetPromotionAccepted(const Napi::CallbackInfo& info); - void memberSetProfilePicture(const Napi::CallbackInfo& info); + void memberSetProfileDetails(const Napi::CallbackInfo& info); Napi::Value memberResetAllSendingState(const Napi::CallbackInfo& info); void memberSetSupplement(const Napi::CallbackInfo& info); Napi::Value memberEraseAndRekey(const Napi::CallbackInfo& info); diff --git a/include/profile_pic.hpp b/include/profile_pic.hpp index c959571..e1b2c67 100644 --- a/include/profile_pic.hpp +++ b/include/profile_pic.hpp @@ -5,22 +5,6 @@ namespace session::nodeapi { -// Returns {"url": "...", "key": buffer} object; both values will be Null if the pic is not set. - -template <> -struct toJs_impl { - Napi::Object operator()(const Napi::Env& env, const config::profile_pic& pic) { - auto obj = Napi::Object::New(env); - if (pic) { - obj["url"] = toJs(env, pic.url); - obj["key"] = toJs(env, pic.key); - } else { - obj["url"] = env.Null(); - obj["key"] = env.Null(); - } - return obj; - } -}; // Constructs a profile_pic from a Napi::Value which must be either Null or an Object; if an // Object then it *must* contain "url" (string or null) and "key" (uint8array of size 32 or diff --git a/include/user_config.hpp b/include/user_config.hpp index 271fd1e..d6099d1 100644 --- a/include/user_config.hpp +++ b/include/user_config.hpp @@ -20,10 +20,15 @@ class UserConfigWrapper : public ConfigBaseImpl, public Napi::ObjectWrap #include "session/config/namespaces.hpp" +#include "session/config/profile_pic.hpp" #include "session/types.hpp" #include "utilities.hpp" @@ -59,11 +60,15 @@ std::vector toCppBuffer(Napi::Value x, const std::string& identif int64_t toCppInteger(Napi::Value x, const std::string& identifier, bool allowUndefined = false); std::optional maybeNonemptyInt(Napi::Value x, const std::string& identifier); std::optional maybeNonemptyBoolean(Napi::Value x, const std::string& identifier); +std::optional maybeNonemptySysSeconds( + Napi::Value x, const std::string& identifier); + +std::chrono::sys_seconds toCppSysSeconds(Napi::Value x, const std::string& identifier); bool toCppBoolean(Napi::Value x, const std::string& identifier); -// If the object is null/undef/empty returns nullopt, otherwise if a String returns a std::string of -// the value. Throws if something else. +// If the object is null/undef/empty returns nullopt, otherwise if a String returns a +// std::string of the value. Throws if something else. std::optional maybeNonemptyString(Napi::Value x, const std::string& identifier); // If the object is null/undef/empty returns nullopt, otherwise if a Uint8Array returns a @@ -71,8 +76,8 @@ std::optional maybeNonemptyString(Napi::Value x, const std::string& std::optional> maybeNonemptyBuffer( Napi::Value x, const std::string& identifier); -// Implementation struct of toJs(); we add specializations of this for any C++ types we want to be -// able to convert into JS types. +// Implementation struct of toJs(); we add specializations of this for any C++ types we want to +// be able to convert into JS types. template struct toJs_impl { // If this gets instantiated it means we're missing a specialization and so fail to compile: @@ -176,6 +181,30 @@ struct toJs_impl> { } }; +template <> +struct toJs_impl { + auto operator()(const Napi::Env& env, std::chrono::sys_seconds t) const { + return Napi::Number::New(env, t.time_since_epoch().count()); + } +}; + +// Returns {"url": "...", "key": buffer} object; both values will be Null if the pic is not set. + +template <> +struct toJs_impl { + auto operator()(const Napi::Env& env, const config::profile_pic& pic) const { + auto obj = Napi::Object::New(env); + if (pic) { + obj["url"] = toJs(env, pic.url); + obj["key"] = toJs(env, pic.key); + } else { + obj["url"] = env.Null(); + obj["key"] = env.Null(); + } + return obj; + } +}; + // Helper for various "get_all" functions that copy [it...end) into a Napi::Array via toJs(). // Throws a Napi::Error on any exception. template @@ -192,8 +221,8 @@ static Napi::Array get_all_impl(const Napi::CallbackInfo& info, size_t size, It }); } -// Wraps a string in an optional which will be nullopt if the input string is empty. -// This is particularly useful with `toJs` to convert empty strings into Null. +// Wraps a string in an optional which will be nullopt if the input string is +// empty. This is particularly useful with `toJs` to convert empty strings into Null. inline std::optional maybe_string(std::string_view val) { if (val.empty()) return std::nullopt; @@ -231,8 +260,8 @@ auto wrapResult(const Napi::Env& env, Call&& call) { } } -// Similar to wrapResult(), but a small shortcut to allow passing `info` instead of `info.Env()` as -// the first argument. +// Similar to wrapResult(), but a small shortcut to allow passing `info` instead of `info.Env()` +// as the first argument. template auto wrapResult(const Napi::CallbackInfo& info, Call&& call) { return wrapResult(info.Env(), std::forward(call)); @@ -262,6 +291,10 @@ std::string printable(std::span x); * Keep the current priority if a wrapper */ int64_t toPriority(Napi::Value x, int64_t currentPriority); +int64_t toPriority(int64_t newPriority, int64_t currentPriority); + +std::optional maybeNonemptyProfilePic( + Napi::Value x, const std::string& identifier); int64_t unix_timestamp_now(); diff --git a/libsession-util b/libsession-util index 95b9fe7..5e3835b 160000 --- a/libsession-util +++ b/libsession-util @@ -1 +1 @@ -Subproject commit 95b9fe74d1c419487f6e69458db1ebc2bb824a2a +Subproject commit 5e3835bcfa7202cf307bc299bb1e40e160e8d357 diff --git a/package.json b/package.json index cc4c388..f37bb27 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "main": "index.js", "name": "libsession_util_nodejs", "description": "Wrappers for the Session Util Library", - "version": "0.5.5", + "version": "0.5.6", "license": "GPL-3.0", "author": { "name": "Oxen Project", diff --git a/src/contacts_config.cpp b/src/contacts_config.cpp index 19c9593..6572d60 100644 --- a/src/contacts_config.cpp +++ b/src/contacts_config.cpp @@ -44,6 +44,7 @@ struct toJs_impl { obj["nickname"] = toJs(env, maybe_string(contact.nickname)); obj["approved"] = toJs(env, contact.approved); obj["approvedMe"] = toJs(env, contact.approved_me); + obj["profileUpdatedSeconds"] = toJs(env, contact.profile_updated); obj["blocked"] = toJs(env, contact.blocked); obj["priority"] = toJs(env, contact.priority); obj["createdAtSeconds"] = toJs(env, contact.created); @@ -65,6 +66,7 @@ void ContactsConfigWrapper::Init(Napi::Env env, Napi::Object exports) { InstanceMethod("getAll", &ContactsConfigWrapper::getAll), InstanceMethod("set", &ContactsConfigWrapper::set), InstanceMethod("erase", &ContactsConfigWrapper::erase), + }); } @@ -122,8 +124,6 @@ void ContactsConfigWrapper::set(const Napi::CallbackInfo& info) { // created time) contact.created = unix_timestamp_now(); - if (auto name = maybeNonemptyString(obj.Get("name"), "contacts.set name")) - contact.set_name(std::move(*name)); if (auto nickname = maybeNonemptyString(obj.Get("nickname"), "contacts.set nickname")) contact.set_nickname(std::move(*nickname)); else @@ -139,12 +139,25 @@ void ContactsConfigWrapper::set(const Napi::CallbackInfo& info) { toCppString(obj.Get("expirationMode"), "contacts.set expirationMode")); contact.exp_timer = std::chrono::seconds{toCppInteger( obj.Get("expirationTimerSeconds"), "contacts.set expirationTimerSeconds")}; - if (auto pic = obj.Get("profilePicture"); !pic.IsUndefined()) - contact.profile_picture = profile_pic_from_object(pic); - else - contact.profile_picture.clear(); - // if no profile picture are given from the JS side, - // reset that user profile picture + + auto newProfileUpdateSeconds = toCppSysSeconds( + obj.Get("profileUpdatedSeconds"), "contacts.set, profileUpdatedSeconds"); + + // if the saved profile info is older than the new one, update it and the profile fields + // provided + if (contact.profile_updated < newProfileUpdateSeconds) { + contact.profile_updated = newProfileUpdateSeconds; + + if (auto name = maybeNonemptyString(obj.Get("name"), "contacts.set name")) + contact.set_name(std::move(*name)); + + // if no profile picture are given from the JS side, + // reset that user profile picture + if (auto pic = obj.Get("profilePicture"); !pic.IsUndefined()) + contact.profile_picture = profile_pic_from_object(pic); + else + contact.profile_picture.clear(); + } config.set(contact); }); diff --git a/src/groups/meta_group_wrapper.cpp b/src/groups/meta_group_wrapper.cpp index 77b703f..cc5a392 100644 --- a/src/groups/meta_group_wrapper.cpp +++ b/src/groups/meta_group_wrapper.cpp @@ -17,6 +17,7 @@ Napi::Object member_to_js(const Napi::Env& env, const member& info, const member obj["pubkeyHex"] = toJs(env, info.session_id); obj["name"] = toJs(env, info.name); obj["profilePicture"] = toJs(env, info.profile_picture); + obj["profileUpdatedSeconds"] = toJs(env, info.profile_updated); obj["supplement"] = toJs(env, info.supplement); switch (status) { @@ -110,8 +111,6 @@ void MetaGroupWrapper::Init(Napi::Env env, Napi::Object exports) { InstanceMethod( "membersMarkPendingRemoval", &MetaGroupWrapper::membersMarkPendingRemoval), - InstanceMethod( - "memberSetNameTruncated", &MetaGroupWrapper::memberSetNameTruncated), InstanceMethod("memberSetSupplement", &MetaGroupWrapper::memberSetSupplement), InstanceMethod("memberSetInviteSent", &MetaGroupWrapper::memberSetInviteSent), InstanceMethod( @@ -130,7 +129,7 @@ void MetaGroupWrapper::Init(Napi::Env env, Napi::Object exports) { "memberSetPromotionAccepted", &MetaGroupWrapper::memberSetPromotionAccepted), InstanceMethod( - "memberSetProfilePicture", &MetaGroupWrapper::memberSetProfilePicture), + "memberSetProfileDetails", &MetaGroupWrapper::memberSetProfileDetails), InstanceMethod( "memberResetAllSendingState", &MetaGroupWrapper::memberResetAllSendingState), @@ -519,21 +518,6 @@ Napi::Value MetaGroupWrapper::memberConstructAndSet(const Napi::CallbackInfo& in }); } -void MetaGroupWrapper::memberSetNameTruncated(const Napi::CallbackInfo& info) { - wrapExceptions(info, [&] { - assertIsString(info[0]); - assertIsString(info[1]); - - auto pubkeyHex = toCppString(info[0], "memberSetNameTruncated pubkeyHex"); - auto newName = toCppString(info[1], "memberSetNameTruncated newName"); - auto m = this->meta_group->members->get(pubkeyHex); - if (m) { - m->set_name(newName); - this->meta_group->members->set(*m); - } - }); -} - void MetaGroupWrapper::memberSetSupplement(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { assertIsString(info[0]); @@ -652,18 +636,30 @@ void MetaGroupWrapper::memberSetPromotionAccepted(const Napi::CallbackInfo& info }); } -void MetaGroupWrapper::memberSetProfilePicture(const Napi::CallbackInfo& info) { +void MetaGroupWrapper::memberSetProfileDetails(const Napi::CallbackInfo& info) { wrapExceptions(info, [&] { assertInfoLength(info, 2); assertIsString(info[0]); assertIsObject(info[1]); - auto pubkeyHex = toCppString(info[0], "memberSetProfilePicture"); - auto profilePicture = profile_pic_from_object(info[1]); + auto pubkeyHex = toCppString(info[0], "memberSetProfileDetails"); auto m = this->meta_group->members->get(pubkeyHex); - if (m) { + auto argsAsObj = info[1].As(); + auto updatedAtSeconds = + toCppSysSeconds(argsAsObj.Get("profileUpdatedSeconds"), "memberSetProfileDetails"); + + // if the profile details provided are more recent that the ones saved, update them + if (m && updatedAtSeconds > m->profile_updated) { + m->profile_updated = updatedAtSeconds; + + auto profilePicture = profile_pic_from_object(argsAsObj.Get("profilePicture")); m->profile_picture = profilePicture; + + // this will truncate silently if the name is too long + auto newName = toCppString(argsAsObj.Get("name"), "memberSetProfileDetails newName"); + m->set_name_truncated(newName); + this->meta_group->members->set(*m); } }); diff --git a/src/user_config.cpp b/src/user_config.cpp index 4f1937c..19237ab 100644 --- a/src/user_config.cpp +++ b/src/user_config.cpp @@ -20,10 +20,17 @@ void UserConfigWrapper::Init(Napi::Env env, Napi::Object exports) { InstanceMethod("getPriority", &UserConfigWrapper::getPriority), InstanceMethod("getName", &UserConfigWrapper::getName), InstanceMethod("getProfilePic", &UserConfigWrapper::getProfilePic), + InstanceMethod( + "getProfilePicWithKeyHex", &UserConfigWrapper::getProfilePicWithKeyHex), + InstanceMethod( + "getProfileUpdatedSeconds", + &UserConfigWrapper::getProfileUpdatedSeconds), + InstanceMethod( + "setReuploadProfilePic", &UserConfigWrapper::setReuploadProfilePic), InstanceMethod("setPriority", &UserConfigWrapper::setPriority), InstanceMethod("setName", &UserConfigWrapper::setName), InstanceMethod("setNameTruncated", &UserConfigWrapper::setNameTruncated), - InstanceMethod("setProfilePic", &UserConfigWrapper::setProfilePic), + InstanceMethod("setNewProfilePic", &UserConfigWrapper::setNewProfilePic), InstanceMethod( "getEnableBlindedMsgRequest", &UserConfigWrapper::getEnableBlindedMsgRequest), @@ -107,7 +114,7 @@ void UserConfigWrapper::setNameTruncated(const Napi::CallbackInfo& info) { }); } -void UserConfigWrapper::setProfilePic(const Napi::CallbackInfo& info) { +void UserConfigWrapper::setNewProfilePic(const Napi::CallbackInfo& info) { return wrapExceptions(info, [&] { assertInfoLength(info, 1); auto profile_pic_obj = info[0]; @@ -119,6 +126,36 @@ void UserConfigWrapper::setProfilePic(const Napi::CallbackInfo& info) { }); } +void UserConfigWrapper::setReuploadProfilePic(const Napi::CallbackInfo& info) { + assertInfoLength(info, 1); + auto profile_pic_obj = info[0]; + + if (!profile_pic_obj.IsNull() && !profile_pic_obj.IsUndefined()) + assertIsObject(profile_pic_obj); + + config.set_reupload_profile_pic(profile_pic_from_object(profile_pic_obj)); +} + +Napi::Value UserConfigWrapper::getProfileUpdatedSeconds(const Napi::CallbackInfo& info) { + return wrapResult(info, [&] { + auto env = info.Env(); + return config.get_profile_updated(); + }); +} + +Napi::Value UserConfigWrapper::getProfilePicWithKeyHex(const Napi::CallbackInfo& info) { + return wrapResult(info, [&]() -> std::optional { + auto env = info.Env(); + auto pic = config.get_profile_pic(); + // if pic.key and url are set, return a combined string with both merged by a hash, and the + // key in hex + if (!pic.url.empty() && !pic.key.empty()) { + return std::string(pic.url + "#" + to_hex(pic.key)); + } + return std::nullopt; + }); +} + Napi::Value UserConfigWrapper::getEnableBlindedMsgRequest(const Napi::CallbackInfo& info) { return wrapResult(info, [&] { auto env = info.Env(); diff --git a/src/utilities.cpp b/src/utilities.cpp index 089aab0..15bd2f3 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -5,6 +5,7 @@ #include #include "session/config/namespaces.hpp" +#include "session/config/profile_pic.hpp" #include "session/util.hpp" namespace session::nodeapi { @@ -138,6 +139,63 @@ std::optional maybeNonemptyInt(Napi::Value x, const std::string& identi throw std::invalid_argument{"maybeNonemptyInt with invalid type, called from " + identifier}; } +std::optional maybeNonemptySysSeconds( + Napi::Value x, const std::string& identifier) { + if (x.IsNull() || x.IsUndefined()) + return std::nullopt; + + if (x.IsNumber()) { + auto num = x.As().Int64Value(); + return std::chrono::sys_seconds{std::chrono::seconds{num}}; + } + + throw std::invalid_argument{ + "maybeNonemptySysSeconds with invalid type, called from " + identifier}; +} + +std::chrono::sys_seconds toCppSysSeconds(Napi::Value x, const std::string& identifier) { + + if (x.IsNumber()) { + auto num = x.As().Int64Value(); + return std::chrono::sys_seconds{std::chrono::seconds{num}}; + } + + throw std::invalid_argument{"toCppSysSeconds with invalid type, called from " + identifier}; +} + +std::optional maybeNonemptyProfilePic( + Napi::Value x, const std::string& identifier) { + if (x.IsNull() || x.IsUndefined()) + return std::nullopt; + /** + * We want to allow url and key to be empty strings here, + * as this means to remove the profile pic altogether. + * However, if they are undefined/null, we want to return nullopt, as no changes should be + * made. + */ + + if (!x.IsObject()) { + throw std::invalid_argument{ + "maybeNonemptyProfilePic with invalid type, called from " + identifier}; + } + auto obj = x.As(); + if (obj.IsEmpty()) { + return std::nullopt; + } + + auto url = maybeNonemptyString(obj.Get("url"), "maybeNonemptyProfilePic url"); + auto key = maybeNonemptyBuffer(obj.Get("key"), "maybeNonemptyProfilePic key"); + + if (!url || !key) { + // when the `x` obj is provided (i.e. not null), it should have those 2 fields set. + // They can be empty (meaning to remove the profile pic), but not undefined/null, as the + // object itself should have been undefined/null + throw new std::invalid_argument{"maybeNonemptyProfilePic with invalid input"}; + } + return session::config::profile_pic{*url, *key}; + +} // namespace session::nodeapi + std::optional maybeNonemptyBoolean(Napi::Value x, const std::string& identifier) { if (x.IsNull() || x.IsUndefined()) return std::nullopt; @@ -190,6 +248,16 @@ int64_t toPriority(Napi::Value x, int64_t currentPriority) { return newPriority; } +int64_t toPriority(int64_t newPriority, int64_t currentPriority) { + if (newPriority > 0) + // keep the existing priority if it is already set + return std::max(currentPriority, 1); + + // newPriority being < 0 means that that conversation is hidden (and so + // unpinned) + return newPriority; +} + int64_t unix_timestamp_now() { using namespace std::chrono; return duration_cast(system_clock::now().time_since_epoch()).count(); @@ -249,5 +317,4 @@ confirm_pushed_entry_t confirm_pushed_entry_from_JS(const Napi::Env& env, const confirm_pushed_entry_t confirmed_pushed_entry{seqno, hashes}; return confirmed_pushed_entry; } - -} // namespace session::nodeapi +} // namespace session::nodeapi \ No newline at end of file diff --git a/types/blinding/blinding.d.ts b/types/blinding/blinding.d.ts index 7ab01f0..fcad107 100644 --- a/types/blinding/blinding.d.ts +++ b/types/blinding/blinding.d.ts @@ -34,7 +34,6 @@ declare module 'libsession_util_nodejs' { */ export class BlindingWrapperNode { public static blindVersionPubkey: BlindingWrapper['blindVersionPubkey']; - public static blindVersionSignRequest: BlindingWrapper['blindVersionSignRequest']; public static blindVersionSign: BlindingWrapper['blindVersionSign']; } diff --git a/types/groups/groupmembers.d.ts b/types/groups/groupmembers.d.ts index 6cbe676..6d65745 100644 --- a/types/groups/groupmembers.d.ts +++ b/types/groups/groupmembers.d.ts @@ -61,6 +61,8 @@ declare module 'libsession_util_nodejs' { */ supplement: boolean; + profileUpdatedSeconds: number; + /** * True if the member is scheduled to get the keys (`.admin` field of libsession). * This is equivalent of memberStatus being one of: @@ -85,7 +87,6 @@ declare module 'libsession_util_nodejs' { memberGetAllPendingRemovals: () => Array; // setters - memberSetNameTruncated: (pubkeyHex: PubkeyType, newName: string) => void; /** * A member's invite state defaults to invite-not-sent. @@ -115,7 +116,14 @@ declare module 'libsession_util_nodejs' { /** Called when the member accepted the promotion */ memberSetPromotionAccepted: (pubkeyHex: PubkeyType) => void; - memberSetProfilePicture: (pubkeyHex: PubkeyType, profilePicture: ProfilePicture) => void; + memberSetProfileDetails: ( + pubkeyHex: PubkeyType, + profileDetails: { + profileUpdatedSeconds: number; + name: string; + profilePicture: ProfilePicture; + } + ) => void; memberResetAllSendingState: () => boolean; memberSetSupplement: (pubkeyHex: PubkeyType) => void; membersMarkPendingRemoval: (members: Array, withMessages: boolean) => void; diff --git a/types/groups/metagroup.d.ts b/types/groups/metagroup.d.ts index 1231955..deed1bd 100644 --- a/types/groups/metagroup.d.ts +++ b/types/groups/metagroup.d.ts @@ -97,7 +97,6 @@ declare module 'libsession_util_nodejs' { public memberGetAll: MetaGroupWrapper['memberGetAll']; public memberGetAllPendingRemovals: MetaGroupWrapper['memberGetAllPendingRemovals']; public memberSetInviteAccepted: MetaGroupWrapper['memberSetInviteAccepted']; - public memberSetNameTruncated: MetaGroupWrapper['memberSetNameTruncated']; public memberSetPromoted: MetaGroupWrapper['memberSetPromoted']; public memberSetPromotionAccepted: MetaGroupWrapper['memberSetPromotionAccepted']; public memberSetPromotionSent: MetaGroupWrapper['memberSetPromotionSent']; @@ -107,7 +106,7 @@ declare module 'libsession_util_nodejs' { public memberSetInviteFailed: MetaGroupWrapper['memberSetInviteFailed']; public memberEraseAndRekey: MetaGroupWrapper['memberEraseAndRekey']; public membersMarkPendingRemoval: MetaGroupWrapper['membersMarkPendingRemoval']; - public memberSetProfilePicture: MetaGroupWrapper['memberSetProfilePicture']; + public memberSetProfileDetails: MetaGroupWrapper['memberSetProfileDetails']; public memberResetAllSendingState: MetaGroupWrapper['memberResetAllSendingState']; public memberSetSupplement: MetaGroupWrapper['memberSetSupplement']; @@ -148,7 +147,6 @@ declare module 'libsession_util_nodejs' { | MakeActionCall | MakeActionCall | MakeActionCall - | MakeActionCall | MakeActionCall | MakeActionCall | MakeActionCall @@ -158,7 +156,7 @@ declare module 'libsession_util_nodejs' { | MakeActionCall | MakeActionCall | MakeActionCall - | MakeActionCall + | MakeActionCall | MakeActionCall | MakeActionCall diff --git a/types/user/contacts.d.ts b/types/user/contacts.d.ts index 8fb8164..a2eee0f 100644 --- a/types/user/contacts.d.ts +++ b/types/user/contacts.d.ts @@ -10,9 +10,9 @@ declare module 'libsession_util_nodejs' { init: (secretKey: Uint8Array, dump: Uint8Array | null) => void; /** This function is used to free wrappers from memory only */ free: () => void; - get: (pubkeyHex: string) => ContactInfo | null; + get: (pubkeyHex: string) => ContactInfoGet | null; set: (contact: ContactInfoSet) => void; - getAll: () => Array; + getAll: () => Array; erase: (pubkeyHex: string) => void; }; @@ -26,12 +26,25 @@ declare module 'libsession_util_nodejs' { type ContactInfoShared = WithPriority & { id: string; - name?: string; nickname?: string; - profilePicture?: ProfilePicture; - createdAtSeconds: number; // can only be set the first time a contact is created, a new change won't override the value in the wrapper. + /** + * Can only be set the first time a contact is created, a new change won't override the value in the wrapper. + */ + createdAtSeconds: number; expirationMode?: DisappearingMessageConversationModeType; expirationTimerSeconds?: number; + /** + * A name & profile pic change won't be applied unless this value is more recent than the currently saved one. + */ + profileUpdatedSeconds: number; + /** + * see `profileUpdatedSeconds` for more info. + */ + name?: string; + /** + * see `profileUpdatedSeconds` for more info. + */ + profilePicture?: ProfilePicture; }; export type ContactInfoSet = ContactInfoShared & { @@ -40,7 +53,7 @@ declare module 'libsession_util_nodejs' { blocked?: boolean; }; - export type ContactInfo = ContactInfoShared & { + export type ContactInfoGet = ContactInfoShared & { approved: boolean; approvedMe: boolean; blocked: boolean; diff --git a/types/user/userconfig.d.ts b/types/user/userconfig.d.ts index 95ac407..9f6b6ea 100644 --- a/types/user/userconfig.d.ts +++ b/types/user/userconfig.d.ts @@ -17,7 +17,25 @@ declare module 'libsession_util_nodejs' { setPriority: (priority: number) => void; setName: (name: string) => void; setNameTruncated: (name: string) => void; - setProfilePic: (pic: ProfilePicture) => void; + + /** + * Call this when uploading a new profile picture (i.e. not an auto reupload) + */ + setNewProfilePic: (pic: ProfilePicture) => void; + /** + * Call this when reuploading a the previous profile picture + */ + setReuploadProfilePic: (pic: ProfilePicture) => void; + + getProfileUpdatedSeconds: () => number; + /** + * Returns the profile picture url and key merged as a url fragment, or null if not set. + * So this could return something like + * `http://filev2.getsession.org/file/1234#ba7e23ef3d4dc54b706d79c149ec571a4d64043e13771236afc030f6c8118575` + * + */ + getProfilePicWithKeyHex: () => string | undefined; + setEnableBlindedMsgRequest: (msgRequest: boolean) => void; getEnableBlindedMsgRequest: () => boolean | undefined; setNoteToSelfExpiry: (expirySeconds: number) => void; @@ -40,7 +58,10 @@ declare module 'libsession_util_nodejs' { public setPriority: UserConfigWrapper['setPriority']; public setName: UserConfigWrapper['setName']; public setNameTruncated: UserConfigWrapper['setNameTruncated']; - public setProfilePic: UserConfigWrapper['setProfilePic']; + public setNewProfilePic: UserConfigWrapper['setNewProfilePic']; + public setReuploadProfilePic: UserConfigWrapper['setReuploadProfilePic']; + public getProfileUpdatedSeconds: UserConfigWrapper['getProfileUpdatedSeconds']; + public getProfilePicWithKeyHex: UserConfigWrapper['getProfilePicWithKeyHex']; public getEnableBlindedMsgRequest: UserConfigWrapper['getEnableBlindedMsgRequest']; public setEnableBlindedMsgRequest: UserConfigWrapper['setEnableBlindedMsgRequest']; public getNoteToSelfExpiry: UserConfigWrapper['getNoteToSelfExpiry']; @@ -61,7 +82,10 @@ declare module 'libsession_util_nodejs' { | MakeActionCall | MakeActionCall | MakeActionCall - | MakeActionCall + | MakeActionCall + | MakeActionCall + | MakeActionCall + | MakeActionCall | MakeActionCall | MakeActionCall | MakeActionCall