Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions include/pro/pro.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#pragma once

#include <napi.h>
#include <oxenc/base64.h>
#include <oxenc/hex.h>

#include <algorithm>
#include <span>
#include <vector>

#include "../utilities.hpp"
#include "oxen/log.hpp"
#include "pro/types.hpp"
#include "session/attachments.hpp"
#include "session/config/user_profile.hpp"
#include "session/random.hpp"
#include "session/session_protocol.hpp"

namespace session::nodeapi {

class ProWrapper : public Napi::ObjectWrap<ProWrapper> {
public:
ProWrapper(const Napi::CallbackInfo& info) : Napi::ObjectWrap<ProWrapper>{info} {
throw std::invalid_argument("ProWrapper is static and doesn't need to be constructed");
}

static void Init(Napi::Env env, Napi::Object exports) {
MetaBaseWrapper::NoBaseClassInitHelper<ProWrapper>(
env,
exports,
"ProWrapperNode",
{
// Pro features
StaticMethod<&ProWrapper::proFeaturesForMessage>(
"proFeaturesForMessage",
static_cast<napi_property_attributes>(
napi_writable | napi_configurable)),
});
}

private:
static Napi::Value proFeaturesForMessage(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
// we expect two arguments that match:
// first: {
// "utf16": string,
// "proFeatures": Array<ProFeature>,
// }

assertInfoLength(info, 1);
assertIsObject(info[0]);

auto first = info[0].As<Napi::Object>();

if (first.IsEmpty())
throw std::invalid_argument("proFeaturesForMessage first received empty");

assertIsArray(first.Get("proFeatures"), "proFeaturesForMessage.proFeatures");
auto proFeaturesJS = first.Get("proFeatures").As<Napi::Array>();
std::vector<std::string> proFeatures;
proFeatures.reserve(proFeaturesJS.Length());
for (uint32_t i = 0; i < proFeaturesJS.Length(); i++) {
auto itemValue = proFeaturesJS.Get(i);
assertIsString(itemValue, "proFeaturesForMessage.proFeatures.itemValue");
std::string item =
toCppString(itemValue, "proFeaturesForMessage.proFeatures.itemValue");
proFeatures.push_back(item);
}

SESSION_PROTOCOL_PRO_EXTRA_FEATURES flags;
for (std::string& feature : proFeatures) {
if (feature == "10K_CHARACTER_LIMIT") {
flags |= SESSION_PROTOCOL_PRO_FEATURES_10K_CHARACTER_LIMIT;
} else if (feature == "PRO_BADGE") {
flags |= SESSION_PROTOCOL_PRO_FEATURES_PRO_BADGE;
} else if (feature == "ANIMATED_AVATAR") {
flags |= SESSION_PROTOCOL_PRO_FEATURES_ANIMATED_AVATAR;
}
}
assertIsString(first.Get("utf16"), "proFeaturesForMessage.utf16");
std::u16string utf16 = first.Get("utf16").As<Napi::String>().Utf16Value();
return session::pro_features_for_utf16((utf16.data()), utf16.length(), flags);
});
};
};
}; // namespace session::nodeapi
15 changes: 15 additions & 0 deletions include/pro/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,19 @@ struct toJs_impl<session::DecodedPro> {
}
};

template <>
struct toJs_impl<session::ProFeaturesForMsg> {
Napi::Object operator()(
const Napi::Env& env, const session::ProFeaturesForMsg pro_features_msg) {
auto obj = Napi::Object::New(env);

obj["success"] = toJs(env, pro_features_msg.success);
obj["error"] = toJs(env, pro_features_msg.error);
obj["codepointCount"] = toJs(env, pro_features_msg.codepoint_count);
obj["proFeatures"] = toJs(env, pro_features_msg.features);

return obj;
}
};

}; // namespace session::nodeapi
2 changes: 2 additions & 0 deletions src/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "convo_info_volatile_config.hpp"
#include "groups/meta_group_wrapper.hpp"
#include "multi_encrypt/multi_encrypt.hpp"
#include "pro/pro.hpp"
#include "user_config.hpp"
#include "user_groups_config.hpp"

Expand Down Expand Up @@ -52,6 +53,7 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {

// Fully static wrappers init
session::nodeapi::MultiEncryptWrapper::Init(env, exports);
session::nodeapi::ProWrapper::Init(env, exports);
session::nodeapi::BlindingWrapper::Init(env, exports);

return exports;
Expand Down
1 change: 1 addition & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
/// <reference path="./blinding/index.d.ts" />
/// <reference path="./groups/index.d.ts" />
/// <reference path="./multi_encrypt/index.d.ts" />
/// <reference path="./pro/pro.d.ts" />
/// <reference path="./user/index.d.ts" />
/// <reference path="./shared.d.ts" />
28 changes: 28 additions & 0 deletions types/pro/pro.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,32 @@ declare module 'libsession_util_nodejs' {
pro_access_not_found: string;
support_url: string;
};

type ProWrapper = {
proFeaturesForMessage: (args: {
utf16: string;
/**
* If the utf16 requires 10K_CHARACTER_LIMIT to be set, it will be set in the return.
* If provided (here) as an input, it will be ignored.
*/
proFeatures: ProFeatures;
}) => WithProFeatures & { success: boolean; error: string | null; codePointCount: number };
};

export type ProActionsCalls = MakeWrapperActionCalls<ProWrapper>;


/**
* To be used inside the web worker only (calls are synchronous and won't work asynchronously)
*/
export class ProWrapperNode {
public static proFeaturesForMessage: ProWrapper['proFeaturesForMessage'];
}

/**
* Those actions are used internally for the web worker communication.
* You should never need to import them in Session directly
* You will need to add an entry here if you add a new function
*/
export type ProActionsType = MakeActionCall<ProWrapper, 'proFeaturesForMessage'>;
}