diff --git a/src/ssb.js.c b/src/ssb.js.c index 7163d84c..01a41e1a 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -1512,6 +1512,96 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst return result; } +typedef struct _following_t +{ + uv_work_t work; + tf_ssb_t* ssb; + JSContext* context; + JSValue promise[2]; + + const char** out_ids; + + int depth; + int ids_count; + const char* ids[]; +} following_t; + +static void _tf_ssb_following_work(uv_work_t* work) +{ + following_t* following = work->data; + following->out_ids = tf_ssb_db_following_deep(following->ssb, following->ids, following->ids_count, following->depth); +} + +static void _tf_ssb_following_after_work(uv_work_t* work, int status) +{ + following_t* following = work->data; + JSContext* context = following->context; + if (status == 0) + { + JSValue array = JS_NewArray(context); + for (int i = 0; following->out_ids[i]; i++) + { + JS_SetPropertyUint32(context, array, i, JS_NewString(context, following->out_ids[i])); + } + JS_Call(context, following->promise[0], JS_UNDEFINED, 1, &array); + JS_FreeValue(context, array); + } + else + { + char buffer[256]; + uv_strerror_r(status, buffer, sizeof(buffer)); + JSValue message = JS_NewString(context, buffer); + JS_Call(context, following->promise[1], JS_UNDEFINED, 1, &message); + JS_FreeValue(context, message); + } + + for (int i = 0; i < following->ids_count; i++) + { + tf_free((void*)following->ids[i]); + } + tf_free(following->out_ids); + tf_free(following); +} + +static JSValue _tf_ssb_following(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + int ids_count = tf_util_get_length(context, argv[0]); + + following_t* following = tf_malloc(sizeof(following_t) + sizeof(char*) * ids_count); + *following = (following_t) + { + .work = + { + .data = following, + }, + .context = context, + .ssb = ssb, + }; + JS_ToInt32(context, &following->depth, argv[1]); + + JSValue result = JS_NewPromiseCapability(context, following->promise); + + for (int i = 0; i < ids_count; i++) + { + JSValue id_value = JS_GetPropertyUint32(context, argv[0], i); + if (!JS_IsUndefined(id_value)) + { + const char* id_string = JS_ToCString(context, id_value); + following->ids[following->ids_count++] = tf_strdup(id_string); + JS_FreeCString(context, id_string); + JS_FreeValue(context, id_value); + } + } + + int r = uv_queue_work(tf_ssb_get_loop(ssb), &following->work, _tf_ssb_following_work, _tf_ssb_following_after_work); + if (r) + { + _tf_ssb_following_after_work(&following->work, r); + } + return result; +} + void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) { JS_NewClassID(&_tf_ssb_classId); @@ -1555,6 +1645,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) JS_SetPropertyStr(context, object, "getBroadcasts", JS_NewCFunction(context, _tf_ssb_getBroadcasts, "getBroadcasts", 0)); JS_SetPropertyStr(context, object, "connect", JS_NewCFunction(context, _tf_ssb_connect, "connect", 1)); JS_SetPropertyStr(context, object, "createTunnel", JS_NewCFunction(context, _tf_ssb_createTunnel, "createTunnel", 3)); + JS_SetPropertyStr(context, object, "following", JS_NewCFunction(context, _tf_ssb_following, "following", 2)); /* Write. */ JS_SetPropertyStr(context, object, "storeMessage", JS_NewCFunction(context, _tf_ssb_storeMessage, "storeMessage", 1)); JS_SetPropertyStr(context, object, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 1));