Compare commits
9 Commits
latest_rel
...
eecdbf6852
| Author | SHA1 | Date | |
|---|---|---|---|
| eecdbf6852 | |||
| ddc4603f13 | |||
| 759b522cd1 | |||
| 7ecb4a192d | |||
| d84626ac31 | |||
| 9c36e0db7b | |||
| fcd26bac1c | |||
| e8e7c98705 | |||
| b5af5cc223 |
@@ -16,9 +16,9 @@ MAKEFLAGS += --no-builtin-rules
|
||||
## LD := Linker.
|
||||
## ANDROID_SDK := Path to the Android SDK.
|
||||
|
||||
VERSION_CODE := 48
|
||||
VERSION_CODE_IOS := 26
|
||||
VERSION_NUMBER := 0.2025.11
|
||||
VERSION_CODE := 49
|
||||
VERSION_CODE_IOS := 27
|
||||
VERSION_NUMBER := 0.2025.12-wip
|
||||
VERSION_NAME := This program kills fascists.
|
||||
|
||||
IPHONEOS_VERSION_MIN=14.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🦀",
|
||||
"previous": "&7dPNAI4sffljUTiwGr3XEUeB8sBD72CFkWMk/o0Z2pw=.sha256"
|
||||
"previous": "&t4hk+Y6NB+TftzDLhJ9gUKLPU5YcFYvTEbJuTT2qPCQ=.sha256"
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ class TfElement extends LitElement {
|
||||
'',
|
||||
'@',
|
||||
'👍',
|
||||
'🚩',
|
||||
...Object.keys(this.visible_private())
|
||||
.sort()
|
||||
.map((x) => '🔐' + JSON.parse(x).join(',')),
|
||||
@@ -491,6 +492,13 @@ class TfElement extends LitElement {
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
tfrpc.rpc.query(
|
||||
`
|
||||
SELECT '🚩' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
WHERE messages.content ->> 'type' = 'flag'
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
])
|
||||
).flat();
|
||||
let latest = {'🔐': undefined};
|
||||
|
||||
@@ -196,6 +196,26 @@ class TfMessageElement extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
flag(event) {
|
||||
let reason = prompt(
|
||||
'What is the reason for reporting this content (spam, nsfw, ...)?',
|
||||
'offensive'
|
||||
);
|
||||
if (reason !== undefined) {
|
||||
tfrpc.rpc
|
||||
.appendMessage(this.whoami, {
|
||||
type: 'flag',
|
||||
flag: {
|
||||
link: this.message.id,
|
||||
reason: reason.length ? reason : undefined,
|
||||
},
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert(error?.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
show_image(link) {
|
||||
let div = document.createElement('div');
|
||||
div.style.left = 0;
|
||||
@@ -499,11 +519,14 @@ class TfMessageElement extends LitElement {
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
<button class="w3-button w3-bar-item" @click=${this.react}>
|
||||
👍 React
|
||||
</button>
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.react}
|
||||
@click=${this.flag}
|
||||
>
|
||||
👍 React
|
||||
⚠️ Flag
|
||||
</button>
|
||||
${formats.map(
|
||||
([format, name]) => html`
|
||||
@@ -965,7 +988,11 @@ class TfMessageElement extends LitElement {
|
||||
style="cursor: pointer"
|
||||
@click=${(x) => this.toggle_expanded(':cw')}
|
||||
>
|
||||
<p>${content.contentWarning}</p>
|
||||
<p>
|
||||
${this.message.flags
|
||||
? `Caution: This message has been flagged ${this.message.flags.length} time${this.message.flags.length == 1 ? '' : 's'}.`
|
||||
: content.contentWarning}
|
||||
</p>
|
||||
<p class="w3-small">
|
||||
${this.is_expanded(':cw') ? 'Show less' : 'Show more'}
|
||||
</p>
|
||||
@@ -976,11 +1003,12 @@ class TfMessageElement extends LitElement {
|
||||
<div @click=${this.body_click}>${body}</div>
|
||||
${this.render_mentions()}
|
||||
`;
|
||||
let payload = content.contentWarning
|
||||
? self.expanded[(this.message.id || '') + ':cw']
|
||||
? html` ${content_warning} ${content_html} `
|
||||
: content_warning
|
||||
: content_html;
|
||||
let payload =
|
||||
this.message.flags || content.contentWarning
|
||||
? self.expanded[(this.message.id || '') + ':cw']
|
||||
? html` ${content_warning} ${content_html} `
|
||||
: content_warning
|
||||
: content_html;
|
||||
return this.render_frame(html`
|
||||
${this.render_header()}
|
||||
<div class="w3-container">${payload}</div>
|
||||
|
||||
@@ -66,6 +66,16 @@ class TfNewsElement extends LitElement {
|
||||
}
|
||||
parent.votes.push(message);
|
||||
message.parent_message = message.content.vote.link;
|
||||
} else if (message.content.type == 'flag') {
|
||||
let parent = ensure_message(message.content.flag.link, message.rowid);
|
||||
if (!parent.flags) {
|
||||
parent.flags = [];
|
||||
}
|
||||
parent.flags.push(message);
|
||||
parent.flags = Object.values(
|
||||
Object.fromEntries(parent.flags.map((x) => [x.id, x]))
|
||||
);
|
||||
message.parent_message = message.content.flag.link;
|
||||
} else if (message.content.type == 'post') {
|
||||
if (message.content.root) {
|
||||
if (typeof message.content.root === 'string') {
|
||||
@@ -106,6 +116,7 @@ class TfNewsElement extends LitElement {
|
||||
message.parent_message = placeholder.parent_message;
|
||||
message.child_messages = placeholder.child_messages;
|
||||
message.votes = placeholder.votes;
|
||||
message.flags = placeholder.flags;
|
||||
if (
|
||||
placeholder.parent_message &&
|
||||
messages_by_id[placeholder.parent_message]
|
||||
|
||||
@@ -84,7 +84,6 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
`,
|
||||
[JSON.stringify(combined.map((x) => x.id))]
|
||||
);
|
||||
let t0 = new Date();
|
||||
let result = [].concat(
|
||||
combined,
|
||||
await tfrpc.rpc.query(
|
||||
@@ -101,8 +100,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
]
|
||||
)
|
||||
);
|
||||
let t1 = new Date();
|
||||
console.log((t1 - t0) / 1000);
|
||||
console.log(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -227,7 +225,8 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
k_max_results,
|
||||
]
|
||||
);
|
||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||
let decrypted = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||
result = await this._fetch_related_messages(decrypted);
|
||||
} else if (this.hash == '#👍') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
@@ -246,6 +245,23 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
`,
|
||||
[JSON.stringify(this.following), start_time, end_time, k_max_results]
|
||||
);
|
||||
} else if (this.hash == '#🚩') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH flags AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'flag' AND
|
||||
(?1 IS NULL OR messages.timestamp >= ?1) AND messages.timestamp < ?2
|
||||
ORDER BY timestamp DESC limit ?3)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM flags
|
||||
JOIN messages ON messages.id = flags.content ->> '$.flag.link'
|
||||
UNION
|
||||
SELECT TRUE AS is_primary, * FROM flags
|
||||
`,
|
||||
[start_time, end_time, k_max_results]
|
||||
);
|
||||
} else {
|
||||
let initial_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
|
||||
@@ -243,6 +243,12 @@ class TfTabNewsElement extends LitElement {
|
||||
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('👍')}👍votes</a
|
||||
>
|
||||
<a
|
||||
href="#🚩"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#🚩' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('🚩')}🚩flagged</a
|
||||
>
|
||||
${Object.keys(this?.visible_private_messages ?? [])
|
||||
?.sort()
|
||||
?.map(
|
||||
|
||||
34
core/core.js
34
core/core.js
@@ -494,18 +494,28 @@ async function getProcessBlob(blobId, key, options) {
|
||||
);
|
||||
}
|
||||
};
|
||||
imports.ssb.swapWithServerIdentity = function (id) {
|
||||
if (
|
||||
process.credentials &&
|
||||
process.credentials.session &&
|
||||
process.credentials.session.name
|
||||
) {
|
||||
return ssb.swapWithServerIdentity(
|
||||
process.credentials.session.name,
|
||||
id
|
||||
);
|
||||
}
|
||||
};
|
||||
if (process.credentials?.permissions?.administration) {
|
||||
imports.ssb.swapWithServerIdentity = function (id) {
|
||||
if (
|
||||
process.credentials &&
|
||||
process.credentials.session &&
|
||||
process.credentials.session.name
|
||||
) {
|
||||
return ssb.swapWithServerIdentity(
|
||||
process.credentials.session.name,
|
||||
id
|
||||
);
|
||||
}
|
||||
};
|
||||
imports.ssb.addBlock = async function (id) {
|
||||
await imports.core.permissionTest('modify_blocks', `Block ${id}.`);
|
||||
await ssb_internal.addBlock(id);
|
||||
};
|
||||
imports.ssb.removeBlock = async function (id) {
|
||||
await imports.core.permissionTest('modify_blocks', `Unblock ${id}.`);
|
||||
await ssb_internal.removeBlock(id);
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
process.credentials &&
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
}:
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
pname = "tildefriends";
|
||||
version = "0.2025.9";
|
||||
version = "0.2025.11";
|
||||
|
||||
src = pkgs.fetchFromGitea {
|
||||
domain = "dev.tildefriends.net";
|
||||
owner = "cory";
|
||||
repo = "tildefriends";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-1nhsfhdOO5HIiiTMb+uROB8nDPL/UpOYm52hZ/OpPyk=";
|
||||
hash = "sha256-z4v4ghKOBTMv+agTUKg+HU8zfE4imluXFsozQCT4qX8=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1758589230,
|
||||
"narHash": "sha256-zMTCFGe8aVGTEr2RqUi/QzC1nOIQ0N1HRsbqB4f646k=",
|
||||
"lastModified": 1763948260,
|
||||
"narHash": "sha256-dY9qLD0H0zOUgU3vWacPY6Qc421BeQAfm8kBuBtPVE0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d1d883129b193f0b495d75c148c2c3a7d95789a0",
|
||||
"rev": "1c8ba8d3f7634acac4a2094eef7c32ad9106532c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.unprompted.tildefriends"
|
||||
android:versionCode="48"
|
||||
android:versionName="0.2025.11">
|
||||
android:versionCode="49"
|
||||
android:versionName="0.2025.12-wip">
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.2025.11</string>
|
||||
<string>0.2025.12</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>26</string>
|
||||
<string>27</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>iphoneos</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
||||
52
src/ssb.db.c
52
src/ssb.db.c
@@ -271,7 +271,6 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
")");
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS identities_user ON identities (user, public_key)");
|
||||
_tf_ssb_db_exec(db, "DELETE FROM identities WHERE user = ':auth'");
|
||||
|
||||
_tf_ssb_db_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS invites ("
|
||||
" invite_public_key TEXT PRIMARY KEY,"
|
||||
@@ -279,6 +278,11 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
" use_count INTEGER,"
|
||||
" expires INTEGER"
|
||||
")");
|
||||
_tf_ssb_db_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS blocks ("
|
||||
" id TEXT PRIMARY KEY,"
|
||||
" timestamp REAL"
|
||||
")");
|
||||
|
||||
bool populate_fts = false;
|
||||
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_fts')"))
|
||||
@@ -2868,3 +2872,49 @@ tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* u
|
||||
tf_free(info);
|
||||
return copy;
|
||||
}
|
||||
|
||||
void tf_ssb_db_add_block(sqlite3* db, const char* id)
|
||||
{
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare_v2(db, "INSERT INTO blocks (id, timestamp) VALUES (?, unixepoch() * 1000) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("add block: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"INSERT INTO blocks (id, timestamp) SELECT messages_refs.ref AS id, unixepoch() * 1000 AS timestamp FROM messages_refs WHERE messages_refs.message = ? ON CONFLICT DO "
|
||||
"NOTHING",
|
||||
-1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("add block messages ref: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
}
|
||||
|
||||
void tf_ssb_db_remove_block(sqlite3* db, const char* id)
|
||||
{
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare_v2(db, "DELETE FROM blocks WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("remove block: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
}
|
||||
|
||||
14
src/ssb.db.h
14
src/ssb.db.h
@@ -617,4 +617,18 @@ tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* u
|
||||
*/
|
||||
void tf_ssb_db_add_blob_wants(sqlite3* db, const char* id);
|
||||
|
||||
/**
|
||||
** Add an instance-wide block.
|
||||
** @param db The database.
|
||||
** @param id The account, message, or blob ID to block.
|
||||
*/
|
||||
void tf_ssb_db_add_block(sqlite3* db, const char* id);
|
||||
|
||||
/**
|
||||
** Remove an instance-wide block.
|
||||
** @param db The database.
|
||||
** @param id The account, message, or blob ID to unblock.
|
||||
*/
|
||||
void tf_ssb_db_remove_block(sqlite3* db, const char* id);
|
||||
|
||||
/** @} */
|
||||
|
||||
62
src/ssb.js.c
62
src/ssb.js.c
@@ -2172,6 +2172,66 @@ static JSValue _tf_ssb_port(JSContext* context, JSValueConst this_val, int argc,
|
||||
return JS_NewInt32(context, tf_ssb_server_get_port(ssb));
|
||||
}
|
||||
|
||||
typedef struct _modify_block_t
|
||||
{
|
||||
char id[k_id_base64_len];
|
||||
bool add;
|
||||
JSValue promise[2];
|
||||
} modify_block_t;
|
||||
|
||||
static void _tf_ssb_modify_block_work(tf_ssb_t* ssb, void* user_data)
|
||||
{
|
||||
modify_block_t* work = user_data;
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (work->add)
|
||||
{
|
||||
tf_ssb_db_add_block(db, work->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_ssb_db_remove_block(db, work->id);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
|
||||
static void _tf_ssb_modify_block_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||
{
|
||||
modify_block_t* request = user_data;
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 0, NULL);
|
||||
tf_util_report_error(context, error);
|
||||
JS_FreeValue(context, error);
|
||||
JS_FreeValue(context, request->promise[0]);
|
||||
JS_FreeValue(context, request->promise[1]);
|
||||
tf_free(request);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_add_block(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
const char* id = JS_ToCString(context, argv[0]);
|
||||
modify_block_t* work = tf_malloc(sizeof(modify_block_t));
|
||||
*work = (modify_block_t) { .add = true };
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||
JS_FreeCString(context, id);
|
||||
tf_ssb_run_work(ssb, _tf_ssb_modify_block_work, _tf_ssb_modify_block_after_work, work);
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_remove_block(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
const char* id = JS_ToCString(context, argv[0]);
|
||||
modify_block_t* work = tf_malloc(sizeof(modify_block_t));
|
||||
*work = (modify_block_t) { .add = false };
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||
JS_FreeCString(context, id);
|
||||
tf_ssb_run_work(ssb, _tf_ssb_modify_block_work, _tf_ssb_modify_block_after_work, work);
|
||||
return result;
|
||||
}
|
||||
|
||||
void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
||||
{
|
||||
JS_NewClassID(&_tf_ssb_classId);
|
||||
@@ -2227,6 +2287,8 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
||||
JS_SetPropertyStr(context, object_internal, "getIdentityInfo", JS_NewCFunction(context, _tf_ssb_getIdentityInfo, "getIdentityInfo", 3));
|
||||
JS_SetPropertyStr(context, object_internal, "addEventListener", JS_NewCFunction(context, _tf_ssb_add_event_listener, "addEventListener", 2));
|
||||
JS_SetPropertyStr(context, object_internal, "removeEventListener", JS_NewCFunction(context, _tf_ssb_remove_event_listener, "removeEventListener", 2));
|
||||
JS_SetPropertyStr(context, object_internal, "addBlock", JS_NewCFunction(context, _tf_ssb_add_block, "addBlock", 1));
|
||||
JS_SetPropertyStr(context, object_internal, "removeBlock", JS_NewCFunction(context, _tf_ssb_remove_block, "removeBlock", 1));
|
||||
|
||||
JS_FreeValue(context, global);
|
||||
}
|
||||
|
||||
@@ -1851,4 +1851,70 @@ void tf_ssb_test_following_perf(const tf_test_options_t* options)
|
||||
uv_run(&loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&loop);
|
||||
}
|
||||
|
||||
static void _store_callback(const char* id, bool verified, bool is_new, void* user_data)
|
||||
{
|
||||
tf_string_set(user_data, k_id_base64_len, id);
|
||||
}
|
||||
|
||||
void tf_ssb_test_blocks(const tf_test_options_t* options)
|
||||
{
|
||||
uv_loop_t loop = { 0 };
|
||||
uv_loop_init(&loop);
|
||||
|
||||
tf_printf("Testing blocks.\n");
|
||||
unlink("out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||
|
||||
tf_ssb_generate_keys(ssb);
|
||||
|
||||
uint8_t priv[512] = { 0 };
|
||||
tf_ssb_get_private_key(ssb, priv, sizeof(priv));
|
||||
|
||||
char id[k_id_base64_len] = { 0 };
|
||||
tf_ssb_whoami(ssb, id, sizeof(id));
|
||||
tf_printf("ID %s\n", id);
|
||||
|
||||
char blob_id[k_id_base64_len] = { 0 };
|
||||
const char* k_blob = "Hello, blob!";
|
||||
bool blob_stored = false;
|
||||
tf_ssb_add_blob_stored_callback(ssb, _blob_stored, NULL, &blob_stored);
|
||||
tf_ssb_db_blob_store(ssb, (const uint8_t*)k_blob, strlen(k_blob), blob_id, sizeof(blob_id), NULL);
|
||||
tf_ssb_notify_blob_stored(ssb, blob_id);
|
||||
tf_ssb_remove_blob_stored_callback(ssb, _blob_stored, &blob_stored);
|
||||
assert(blob_stored);
|
||||
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue obj = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, obj, "type", JS_NewString(context, "post"));
|
||||
JS_SetPropertyStr(context, obj, "text", JS_NewString(context, "First post."));
|
||||
JSValue mentions = JS_NewArray(context);
|
||||
JSValue mention = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, mention, "link", JS_NewString(context, blob_id));
|
||||
JS_SetPropertyUint32(context, mentions, 0, mention);
|
||||
JS_SetPropertyStr(context, obj, "mentions", mentions);
|
||||
JSValue signed_message = tf_ssb_sign_message(ssb, id, priv, obj, NULL, 0);
|
||||
char message_id[k_id_base64_len] = { 0 };
|
||||
tf_ssb_verify_strip_and_store_message(ssb, signed_message, _store_callback, message_id);
|
||||
JS_FreeValue(context, signed_message);
|
||||
while (!*message_id)
|
||||
{
|
||||
uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE);
|
||||
}
|
||||
JS_FreeValue(context, obj);
|
||||
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
tf_ssb_db_add_block(db, message_id);
|
||||
tf_ssb_db_add_block(db, blob_id);
|
||||
tf_ssb_db_add_block(db, id);
|
||||
tf_ssb_db_remove_block(db, blob_id);
|
||||
tf_ssb_db_remove_block(db, message_id);
|
||||
tf_ssb_db_remove_block(db, id);
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
|
||||
tf_ssb_destroy(ssb);
|
||||
|
||||
uv_run(&loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(&loop);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -107,4 +107,10 @@ void tf_ssb_test_cli(const tf_test_options_t* options);
|
||||
*/
|
||||
void tf_ssb_test_following_perf(const tf_test_options_t* options);
|
||||
|
||||
/**
|
||||
** Test blocks.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_blocks(const tf_test_options_t* options);
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -982,7 +982,6 @@ void tf_tests(const tf_test_options_t* options)
|
||||
_tf_test_run(options, "b64", _test_b64, false);
|
||||
_tf_test_run(options, "rooms", tf_ssb_test_rooms, false);
|
||||
_tf_test_run(options, "bench", tf_ssb_test_bench, false);
|
||||
_tf_test_run(options, "auto", _test_auto, false);
|
||||
_tf_test_run(options, "go-ssb-room", tf_ssb_test_go_ssb_room, true);
|
||||
_tf_test_run(options, "encrypt", tf_ssb_test_encrypt, false);
|
||||
_tf_test_run(options, "peer_exchange", tf_ssb_test_peer_exchange, false);
|
||||
@@ -994,6 +993,8 @@ void tf_tests(const tf_test_options_t* options)
|
||||
_tf_test_run(options, "triggers", tf_ssb_test_triggers, false);
|
||||
_tf_test_run(options, "cli", tf_ssb_test_cli, false);
|
||||
_tf_test_run(options, "following_perf", tf_ssb_test_following_perf, true);
|
||||
_tf_test_run(options, "blocks", tf_ssb_test_blocks, true);
|
||||
_tf_test_run(options, "auto", _test_auto, false);
|
||||
tf_printf("Tests completed.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#define VERSION_NUMBER "0.2025.11"
|
||||
#define VERSION_NUMBER "0.2025.12-wip"
|
||||
#define VERSION_NAME "This program kills fascists."
|
||||
|
||||
@@ -18,6 +18,7 @@ k_feeds = {
|
||||
'manyverse': 'https://gitlab.com/staltz/manyverse/-/commits/master?format=atom',
|
||||
'ahau': 'https://gitlab.com/ahau/ahau/-/commits/master.atom',
|
||||
'patchfox': 'https://github.com/soapdog/patchfox/commits.atom',
|
||||
'ponchowonky': 'https://github.com/soapdog/patchwork/commits.atom',
|
||||
}
|
||||
|
||||
def fix_title(entry):
|
||||
|
||||
Reference in New Issue
Block a user