Skip to content
Draft
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
11 changes: 11 additions & 0 deletions include/user_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ class UserConfigWrapper : public ConfigBaseImpl, public Napi::ObjectWrap<UserCon
void setRefundRequested(const Napi::CallbackInfo& info);
Napi::Value getProPrepaid(const Napi::CallbackInfo& info);
void setProPrepaid(const Napi::CallbackInfo& info);
// Auto-renewing (config key A). Presence-only in core: set_pro_auto_renewing uses
// set_nonzero_int, so writing false ERASES the key — absent means terminal/unknown, and a
// caller must compare the getter's value rather than the key's presence.
Napi::Value getProAutoRenewing(const Napi::CallbackInfo& info);
void setProAutoRenewing(const Napi::CallbackInfo& info);
// Grace period (config key G), in ms on the JS side. Synced alongside E so any linked device
// can derive the paid-through instant as E - G: the backend folds grace into the stored expiry
// for auto-renewing subscriptions, so E is the end of coverage, not the date the renewal is
// due. Deliberately not optional -- the backend sends 0 when not auto-renewing, and E - 0 == E.
Napi::Value getProGracePeriod(const Napi::CallbackInfo& info);
void setProGracePeriod(const Napi::CallbackInfo& info);
Napi::Value getProRenewalTarget(const Napi::CallbackInfo& info);
};
}; // namespace session::nodeapi
42 changes: 42 additions & 0 deletions src/user_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ void UserConfigWrapper::Init(Napi::Env env, Napi::Object exports) {
InstanceMethod("setRefundRequested", &UserConfigWrapper::setRefundRequested),
InstanceMethod("getProPrepaid", &UserConfigWrapper::getProPrepaid),
InstanceMethod("setProPrepaid", &UserConfigWrapper::setProPrepaid),
InstanceMethod("getProAutoRenewing", &UserConfigWrapper::getProAutoRenewing),
InstanceMethod("setProAutoRenewing", &UserConfigWrapper::setProAutoRenewing),
InstanceMethod("getProGracePeriod", &UserConfigWrapper::getProGracePeriod),
InstanceMethod("setProGracePeriod", &UserConfigWrapper::setProGracePeriod),
InstanceMethod("getProRenewalTarget", &UserConfigWrapper::getProRenewalTarget),
});
}
Expand Down Expand Up @@ -462,6 +466,44 @@ void UserConfigWrapper::setProPrepaid(const Napi::CallbackInfo& info) {
});
}

Napi::Value UserConfigWrapper::getProAutoRenewing(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] { return toJs(info.Env(), config.get_pro_auto_renewing()); });
}

void UserConfigWrapper::setProAutoRenewing(const Napi::CallbackInfo& info) {
wrapExceptions(info, [&] {
assertInfoLength(info, 1);
assertIsBoolean(info[0], "setProAutoRenewing");
auto auto_renewing = toCppBoolean(info[0], "UserConfigWrapper::setProAutoRenewing");
config.set_pro_auto_renewing(auto_renewing);
});
}

Napi::Value UserConfigWrapper::getProGracePeriod(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
// libsession stores whole seconds; the JS domain is milliseconds, matching the rest of the
// Pro accessors (and the `gracePeriodDurationMs` field callers get from get_pro_status).
// Not optional, unlike the auto-renewing pair: the backend sends 0 when the subscription
// isn't auto-renewing, and `E - 0 == E`, so an absent key and a stored zero are the same
// account.
auto grace_s = config.get_pro_grace_period();
return static_cast<int64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(grace_s).count());
});
}

void UserConfigWrapper::setProGracePeriod(const Napi::CallbackInfo& info) {
wrapExceptions(info, [&] {
assertInfoLength(info, 1);
assertIsNumber(info[0], "setProGracePeriod");
auto grace_ms = toCppInteger(info[0], "UserConfigWrapper::setProGracePeriod", false);
// Floor rather than round: a grace period is a coverage window, so truncating keeps
// `E - G` from claiming coverage the backend didn't grant. Zero or negative clears the key.
config.set_pro_grace_period(
std::chrono::floor<std::chrono::seconds>(std::chrono::milliseconds{grace_ms}));
});
}

Napi::Value UserConfigWrapper::getProRenewalTarget(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
assertInfoLength(info, 1);
Expand Down
38 changes: 38 additions & 0 deletions types/user/userconfig.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,36 @@ declare module 'libsession_util_nodejs' {
/** Pro-prepaid / purchase-in-flight marker (config key I), in ms; null when unset or gated. */
getProPrepaid: () => number | null;
setProPrepaid: (prepaidTsMs: number | null) => void;
/**
* Whether the subscription auto-renews (config key A), from get_pro_status.auto_renewing.
*
* Presence-only: core writes it with set_nonzero_int, so `setProAutoRenewing(false)` ERASES the
* key rather than storing a false. The getter therefore returns false for all three of "not
* auto-renewing", "never fetched yet" and "written by a client predating key A" — so treat
* false as terminal/unknown, and never key a change check on whether the key is present.
*/
getProAutoRenewing: () => boolean;
setProAutoRenewing: (autoRenewing: boolean) => void;
/**
* The account's grace period (config key G), in MILLISECONDS, from
* get_pro_status.grace_period_duration. 0 when unset.
*
* Synced alongside `E` so any linked device can derive the paid-through instant as
* `getProAccessExpiry() - getProGracePeriod()`: the backend folds grace into the stored expiry
* for auto-renewing subscriptions, so `E` is the END OF COVERAGE, not the date the renewal is due.
*
* ⚠️ Only meaningful if `E` and `G` are written from the SAME get_pro_status response — they are
* then consistent whatever grace was in force. Write them together, and clear `G` whenever you
* clear `E`, or a later `E` will pair with a stale grace.
*
* Not optional, unlike the auto-renewing pair: the backend sends 0 when the subscription isn't
* auto-renewing, and `E - 0 == E`, so an absent key and a stored zero describe the same account.
* Milliseconds here (not seconds like setNoteToSelfExpiry) to match the rest of the Pro accessors
* and the `gracePeriodDurationMs` field callers receive from get_pro_status; core stores seconds
* and the conversion floors.
*/
getProGracePeriod: () => number;
setProGracePeriod: (graceMs: number) => void;
/**
* When to (re)request a proof given `nowMs`: nowMs (request now), a future ms (preemptive
* renewal ~1h before expiry), or null (don't renew). Supersedes bespoke auto-renew logic.
Expand Down Expand Up @@ -136,6 +166,10 @@ declare module 'libsession_util_nodejs' {
public setRefundRequested: UserConfigWrapper['setRefundRequested'];
public getProPrepaid: UserConfigWrapper['getProPrepaid'];
public setProPrepaid: UserConfigWrapper['setProPrepaid'];
public getProAutoRenewing: UserConfigWrapper['getProAutoRenewing'];
public setProAutoRenewing: UserConfigWrapper['setProAutoRenewing'];
public getProGracePeriod: UserConfigWrapper['getProGracePeriod'];
public setProGracePeriod: UserConfigWrapper['setProGracePeriod'];
public getProRenewalTarget: UserConfigWrapper['getProRenewalTarget'];
}

Expand Down Expand Up @@ -175,5 +209,9 @@ declare module 'libsession_util_nodejs' {
| MakeActionCall<UserConfigWrapper, 'setRefundRequested'>
| MakeActionCall<UserConfigWrapper, 'getProPrepaid'>
| MakeActionCall<UserConfigWrapper, 'setProPrepaid'>
| MakeActionCall<UserConfigWrapper, 'getProAutoRenewing'>
| MakeActionCall<UserConfigWrapper, 'setProAutoRenewing'>
| MakeActionCall<UserConfigWrapper, 'getProGracePeriod'>
| MakeActionCall<UserConfigWrapper, 'setProGracePeriod'>
| MakeActionCall<UserConfigWrapper, 'getProRenewalTarget'>;
}
Loading