Switch over to use use blocks from nostrdb

This is still kind of broken until queries are switched over to nostrdb.
Will do this next

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-02-01 17:26:57 -08:00
committed by Daniel D’Aquino
parent 208b3331ca
commit 28a06af534
51 changed files with 1086 additions and 501 deletions
@@ -103,7 +103,7 @@ struct NotificationFormatter {
content.title = Self.zap_notification_title(zap)
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
content.sound = UNNotificationSound.default
content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
content.userInfo = LossyLocalNotification(type: .zap, mention: .init(nip19: .note(notify.event.id))).to_user_info()
return (content, "myZapNotification")
default:
// The sync method should have taken care of this.
+1
View File
@@ -1 +1,2 @@
Fix q tags
1.5-24 profile loading was much better
+23 -23
View File
@@ -1179,7 +1179,7 @@ static INLINE int parse_i64(struct cursor *read, uint64_t *val)
shift = 0;
do {
if (!pull_byte(read, &byte))
if (!cursor_pull_byte(read, &byte))
return 0;
*val |= (byte & 0x7FULL) << shift;
shift += 7;
@@ -1199,7 +1199,7 @@ static INLINE int uleb128_read(struct cursor *read, unsigned int *val)
*val = 0;
for (;;) {
if (!pull_byte(read, &byte))
if (!cursor_pull_byte(read, &byte))
return 0;
*val |= (0x7F & byte) << shift;
@@ -1222,7 +1222,7 @@ static INLINE int sleb128_read(struct cursor *read, signed int *val)
shift = 0;
do {
if (!pull_byte(read, &byte))
if (!cursor_pull_byte(read, &byte))
return 0;
*val |= ((byte & 0x7F) << shift);
shift += 7;
@@ -1241,21 +1241,21 @@ static INLINE int uleb128_read(struct cursor *read, unsigned int *val)
unsigned char p[6] = {0};
*val = 0;
if (pull_byte(read, &p[0]) && (p[0] & 0x80) == 0) {
if (cursor_pull_byte(read, &p[0]) && (p[0] & 0x80) == 0) {
*val = LEB128_1(unsigned int);
if (p[0] == 0x7F)
assert((int)*val == -1);
return 1;
} else if (pull_byte(read, &p[1]) && (p[1] & 0x80) == 0) {
} else if (cursor_pull_byte(read, &p[1]) && (p[1] & 0x80) == 0) {
*val = LEB128_2(unsigned int);
return 2;
} else if (pull_byte(read, &p[2]) && (p[2] & 0x80) == 0) {
} else if (cursor_pull_byte(read, &p[2]) && (p[2] & 0x80) == 0) {
*val = LEB128_3(unsigned int);
return 3;
} else if (pull_byte(read, &p[3]) && (p[3] & 0x80) == 0) {
} else if (cursor_pull_byte(read, &p[3]) && (p[3] & 0x80) == 0) {
*val = LEB128_4(unsigned int);
return 4;
} else if (pull_byte(read, &p[4]) && (p[4] & 0x80) == 0) {
} else if (cursor_pull_byte(read, &p[4]) && (p[4] & 0x80) == 0) {
if (!(p[4] & 0xF0)) {
*val = LEB128_5(unsigned int);
return 5;
@@ -1296,7 +1296,7 @@ static int parse_section_tag(struct cursor *cur, enum section_tag *section)
start = cur->p;
if (!pull_byte(cur, &byte)) {
if (!cursor_pull_byte(cur, &byte)) {
return 0;
}
@@ -1315,7 +1315,7 @@ static int parse_valtype(struct wasm_parser *p, enum valtype *valtype)
start = p->cur.p;
if (unlikely(!pull_byte(&p->cur, (unsigned char*)valtype))) {
if (unlikely(!cursor_pull_byte(&p->cur, (unsigned char*)valtype))) {
return parse_err(p, "valtype tag oob");
}
@@ -1416,7 +1416,7 @@ static int parse_export_desc(struct wasm_parser *p, enum exportdesc *desc)
{
unsigned char byte;
if (!pull_byte(&p->cur, &byte)) {
if (!cursor_pull_byte(&p->cur, &byte)) {
parse_err(p, "export desc byte eof");
return 0;
}
@@ -1523,7 +1523,7 @@ static int parse_name_subsection(struct wasm_parser *p, struct namesec *sec, u32
u8 tag;
u8 *start = p->cur.p;
if (!pull_byte(&p->cur, &tag))
if (!cursor_pull_byte(&p->cur, &tag))
return parse_err(p, "name subsection tag oob?");
if (!is_valid_name_subsection(tag))
@@ -1676,7 +1676,7 @@ static int parse_reftype(struct wasm_parser *p, enum reftype *reftype)
{
u8 tag;
if (!pull_byte(&p->cur, &tag)) {
if (!cursor_pull_byte(&p->cur, &tag)) {
parse_err(p, "reftype");
return 0;
}
@@ -1720,7 +1720,7 @@ static int parse_export_section(struct wasm_parser *p,
static int parse_limits(struct wasm_parser *p, struct limits *limits)
{
unsigned char tag;
if (!pull_byte(&p->cur, &tag)) {
if (!cursor_pull_byte(&p->cur, &tag)) {
return parse_err(p, "oob");
}
@@ -1803,7 +1803,7 @@ static void print_code(u8 *code, int code_len)
make_cursor(code, code + code_len, &c);
for (;;) {
if (!pull_byte(&c, &tag)) {
if (!cursor_pull_byte(&c, &tag)) {
break;
}
@@ -2169,7 +2169,7 @@ static int parse_const_expr(struct expr_parser *p, struct expr *expr)
expr->code = p->code->p;
while (1) {
if (unlikely(!pull_byte(p->code, &tag))) {
if (unlikely(!cursor_pull_byte(p->code, &tag))) {
return note_error(p->errs, p->code, "oob");
}
@@ -2332,7 +2332,7 @@ static int parse_instrs_until_at(struct expr_parser *p, u8 stop_instr,
p->code->p - p->code->start,
dbg_inst, instr_name(stop_instr));
for (;;) {
if (!pull_byte(p->code, &tag))
if (!cursor_pull_byte(p->code, &tag))
return note_error(p->errs, p->code, "oob");
if ((tag != i_if && tag == stop_instr) ||
@@ -2413,7 +2413,7 @@ static int parse_element(struct wasm_parser *p, struct elem *elem)
make_expr_parser(&p->errs, &p->cur, &expr_parser);
if (!pull_byte(&p->cur, &tag))
if (!cursor_pull_byte(&p->cur, &tag))
return parse_err(p, "tag");
if (tag > 7)
@@ -2545,7 +2545,7 @@ static int parse_wdata(struct wasm_parser *p, struct wdata *data)
struct expr_parser parser;
u8 tag;
if (!pull_byte(&p->cur, &tag)) {
if (!cursor_pull_byte(&p->cur, &tag)) {
return parse_err(p, "tag");
}
@@ -2700,7 +2700,7 @@ static int parse_importdesc(struct wasm_parser *p, struct importdesc *desc)
{
u8 tag;
if (!pull_byte(&p->cur, &tag)) {
if (!cursor_pull_byte(&p->cur, &tag)) {
parse_err(p, "oom");
return 0;
}
@@ -4134,7 +4134,7 @@ static int parse_blocktype(struct cursor *cur, struct errors *errs, struct block
{
unsigned char byte;
if (unlikely(!pull_byte(cur, &byte))) {
if (unlikely(!cursor_pull_byte(cur, &byte))) {
return note_error(errs, cur, "parse_blocktype: oob\n");
}
@@ -4656,7 +4656,7 @@ static int parse_bulk_op(struct cursor *code, struct errors *errs,
{
u8 tag;
if (unlikely(!pull_byte(code, &tag)))
if (unlikely(!cursor_pull_byte(code, &tag)))
return note_error(errs, code, "oob");
if (unlikely(tag < 10 || tag > 17))
@@ -6552,7 +6552,7 @@ static INLINE int interp_parse_instr(struct wasm_interp *interp,
{
u8 tag;
if (unlikely(!pull_byte(code, &tag))) {
if (unlikely(!cursor_pull_byte(code, &tag))) {
return interp_error(interp, "no more instrs to pull");
}
+2
View File
@@ -27,6 +27,8 @@ static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
#define interp_error(p, fmt, ...) note_error(&((p)->errors), interp_codeptr(p), fmt, ##__VA_ARGS__)
#define parse_err(p, fmt, ...) note_error(&((p)->errs), &(p)->cur, fmt, ##__VA_ARGS__)
#include "short_types.h"
enum valtype {
val_i32 = 0x7F,
val_i64 = 0x7E,
+52 -6
View File
@@ -309,6 +309,28 @@
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; };
4CB8FC232A41ABA800763C51 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8FC222A41ABA500763C51 /* AboutView.swift */; };
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */; };
4CBB6F662B72B5DD000477A4 /* NdbBlocksIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */; };
4CBB6F672B72B5E8000477A4 /* NdbBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480582B633F3800F2B2C0 /* NdbBlock.swift */; };
4CBB6F682B72B5F0000477A4 /* NdbProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FFE2B631C0100F2B2C0 /* NdbProfile.swift */; };
4CBB6F692B72C783000477A4 /* NdbBlocksIterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */; };
4CBB6F6A2B730EF1000477A4 /* nostrdb.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FDE2B631C0100F2B2C0 /* nostrdb.c */; };
4CBB6F6B2B7310EC000477A4 /* tal.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4801F2B631C0100F2B2C0 /* tal.c */; };
4CBB6F6C2B7310F8000477A4 /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FF52B631C0100F2B2C0 /* sha256.c */; };
4CBB6F6D2B731102000477A4 /* take.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480232B631C0100F2B2C0 /* take.c */; };
4CBB6F6E2B731113000477A4 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FDF2B631C0100F2B2C0 /* block.c */; };
4CBB6F6F2B73116B000477A4 /* content_parser.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FF62B631C0100F2B2C0 /* content_parser.c */; };
4CBB6F702B731179000477A4 /* invoice.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480372B631C0100F2B2C0 /* invoice.c */; };
4CBB6F712B731184000477A4 /* bolt11.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480102B631C0100F2B2C0 /* bolt11.c */; };
4CBB6F722B7311AA000477A4 /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480122B631C0100F2B2C0 /* list.c */; };
4CBB6F732B7311AA000477A4 /* mem.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480182B631C0100F2B2C0 /* mem.c */; };
4CBB6F742B7311AA000477A4 /* hash_u5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4801A2B631C0100F2B2C0 /* hash_u5.c */; };
4CBB6F752B7311AA000477A4 /* talstr.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4801C2B631C0100F2B2C0 /* talstr.c */; };
4CBB6F762B7311AA000477A4 /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4801D2B631C0100F2B2C0 /* utf8.c */; };
4CBB6F772B7311AA000477A4 /* bech32.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF4801E2B631C0100F2B2C0 /* bech32.c */; };
4CBB6F782B7311AA000477A4 /* amount.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480252B631C0100F2B2C0 /* amount.c */; };
4CBB6F792B7311AA000477A4 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480262B631C0100F2B2C0 /* error.c */; };
4CBB6F7A2B7311AA000477A4 /* bech32_util.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480282B631C0100F2B2C0 /* bech32_util.c */; };
4CBB6F7C2B7312A7000477A4 /* nostr_bech32.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF47FE52B631C0100F2B2C0 /* nostr_bech32.c */; };
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */; };
4CC14FEF2A73FCCB007AEB17 /* IdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC14FEE2A73FCCB007AEB17 /* IdType.swift */; };
4CC14FF12A73FCDB007AEB17 /* Pubkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC14FF02A73FCDB007AEB17 /* Pubkey.swift */; };
@@ -398,10 +420,9 @@
4CF4804D2B631C0100F2B2C0 /* amount.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480252B631C0100F2B2C0 /* amount.c */; };
4CF4804E2B631C0100F2B2C0 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480262B631C0100F2B2C0 /* error.c */; };
4CF4804F2B631C0100F2B2C0 /* bech32_util.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480282B631C0100F2B2C0 /* bech32_util.c */; };
4CF480502B631C0100F2B2C0 /* libnostrdb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CF4802A2B631C0100F2B2C0 /* libnostrdb.a */; };
4CF480512B631C0100F2B2C0 /* node_id.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480342B631C0100F2B2C0 /* node_id.c */; };
4CF480522B631C0100F2B2C0 /* invoice.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480372B631C0100F2B2C0 /* invoice.c */; };
4CF480552B631C4F00F2B2C0 /* wasm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480532B631C4F00F2B2C0 /* wasm.c */; };
4CF480592B633F3800F2B2C0 /* NdbBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF480582B633F3800F2B2C0 /* NdbBlock.swift */; };
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD502E2A2DA45800A229DB /* MediaView.swift */; };
4CFF8F5929C9FD1E008DB934 /* DamusPurpleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */; };
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
@@ -1982,6 +2003,7 @@
4C1253652A76D0FF0004F4B8 /* OnlyZapsNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnlyZapsNotify.swift; sourceTree = "<group>"; };
4C1253672A76D2470004F4B8 /* MuteNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteNotify.swift; sourceTree = "<group>"; };
4C1253692A76D3850004F4B8 /* RelaysChangedNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaysChangedNotify.swift; sourceTree = "<group>"; };
4C15224A2B8D499F007CDC17 /* parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parser.h; sourceTree = "<group>"; };
4C15C7142A55DE7A00D0A0DB /* ReactionsSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsSettingsView.swift; sourceTree = "<group>"; };
4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; };
4C190F242A547D2000027FD5 /* LoadScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadScript.swift; sourceTree = "<group>"; };
@@ -2437,6 +2459,8 @@
4CF480372B631C0100F2B2C0 /* invoice.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = invoice.c; sourceTree = "<group>"; };
4CF480532B631C4F00F2B2C0 /* wasm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wasm.c; sourceTree = "<group>"; };
4CF480542B631C4F00F2B2C0 /* wasm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wasm.h; sourceTree = "<group>"; };
4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbBlocksIterator.swift; sourceTree = "<group>"; };
4CF480582B633F3800F2B2C0 /* NdbBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NdbBlock.swift; sourceTree = "<group>"; };
4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
4CFF8F5829C9FD1E008DB934 /* DamusPurpleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleView.swift; sourceTree = "<group>"; };
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
@@ -2687,7 +2711,6 @@
D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */,
D78DB8592C1CE9CA00F0AB12 /* SwipeActions in Frameworks */,
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
4CF480502B631C0100F2B2C0 /* libnostrdb.a in Frameworks */,
4C27C9322A64766F007DBC75 /* MarkdownUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -3144,6 +3167,7 @@
isa = PBXGroup;
children = (
D733F9E42D92C75C00317B11 /* UnownedNdbNote.swift */,
4C15224A2B8D499F007CDC17 /* parser.h */,
4CF47FDC2B631C0100F2B2C0 /* src */,
4C47928D2A9939BD00489948 /* flatcc */,
4CE9FBBB2A6B3D9C007E485C /* Test */,
@@ -3163,6 +3187,8 @@
4C78EFDA2A707C67007E8197 /* secp256k1_extrakeys.h */,
4C78EFD92A707C4D007E8197 /* secp256k1.h */,
D798D2272B085CDA00234419 /* NdbNote+.swift */,
4CF480562B633F2600F2B2C0 /* NdbBlocksIterator.swift */,
4CF480582B633F3800F2B2C0 /* NdbBlock.swift */,
);
path = nostrdb;
sourceTree = "<group>";
@@ -5167,6 +5193,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4CBB6F692B72C783000477A4 /* NdbBlocksIterator.swift in Sources */,
4C3DCC762A9FE9EC0091E592 /* NdbTxn.swift in Sources */,
4CEF958D2A9CE650000F901B /* verifier.c in Sources */,
D73BDB0E2D6FF5F600D69970 /* NostrNetworkManager.swift in Sources */,
@@ -5312,7 +5339,6 @@
D74AAFC22B153395006CF0F4 /* HeadlessDamusState.swift in Sources */,
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
D73BDB1A2D71311900D69970 /* UserRelayListErrors.swift in Sources */,
4CF480512B631C0100F2B2C0 /* node_id.c in Sources */,
4C30AC7629A5770900E2BD5A /* NotificationItemView.swift in Sources */,
4C86F7C42A76C44C00EC0817 /* ZappingNotify.swift in Sources */,
4C363A8428233689006E126D /* Parser.swift in Sources */,
@@ -5354,6 +5380,7 @@
D733F9E32D92C1D900317B11 /* SubscriptionManager.swift in Sources */,
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */,
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */,
4CF480592B633F3800F2B2C0 /* NdbBlock.swift in Sources */,
3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */,
F75BA12D29A1855400E10810 /* BookmarksManager.swift in Sources */,
4CC14FEF2A73FCCB007AEB17 /* IdType.swift in Sources */,
@@ -5492,7 +5519,6 @@
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */,
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */,
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */,
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
5C7389B12B6EFA7100781E0A /* ProxyView.swift in Sources */,
4C1253542A76C7D60004F4B8 /* LogoutNotify.swift in Sources */,
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
@@ -6795,6 +6821,27 @@
files = (
4C8FA7242BED58A900798A6A /* ThreadReply.swift in Sources */,
D733F9E52D92C76100317B11 /* UnownedNdbNote.swift in Sources */,
4CBB6F7C2B7312A7000477A4 /* nostr_bech32.c in Sources */,
4CBB6F722B7311AA000477A4 /* list.c in Sources */,
4CBB6F732B7311AA000477A4 /* mem.c in Sources */,
4CBB6F742B7311AA000477A4 /* hash_u5.c in Sources */,
4CBB6F752B7311AA000477A4 /* talstr.c in Sources */,
4CBB6F762B7311AA000477A4 /* utf8.c in Sources */,
4CBB6F772B7311AA000477A4 /* bech32.c in Sources */,
4CBB6F782B7311AA000477A4 /* amount.c in Sources */,
4CBB6F792B7311AA000477A4 /* error.c in Sources */,
4CBB6F7A2B7311AA000477A4 /* bech32_util.c in Sources */,
4CBB6F712B731184000477A4 /* bolt11.c in Sources */,
4CBB6F702B731179000477A4 /* invoice.c in Sources */,
4CBB6F6F2B73116B000477A4 /* content_parser.c in Sources */,
4CBB6F6E2B731113000477A4 /* block.c in Sources */,
4CBB6F6D2B731102000477A4 /* take.c in Sources */,
4CBB6F6C2B7310F8000477A4 /* sha256.c in Sources */,
4CBB6F6B2B7310EC000477A4 /* tal.c in Sources */,
4CBB6F6A2B730EF1000477A4 /* nostrdb.c in Sources */,
4CBB6F682B72B5F0000477A4 /* NdbProfile.swift in Sources */,
4CBB6F672B72B5E8000477A4 /* NdbBlock.swift in Sources */,
4CBB6F662B72B5DD000477A4 /* NdbBlocksIterator.swift in Sources */,
D798D21F2B0858D600234419 /* MigratedTypes.swift in Sources */,
D7CE1B472B0BE719002EDAD4 /* NativeObject.swift in Sources */,
D71AD9002CEC176A002E2C3C /* AppAccessibilityIdentifiers.swift in Sources */,
@@ -6909,7 +6956,6 @@
D7EDED262B117FC80018B19C /* StringUtil.swift in Sources */,
D7CE1B1E2B0BE190002EDAD4 /* midl.c in Sources */,
D7CB5D3C2B1130C600AD4105 /* LocalNotification.swift in Sources */,
D7CE1B2D2B0BE250002EDAD4 /* take.c in Sources */,
B59CAD4D2B688D1000677E8B /* MutelistManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
+11 -2
View File
@@ -1220,8 +1220,8 @@ extension LossyLocalNotification {
/// Computes a view open action from a mention reference.
/// Use this when opening a user-presentable interface to a specific mention reference.
func toViewOpenAction() -> ContentView.ViewOpenAction {
switch self.mention {
case .pubkey(let pubkey):
switch self.mention.nip19 {
case .npub(let pubkey):
return .route(.ProfileByKey(pubkey: pubkey))
case .note(let noteId):
return .route(.LoadableNostrEvent(note_reference: .note_id(noteId)))
@@ -1241,6 +1241,15 @@ extension LossyLocalNotification {
)))
case .naddr(let nAddr):
return .route(.LoadableNostrEvent(note_reference: .naddr(nAddr)))
case .nsec(_):
// `nsec` urls are a terrible idea security-wise, so we should intentionally not support those in order to discourage their use.
return .sheet(.error(ErrorView.UserPresentableError(
user_visible_description: NSLocalizedString("You opened an invalid link. The link you tried to open refers to \"nsec\", which is not supported.", comment: "User-visible error description for a user who tries to open an unsupported \"nsec\" link."),
tip: NSLocalizedString("Please contact the person who provided the link, and ask for another link. Also, this link may have sensitive information, please use caution before sharing it.", comment: "User-visible tip on what to do if a link contains an unsupported \"nsec\" reference."),
technical_info: "`MentionRef.toViewOpenAction` detected unsupported `nsec` contents"
)))
case .nscript(let script):
return .route(.Script(script: ScriptModel(data: script, state: .not_loaded)))
}
}
}
+101 -47
View File
@@ -18,22 +18,38 @@ enum MentionType: AsciiCharacter, TagKey {
}
}
enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
case pubkey(Pubkey)
case note(NoteId)
case nevent(NEvent)
case nprofile(NProfile)
case nrelay(String)
case naddr(NAddr)
extension UnsafePointer<UInt8> {
func as_data(size: Int) -> Data {
return Data(bytes: self, count: size)
}
}
struct MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
let nip19: Bech32Object
static func note(_ note_id: NoteId) -> MentionRef {
return self.init(nip19: .note(note_id))
}
init?(block: ndb_mention_bech32_block) {
guard let bech32_obj = Bech32Object.init(block: block) else {
return nil
}
self.nip19 = bech32_obj
}
init(nip19: Bech32Object) {
self.nip19 = nip19
}
var key: MentionType {
switch self {
case .pubkey: return .p
case .note: return .e
case .nevent: return .e
case .nprofile: return .p
switch self.nip19 {
case .note, .nevent: return .e
case .nprofile, .npub: return .p
case .nrelay: return .r
case .naddr: return .a
case .nscript: return .a
case .nsec: return .p
}
}
@@ -41,38 +57,40 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
return Bech32Object.encode(toBech32Object())
}
static func from_bech32(str: String) -> MentionRef? {
switch Bech32Object.parse(str) {
case .note(let noteid): return .note(noteid)
case .npub(let pubkey): return .pubkey(pubkey)
default: return nil
init?(bech32_str: String) {
guard let obj = Bech32Object.parse(bech32_str) else {
return nil
}
self.nip19 = obj
}
var pubkey: Pubkey? {
switch self {
case .pubkey(let pubkey): return pubkey
switch self.nip19 {
case .npub(let pubkey): return pubkey
case .note: return nil
case .nevent(let nevent): return nevent.author
case .nprofile(let nprofile): return nprofile.author
case .nrelay: return nil
case .naddr: return nil
case .nsec(let prv): return privkey_to_pubkey(privkey: prv)
case .nscript(_): return nil
}
}
var tag: [String] {
switch self {
case .pubkey(let pubkey): return ["p", pubkey.hex()]
switch self.nip19 {
case .npub(let pubkey): return ["p", pubkey.hex()]
case .note(let noteId): return ["e", noteId.hex()]
case .nevent(let nevent):
var tagBuilder = ["e", nevent.noteid.hex()]
let relay = nevent.relays.first
if let author = nevent.author?.hex() {
tagBuilder.append(relay ?? "")
tagBuilder.append(relay?.absoluteString ?? "")
tagBuilder.append(author)
} else if let relay {
tagBuilder.append(relay)
tagBuilder.append(relay.absoluteString)
}
return tagBuilder
@@ -80,7 +98,7 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
var tagBuilder = ["p", nprofile.author.hex()]
if let relay = nprofile.relays.first {
tagBuilder.append(relay)
tagBuilder.append(relay.absoluteString)
}
return tagBuilder
@@ -89,10 +107,14 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
var tagBuilder = ["a", "\(naddr.kind.description):\(naddr.author.hex()):\(naddr.identifier.string())"]
if let relay = naddr.relays.first {
tagBuilder.append(relay)
tagBuilder.append(relay.absoluteString)
}
return tagBuilder
case .nsec(_):
return []
case .nscript(_):
return []
}
}
@@ -112,10 +134,10 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
switch mention_type {
case .p:
guard let data = element.id() else { return nil }
return .pubkey(Pubkey(data))
return .init(nip19: .npub(Pubkey(data)))
case .e:
guard let data = element.id() else { return nil }
return .note(NoteId(data))
return .init(nip19: .note(NoteId(data)))
case .a:
let str = element.string()
let data = str.split(separator: ":")
@@ -124,26 +146,13 @@ enum MentionRef: TagKeys, TagConvertible, Equatable, Hashable {
guard let pubkey = Pubkey(hex: String(data[1])) else { return nil }
guard let kind = UInt32(data[0]) else { return nil }
return .naddr(NAddr(identifier: String(data[2]), author: pubkey, relays: [], kind: kind))
case .r: return .nrelay(element.string())
return .init(nip19: .naddr(NAddr(identifier: String(data[2]), author: pubkey, relays: [], kind: kind)))
case .r: return .init(nip19: .nrelay(element.string()))
}
}
func toBech32Object() -> Bech32Object {
switch self {
case .pubkey(let pk):
return .npub(pk)
case .note(let noteid):
return .note(noteid)
case .naddr(let naddr):
return .naddr(naddr)
case .nevent(let nevent):
return .nevent(nevent)
case .nprofile(let nprofile):
return .nprofile(nprofile)
case .nrelay(let url):
return .nrelay(url)
}
self.nip19
}
}
@@ -185,7 +194,6 @@ struct LightningInvoice<T> {
let amount: T
let string: String
let expiry: UInt64
let payment_hash: Data
let created_at: UInt64
var abbreviated: String {
@@ -213,8 +221,8 @@ struct LightningInvoice<T> {
}
}
func maybe_pointee<T>(_ p: UnsafeMutablePointer<T>!) -> T? {
guard p != nil else {
func maybe_pointee<T>(_ p: UnsafeMutablePointer<T>?) -> T? {
guard let p else {
return nil
}
return p.pointee
@@ -282,7 +290,7 @@ func format_msats(_ msat: Int64, locale: Locale = Locale.current) -> String {
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats)
}
func convert_invoice_description(b11: bolt11) -> InvoiceDescription? {
func convert_invoice_description(b11: ndb_invoice) -> InvoiceDescription? {
if let desc = b11.description {
return .description(String(cString: desc))
}
@@ -307,3 +315,49 @@ func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {
return nil
}
struct PostTags {
let blocks: [Block]
let tags: [[String]]
}
/// Convert
func make_post_tags(post_blocks: [Block], tags: [[String]]) -> PostTags {
var new_tags = tags
for post_block in post_blocks {
switch post_block {
case .mention(let mention):
switch(mention.ref.nip19) {
case .note, .nevent:
continue
default:
break
}
new_tags.append(mention.ref.tag)
case .hashtag(let hashtag):
new_tags.append(["t", hashtag.lowercased()])
case .text: break
case .invoice: break
case .relay: break
case .url(let url):
new_tags.append(["r", url.absoluteString])
break
}
}
return PostTags(blocks: post_blocks, tags: new_tags)
}
func post_to_event(post: NostrPost, keypair: FullKeypair) -> NostrEvent? {
let tags = post.references.map({ r in r.tag }) + post.tags
guard let post_blocks = parse_post_blocks(content: post.content)?.blocks else {
return nil
}
let post_tags = make_post_tags(post_blocks: post_blocks, tags: tags)
let content = post_tags.blocks
.map({ b in b.asString })
.joined(separator: "")
return NostrEvent(content: content, keypair: keypair.to_keypair(), kind: post.kind.rawValue, tags: post_tags.tags)
}
+24 -11
View File
@@ -778,13 +778,18 @@ func validate_event(ev: NostrEvent) -> ValidationResult {
return ok ? .ok : .bad_sig
}
func first_eref_mention(ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
let blocks = ev.blocks(keypair).blocks.filter { block in
func first_eref_mention(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
guard let blocks_txn = ev.blocks(ndb: ndb) else {
return nil
}
let ndb_blocks = blocks_txn.unsafeUnownedValue
let blocks = ndb_blocks.iter(note: ev).filter { block in
guard case .mention(let mention) = block else {
return false
}
switch mention.ref {
switch mention.bech32_type {
case .note, .nevent:
return true
default:
@@ -795,11 +800,13 @@ func first_eref_mention(ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
/// MARK: - Preview
if let firstBlock = blocks.first,
case .mention(let mention) = firstBlock {
switch mention.ref {
case .note(let note_id):
return .note(note_id)
case .nevent(let nevent):
return .note(nevent.noteid)
switch mention.bech32_type {
case .note:
let data = mention.bech32.note.event_id.as_data(size: 32)
return .note(NoteId(data))
case .nevent:
let data = mention.bech32.nevent.event_id.as_data(size: 32)
return .note(NoteId(data))
default:
return nil
}
@@ -807,9 +814,15 @@ func first_eref_mention(ev: NostrEvent, keypair: Keypair) -> Mention<NoteId>? {
return nil
}
func separate_invoices(ev: NostrEvent, keypair: Keypair) -> [Invoice]? {
let invoiceBlocks: [Invoice] = ev.blocks(keypair).blocks.reduce(into: []) { invoices, block in
guard case .invoice(let invoice) = block else {
func separate_invoices(ndb: Ndb, ev: NostrEvent) -> [Invoice]? {
guard let blocks_txn = ev.blocks(ndb: ndb) else {
return nil
}
let ndb_blocks = blocks_txn.unsafeUnownedValue
let invoiceBlocks: [Invoice] = ndb_blocks.iter(note: ev).reduce(into: []) { invoices, block in
guard case .invoice(let invoice) = block,
let invoice = invoice.as_invoice()
else {
return
}
invoices.append(invoice)
+1 -1
View File
@@ -89,7 +89,7 @@ enum NostrResponse {
free(data)
return nil
}
let new_note = note_data.assumingMemoryBound(to: ndb_note.self)
let new_note = ndb_note_ptr(ptr: OpaquePointer(note_data))
let note = NdbNote(note: new_note, size: Int(len), owned: true, key: nil)
guard let subid = sized_cstr(cstr: tce.subid, len: tce.subid_len) else {
+36 -82
View File
@@ -2,22 +2,11 @@
// Block.swift
// damus
//
// Created by Kyle Roucis on 2023-08-21.
//
import Foundation
fileprivate extension String {
/// Failable initializer to build a Swift.String from a C-backed `str_block_t`.
init?(_ s: str_block_t) {
let len = s.end - s.start
let bytes = Data(bytes: s.start, count: len)
self.init(bytes: bytes, encoding: .utf8)
}
}
/// Represents a block of data stored by the NOSTR protocol. This can be
/// Represents a block of data stored in nostrdb. This can be
/// simple text, a hashtag, a url, a relay reference, a mention ref and
/// potentially more in the future.
enum Block: Equatable {
@@ -38,22 +27,6 @@ enum Block: Equatable {
}
}
var is_previewable: Bool {
switch self {
case .mention(let m):
switch m.ref {
case .note, .nevent: return true
default: return false
}
case .invoice:
return true
case .url:
return true
default:
return false
}
}
case text(String)
case mention(Mention<MentionRef>)
case hashtag(String)
@@ -67,61 +40,56 @@ struct Blocks: Equatable {
let blocks: [Block]
}
extension ndb_str_block {
func as_str() -> String {
let buf = UnsafeBufferPointer(start: self.str, count: Int(self.len))
let uint8Buf = buf.map { UInt8(bitPattern: $0) }
return String(decoding: uint8Buf, as: UTF8.self)
}
}
extension ndb_block_ptr {
func as_str() -> String {
guard let str_block = ndb_block_str(self.ptr) else {
return ""
}
return str_block.pointee.as_str()
}
var block: ndb_block.__Unnamed_union_block {
self.ptr.pointee.block
}
}
extension Block {
/// Failable initializer for the C-backed type `block_t`. This initializer will inspect
/// the underlying block type and build the appropriate enum value as needed.
init?(_ block: block_t, tags: TagsSequence? = nil) {
switch block.type {
init?(block: ndb_block_ptr, tags: TagsSequence?) {
switch ndb_get_block_type(block.ptr) {
case BLOCK_HASHTAG:
guard let str = String(block.block.str) else {
return nil
}
self = .hashtag(str)
self = .hashtag(block.as_str())
case BLOCK_TEXT:
guard let str = String(block.block.str) else {
return nil
}
self = .text(str)
self = .text(block.as_str())
case BLOCK_MENTION_INDEX:
guard let b = Block(index: Int(block.block.mention_index), tags: tags) else {
return nil
}
self = b
case BLOCK_URL:
guard let b = Block(block.block.str) else {
return nil
}
self = b
guard let url = URL(string: block.as_str()) else { return nil }
self = .url(url)
case BLOCK_INVOICE:
guard let b = Block(invoice: block.block.invoice) else {
return nil
}
guard let b = Block(invoice: block.block.invoice) else { return nil }
self = b
case BLOCK_MENTION_BECH32:
guard let b = Block(bech32: block.block.mention_bech32) else {
return nil
}
guard let b = Block(bech32: block.block.mention_bech32) else { return nil }
self = b
default:
return nil
}
}
}
fileprivate extension Block {
/// Failable initializer for the C-backed type `str_block_t`.
init?(_ b: str_block_t) {
guard let str = String(b) else {
return nil
}
if let url = URL(string: str) {
self = .url(url)
}
else {
self = .text(str)
}
}
}
fileprivate extension Block {
/// Failable initializer for a block index and a tag sequence.
init?(index: Int, tags: TagsSequence? = nil) {
@@ -143,34 +111,19 @@ fileprivate extension Block {
}
}
}
fileprivate extension Block {
/// Failable initializer for the C-backed type `invoice_block_t`.
init?(invoice: invoice_block_t) {
guard let invstr = String(invoice.invstr) else {
return nil
}
guard var b11 = maybe_pointee(invoice.bolt11) else {
return nil
}
guard let description = convert_invoice_description(b11: b11) else {
return nil
}
let amount: Amount = maybe_pointee(b11.msat).map { .specific(Int64($0.millisatoshis)) } ?? .any
let payment_hash = Data(bytes: &b11.payment_hash, count: 32)
let created_at = b11.timestamp
tal_free(invoice.bolt11)
self = .invoice(Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, payment_hash: payment_hash, created_at: created_at))
init?(invoice: ndb_invoice_block) {
guard let invoice = invoice.as_invoice() else { return nil }
self = .invoice(invoice)
}
}
fileprivate extension Block {
/// Failable initializer for the C-backed type `mention_bech32_block_t`. This initializer will inspect the
/// bech32 type code and build the appropriate enum type.
init?(bech32 b: mention_bech32_block_t) {
init?(bech32 b: ndb_mention_bech32_block) {
guard let decoded = decodeCBech32(b.bech32) else {
return nil
}
@@ -180,6 +133,7 @@ fileprivate extension Block {
self = .mention(.any(ref))
}
}
extension Block {
var asString: String {
switch self {
+6 -4
View File
@@ -126,10 +126,12 @@ struct DMChatView: View, KeyboardReadable {
func send_message() {
let tags = [["p", pubkey.hex()]]
let post_blocks = parse_post_blocks(content: dms.draft)
let content = post_blocks
.map(\.asString)
.joined(separator: "")
let post_blocks = parse_post_blocks(content: dms.draft)?.blocks
guard let content = post_blocks?.map({ pb in pb.asString }).joined(separator: "") else {
// TODO: handle these errors somehow?
print("error creating dm")
return
}
guard let dm = NIP04.create_dm(content, to_pk: pubkey, tags: tags, keypair: damus_state.keypair) else {
print("error creating dm")
+1 -1
View File
@@ -17,7 +17,7 @@ struct DMView: View {
var Mention: some View {
Group {
if let mention = first_eref_mention(ev: event, keypair: damus_state.keypair) {
if let mention = first_eref_mention(ndb: damus_state.ndb, ev: event, keypair: damus_state.keypair) {
BuilderEventView(damus: damus_state, event_id: mention.ref)
} else {
EmptyView()
+6 -4
View File
@@ -35,12 +35,12 @@ struct EventShell<Content: View>: View {
!options.contains(.no_action_bar)
}
func get_mention() -> Mention<NoteId>? {
func get_mention(ndb: Ndb) -> Mention<NoteId>? {
if self.options.contains(.nested) || self.options.contains(.no_mentions) {
return nil
}
return first_eref_mention(ev: event, keypair: state.keypair)
return first_eref_mention(ndb: ndb, ev: event, keypair: state.keypair)
}
var ActionBar: some View {
@@ -73,7 +73,7 @@ struct EventShell<Content: View>: View {
content
if let mention = get_mention() {
if let mention = get_mention(ndb: state.ndb) {
MentionView(damus_state: state, mention: mention)
}
@@ -103,7 +103,9 @@ struct EventShell<Content: View>: View {
content
if !options.contains(.no_mentions), let mention = get_mention() {
if !options.contains(.no_mentions),
let mention = get_mention(ndb: state.ndb)
{
MentionView(damus_state: state, mention: mention)
.padding(.horizontal)
}
+78 -35
View File
@@ -66,14 +66,16 @@ func note_artifact_is_separated(kind: NostrKind?) -> Bool {
return kind != .longform
}
func render_note_content(ev: NostrEvent, profiles: Profiles, keypair: Keypair) -> NoteArtifacts {
let blocks = ev.blocks(keypair)
func render_note_content(ndb: Ndb, ev: NostrEvent, profiles: Profiles, keypair: Keypair) -> NoteArtifacts {
guard let blocks = ev.blocks(ndb: ndb) else {
return .separated(.just_content(ev.get_content(keypair)))
}
if ev.known_kind == .longform {
return .longform(LongformContent(ev.content))
}
return .separated(render_blocks(blocks: blocks, profiles: profiles, can_hide_last_previewable_refs: true))
return .separated(render_blocks(blocks: blocks.unsafeUnownedValue, profiles: profiles, note: ev, can_hide_last_previewable_refs: true))
}
// FIXME(tyiu): There are a lot of hacks to get this function to render the blocks correctly.
@@ -81,39 +83,54 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, keypair: Keypair) -
// Block previews should actually be rendered in the position of the note content where it was found.
// Currently, we put some previews at the bottom of the note, which is incorrect as they take things out of
// the author's intended context.
func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewable_refs: Bool = false) -> NoteArtifactsSeparated {
func render_blocks(blocks: NdbBlocks, profiles: Profiles, note: NdbNote, can_hide_last_previewable_refs: Bool = false) -> NoteArtifactsSeparated {
var invoices: [Invoice] = []
var urls: [UrlType] = []
let blocks = bs.blocks
var end_mention_count = 0
var end_url_count = 0
let ndb_blocks = blocks.iter(note: note).collect()
let one_note_ref = ndb_blocks
.filter({
if case .mention(let mention) = $0,
let typ = mention.bech32_type,
typ.is_notelike {
return true
}
return false
})
.count == 1
// Search backwards until we find the beginning index of the chain of previewables that reach the end of the content.
var hide_text_index = blocks.endIndex
var hide_text_index = ndb_blocks.endIndex
if can_hide_last_previewable_refs {
outerLoop: for (i, block) in blocks.enumerated().reversed() {
outerLoop: for (i, block) in ndb_blocks.enumerated().reversed() {
if block.is_previewable {
switch block {
case .mention:
end_mention_count += 1
// If there is more than one previewable mention,
// do not hide anything because we allow rich rendering of only one mention currently.
// This should be fixed in the future to show events inline instead.
if end_mention_count > 1 {
hide_text_index = blocks.endIndex
hide_text_index = ndb_blocks.endIndex
break outerLoop
}
case .url(let url):
case .url(let url_block):
guard let url_string = NdbBlock.convertToStringCopy(from: url_block),
let url = URL(string: url_string) else {
continue // We can't classify this, ignore and move on
}
let url_type = classify_url(url)
if case .link = url_type {
end_url_count += 1
// If there is more than one link, do not hide anything because we allow rich rendering of only
// one link.
if end_url_count > 1 {
hide_text_index = blocks.endIndex
hide_text_index = ndb_blocks.endIndex
break outerLoop
}
}
@@ -121,7 +138,9 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewa
break
}
hide_text_index = i
} else if case .text(let txt) = block, txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
} else if case .text(let txt_block) = block,
let txt = NdbBlock.convertToStringCopy(from: txt_block),
txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
// We should hide whitespace at the end sequence.
hide_text_index = i
} else if case .hashtag = block {
@@ -135,16 +154,21 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewa
}
var ind: Int = -1
let txt: CompatibleText = blocks.reduce(CompatibleText()) { str, block in
let txt: CompatibleText = ndb_blocks.reduce(into: CompatibleText()) { str, block in
ind = ind + 1
// Add the rendered previewable blocks to their type-specific lists.
switch block {
case .invoice(let invoice):
invoices.append(invoice)
case .url(let url):
case .url(let url_block):
guard let url_string = NdbBlock.convertToStringCopy(from: url_block),
let url = URL(string: url_string) else {
break // We can't classify this, ignore and move on
}
let url_type = classify_url(url)
urls.append(url_type)
case .invoice(let invoice_block):
guard let invoice = invoice_block.as_invoice() else { break }
invoices.append(invoice)
default:
break
}
@@ -153,7 +177,7 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewa
// If there are previewable blocks that occur before the consecutive sequence of them at the end of the content,
// we should not hide the text representation of any previewable block to avoid altering the format of the note.
if ind < hide_text_index && block.is_previewable {
hide_text_index = blocks.endIndex
hide_text_index = ndb_blocks.endIndex
}
// No need to show the text representation of the block if the only previewables are the sequence of them
@@ -162,41 +186,56 @@ func render_blocks(blocks bs: Blocks, profiles: Profiles, can_hide_last_previewa
// The only exception is that if there are hashtags embedded in the end sequence, which is not uncommon,
// then we still want to show those hashtags but hide everything else that is previewable in the end sequence.
if ind >= hide_text_index {
if case .text(let txt) = block, txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if case .hashtag = blocks[safe: ind+1] {
return str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: -1, txt: txt))
if case .text(let txt_block) = block,
let txt = NdbBlock.convertToStringCopy(from: txt_block),
txt.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if case .hashtag = ndb_blocks[safe: ind+1] {
str = str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: hide_text_index, txt: txt))
}
} else if case .hashtag(let htag) = block {
return str + hashtag_str(htag)
str = str + hashtag_str(htag.as_str())
}
return str
return
}
}
switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
if let typ = m.bech32_type, typ.is_notelike, one_note_ref {
return
}
guard let mention = MentionRef(block: m) else { return }
str = str + mention_str(.any(mention), profiles: profiles)
case .text(let txt):
if case .hashtag = blocks[safe: ind+1] {
// SPECIAL CASE:
// Do not trim whitespaces from suffix if the following block is a hashtag.
// This is because of the code further up (see "SPECIAL CASE").
return str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: -1, txt: txt))
str = str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: -1, txt: txt.as_str()))
} else {
return str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: hide_text_index, txt: txt))
str = str + CompatibleText(stringLiteral: reduce_text_block(ind: ind, hide_text_index: hide_text_index, txt: txt.as_str()))
}
case .relay(let relay):
return str + CompatibleText(stringLiteral: relay)
case .hashtag(let htag):
return str + hashtag_str(htag)
str = str + hashtag_str(htag.as_str())
case .invoice(let invoice):
return str + invoice_str(invoice)
guard let inv = invoice.as_invoice() else { return }
invoices.append(inv)
case .url(let url):
return str + url_str(url)
guard let url = URL(string: url.as_str()) else { return }
let url_type = classify_url(url)
switch url_type {
case .media:
urls.append(url_type)
case .link(let url):
urls.append(url_type)
str = str + url_str(url)
}
case .mention_index:
return
}
}
return NoteArtifactsSeparated(content: txt, words: bs.words, urls: urls, invoices: invoices)
return NoteArtifactsSeparated(content: txt, words: blocks.words, urls: urls, invoices: invoices)
}
func reduce_text_block(ind: Int, hide_text_index: Int, txt: String) -> String {
@@ -262,13 +301,17 @@ func mention_str(_ m: Mention<MentionRef>, profiles: Profiles) -> CompatibleText
let bech32String = Bech32Object.encode(m.ref.toBech32Object())
let display_str: String = {
switch m.ref {
case .pubkey(let pk): return getDisplayName(pk: pk, profiles: profiles)
switch m.ref.nip19 {
case .npub(let pk): return getDisplayName(pk: pk, profiles: profiles)
case .note: return abbrev_identifier(bech32String)
case .nevent: return abbrev_identifier(bech32String)
case .nprofile(let nprofile): return getDisplayName(pk: nprofile.author, profiles: profiles)
case .nrelay(let url): return url
case .naddr: return abbrev_identifier(bech32String)
case .nsec(let prv):
guard let npub = privkey_to_pubkey(privkey: prv)?.npub else { return "nsec..." }
return abbrev_identifier(npub)
case .nscript(_): return bech32String
}
}()
+50 -12
View File
@@ -23,6 +23,21 @@ struct Blur: UIViewRepresentable {
}
}
extension bech32_nprofile {
func matches_pubkey(pk: Pubkey) -> Bool {
pk.id.withUnsafeBytes { bytes in
memcmp(self.pubkey, bytes, 32) == 0
}
}
}
extension bech32_npub {
func matches_pubkey(pk: Pubkey) -> Bool {
pk.id.withUnsafeBytes { bytes in
memcmp(self.pubkey, bytes, 32) == 0
}
}
}
struct NoteContentView: View {
@@ -280,7 +295,7 @@ struct NoteContentView: View {
}
await preload_event(plan: plan, state: damus_state)
} else if force_artifacts {
let arts = render_note_content(ev: event, profiles: damus_state.profiles, keypair: damus_state.keypair)
let arts = render_note_content(ndb: damus_state.ndb, ev: event, profiles: damus_state.profiles, keypair: damus_state.keypair)
self.artifacts_model.state = .loaded(arts)
}
}
@@ -335,19 +350,36 @@ struct NoteContentView: View {
var body: some View {
ArtifactContent
.onReceive(handle_notify(.profile_updated)) { profile in
let blocks = event.blocks(damus_state.keypair)
for block in blocks.blocks {
guard let blocks_txn = event.blocks(ndb: damus_state.ndb) else {
return
}
let blocks = blocks_txn.unsafeUnownedValue
for block in blocks.iter(note: event) {
switch block {
case .mention(let m):
if case .pubkey(let pk) = m.ref, pk == profile.pubkey {
load(force_artifacts: true)
return
guard let typ = m.bech32_type else {
continue
}
switch typ {
case .nprofile:
if m.bech32.nprofile.matches_pubkey(pk: profile.pubkey) {
load(force_artifacts: true)
}
case .npub:
if m.bech32.npub.matches_pubkey(pk: profile.pubkey) {
load(force_artifacts: true)
}
case .nevent: continue
case .nrelay: continue
case .nsec: continue
case .note: continue
case .naddr: continue
}
case .relay: return
case .text: return
case .hashtag: return
case .url: return
case .invoice: return
case .mention_index(_): return
}
}
}
@@ -503,13 +535,19 @@ struct NoteContentView_Previews: PreviewProvider {
}
}
func separate_images(ev: NostrEvent, keypair: Keypair) -> [MediaUrl]? {
let urlBlocks: [URL] = ev.blocks(keypair).blocks.reduce(into: []) { urls, block in
guard case .url(let url) = block else {
func separate_images(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> [MediaUrl]? {
guard let blocks_txn = ev.blocks(ndb: ndb) else {
return nil
}
let blocks = blocks_txn.unsafeUnownedValue
let urlBlocks: [URL] = blocks.iter(note: ev).reduce(into: []) { urls, block in
guard case .url(let url) = block,
let parsed_url = URL(string: url.as_str()) else {
return
}
if classify_url(url).is_img != nil {
urls.append(url)
if classify_url(parsed_url).is_img != nil {
urls.append(parsed_url)
}
}
let mediaUrls = urlBlocks.map { MediaUrl.image($0) }
@@ -82,7 +82,7 @@ struct SelectedEventView: View {
var Mention: some View {
Group {
if let mention = first_eref_mention(ev: event, keypair: damus.keypair) {
if let mention = first_eref_mention(ndb: damus.ndb, ev: event, keypair: damus.keypair) {
MentionView(damus_state: damus, mention: mention)
.padding(.horizontal)
}
@@ -48,7 +48,7 @@ let test_longform_event = LongformEvent.parse(from: NostrEvent(
struct LongformView_Previews: PreviewProvider {
static var previews: some View {
let st = test_damus_state
let artifacts = render_note_content(ev: test_longform_event.event, profiles: st.profiles, keypair: Keypair(pubkey: .empty, privkey: nil))
let artifacts = render_note_content(ndb: st.ndb, ev: test_longform_event.event, profiles: st.profiles, keypair: Keypair(pubkey: .empty, privkey: nil))
let model = NoteArtifactsModel(state: .loaded(artifacts))
ScrollView {
@@ -18,7 +18,7 @@ func process_local_notification(state: HeadlessDamusState, event ev: NostrEvent)
return
}
guard let local_notification = generate_local_notification_object(from: ev, state: state) else {
guard let local_notification = generate_local_notification_object(ndb: state.ndb, from: ev, state: state) else {
return
}
@@ -65,19 +65,21 @@ func should_display_notification(state: HeadlessDamusState, event ev: NostrEvent
return true
}
func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamusState) -> LocalNotification? {
func generate_local_notification_object(ndb: Ndb, from ev: NostrEvent, state: HeadlessDamusState) -> LocalNotification? {
guard let type = ev.known_kind else {
return nil
}
if type == .text, state.settings.mention_notification {
let blocks = ev.blocks(state.keypair).blocks
for case .mention(let mention) in blocks {
guard case .pubkey(let pk) = mention.ref, pk == state.keypair.pubkey else {
if type == .text,
state.settings.mention_notification,
let blocks = ev.blocks(ndb: ndb)?.unsafeUnownedValue
{
for case .mention(let mention) in blocks.iter(note: ev) {
guard case .npub = mention.bech32_type,
(memcmp(state.keypair.pubkey.id.bytes, mention.bech32.npub.pubkey, 32) == 0) else {
continue
}
let content_preview = render_notification_content_preview(ev: ev, profiles: state.profiles, keypair: state.keypair)
let content_preview = render_notification_content_preview(ndb: ndb, ev: ev, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .mention, event: ev, target: .note(ev), content: content_preview)
}
@@ -101,13 +103,13 @@ func generate_local_notification_object(from ev: NostrEvent, state: HeadlessDamu
state.settings.repost_notification,
let inner_ev = ev.get_inner_event()
{
let content_preview = render_notification_content_preview(ev: inner_ev, profiles: state.profiles, keypair: state.keypair)
let content_preview = render_notification_content_preview(ndb: ndb, ev: inner_ev, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .repost, event: ev, target: .note(inner_ev), content: content_preview)
} else if type == .like, state.settings.like_notification, let evid = ev.referenced_ids.last {
if let txn = state.ndb.lookup_note(evid, txn_name: "local_notification_like"),
let liked_event = txn.unsafeUnownedValue
{
let content_preview = render_notification_content_preview(ev: liked_event, profiles: state.profiles, keypair: state.keypair)
let content_preview = render_notification_content_preview(ndb: ndb, ev: liked_event, profiles: state.profiles, keypair: state.keypair)
return LocalNotification(type: .like, event: ev, target: .note(liked_event), content: content_preview)
} else {
return LocalNotification(type: .like, event: ev, target: .note_id(evid), content: "")
@@ -144,10 +146,10 @@ func create_local_notification(profiles: Profiles, notify: LocalNotification) {
}
}
func render_notification_content_preview(ev: NostrEvent, profiles: Profiles, keypair: Keypair) -> String {
func render_notification_content_preview(ndb: Ndb, ev: NostrEvent, profiles: Profiles, keypair: Keypair) -> String {
let prefix_len = 300
let artifacts = render_note_content(ev: ev, profiles: profiles, keypair: keypair)
let artifacts = render_note_content(ndb: ndb, ev: ev, profiles: profiles, keypair: keypair)
// special case for longform events
if ev.known_kind == .longform {
+38 -2
View File
@@ -97,7 +97,43 @@ extension NostrPost {
}
}
func parse_post_blocks(content: String) -> [Block] {
return parse_note_content(content: .content(content, nil)).blocks
/// Return a list of tags
func parse_post_blocks(content: String) -> Blocks? {
let buf_size = 16000
var buffer = Data(capacity: buf_size)
var blocks_ptr = ndb_blocks_ptr()
var ok = false
return content.withCString { c_content -> Blocks? in
buffer.withUnsafeMutableBytes { buf in
let res = ndb_parse_content(buf, Int32(buf_size), c_content, Int32(content.utf8.count), &blocks_ptr.ptr)
ok = res != 0
}
guard ok else { return nil }
let words = ndb_blocks_word_count(blocks_ptr.ptr)
let bs = collect_blocks(ptr: blocks_ptr, content: c_content)
return Blocks(words: Int(words), blocks: bs)
}
}
fileprivate func collect_blocks(ptr: ndb_blocks_ptr, content: UnsafePointer<CChar>) -> [Block] {
var i = ndb_block_iterator()
var blocks: [Block] = []
var block_ptr = ndb_block_ptr()
ndb_blocks_iterate_start(content, ptr.ptr, &i);
block_ptr.ptr = ndb_blocks_iterate_next(&i)
while (block_ptr.ptr != nil) {
// tags are only used for indexed mentions which aren't used in
// posts anymore, so to simplify the API let's set this to nil
if let block = Block(block: block_ptr, tags: nil) {
blocks.append(block);
}
block_ptr.ptr = ndb_blocks_iterate_next(&i)
}
return blocks
}
+3 -2
View File
@@ -47,8 +47,9 @@ struct AboutView: View {
}
}
.onAppear {
let blocks = parse_note_content(content: .content(about, nil))
about_string = render_blocks(blocks: blocks, profiles: state.profiles).content.attributed
// TODO: Fix about content
//let blocks = ndb_parse_content(content: .content(about, nil))
//about_string = render_blocks(blocks: blocks, profiles: state.profiles).content.attributed
}
}
@@ -781,7 +781,7 @@ class HomeModel: ContactsDelegate {
notification_status.new_events = notifs
guard should_display_notification(state: damus_state, event: ev, mode: .local),
let notification_object = generate_local_notification_object(from: ev, state: damus_state)
let notification_object = generate_local_notification_object(ndb: self.damus_state.ndb, from: ev, state: damus_state)
else {
return
}
@@ -1155,7 +1155,7 @@ func create_in_app_profile_zap_notification(profiles: Profiles, zap: Zap, locale
content.title = NotificationFormatter.zap_notification_title(zap)
content.body = NotificationFormatter.zap_notification_body(profiles: profiles, zap: zap, locale: locale)
content.sound = UNNotificationSound.default
content.userInfo = LossyLocalNotification(type: .profile_zap, mention: .pubkey(profile_id)).to_user_info()
content.userInfo = LossyLocalNotification(type: .profile_zap, mention: .init(nip19: .npub(profile_id))).to_user_info()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
@@ -159,11 +159,14 @@ func translate_note(profiles: Profiles, keypair: Keypair, event: NostrEvent, set
}
// Render translated note
let translated_blocks = parse_note_content(content: .content(translated_note, event.tags))
let artifacts = render_blocks(blocks: translated_blocks, profiles: profiles, can_hide_last_previewable_refs: true)
// TODO: fix translated blocks
//let translated_blocks = parse_note_content(content: .content(translated_note, event.tags))
//let artifacts = render_blocks(blocks: translated_blocks, profiles: profiles, can_hide_last_previewable_refs: true)
return .not_needed
// and cache it
return .translated(Translated(artifacts: artifacts, language: note_lang))
//return .translated(Translated(artifacts: artifacts, language: note_lang))
}
func current_language() -> String {
+31 -22
View File
@@ -408,7 +408,7 @@ func invoice_to_zap_invoice(_ invoice: Invoice) -> ZapInvoice? {
return nil
}
return ZapInvoice(description: invoice.description, amount: amt, string: invoice.string, expiry: invoice.expiry, payment_hash: invoice.payment_hash, created_at: invoice.created_at)
return ZapInvoice(description: invoice.description, amount: amt, string: invoice.string, expiry: invoice.expiry, created_at: invoice.created_at)
}
func determine_zap_target(_ ev: NostrEvent) -> ZapTarget? {
@@ -422,35 +422,44 @@ func determine_zap_target(_ ev: NostrEvent) -> ZapTarget? {
return .profile(ptag)
}
extension UnsafePointer<CChar> {
func as_str() -> String {
String(cString: self)
}
}
func decode_bolt11(_ s: String) -> Invoice? {
var bs = note_blocks()
bs.num_blocks = 0
blocks_init(&bs)
let bytes = s.utf8CString
var bolt11_ptr: UnsafeMutablePointer<bolt11>?
let _ = bytes.withUnsafeBufferPointer { p in
damus_parse_content(&bs, p.baseAddress)
bolt11_ptr = bolt11_decode(nil, p.baseAddress, nil)
}
guard bs.num_blocks == 1 else {
blocks_free(&bs)
guard let bolt11 = maybe_pointee(bolt11_ptr) else {
return nil
}
let block = bs.blocks[0]
guard let converted = Block(block) else {
blocks_free(&bs)
return nil
var amount: Amount = .any
var desc: InvoiceDescription = .description("")
if let amt = maybe_pointee(bolt11.msat) {
amount = .specific(Int64(amt.millisatoshis))
}
guard case .invoice(let invoice) = converted else {
blocks_free(&bs)
return nil
let expiry = bolt11.expiry
let created_at = bolt11.timestamp
if var deschash = maybe_pointee(bolt11.description_hash) {
let data = Data(bytes: &deschash.u, count: 32)
desc = .description_hash(data)
} else {
desc = .description(bolt11.description.as_str())
}
blocks_free(&bs)
let invoice = Invoice(description: desc, amount: amount, string: s, expiry: expiry, created_at: created_at)
tal_free(bolt11_ptr)
return invoice
}
+140
View File
@@ -0,0 +1,140 @@
//
// EventRef.swift
// damus
//
// Created by William Casarin on 2022-05-08.
//
import Foundation
enum EventRef: Equatable {
case mention(Mention<NoteRef>)
case thread_id(NoteRef)
case reply(NoteRef)
case reply_to_root(NoteRef)
var is_mention: NoteRef? {
if case .mention(let m) = self { return m.ref }
return nil
}
var is_direct_reply: NoteRef? {
switch self {
case .mention:
return nil
case .thread_id:
return nil
case .reply(let refid):
return refid
case .reply_to_root(let refid):
return refid
}
}
var is_thread_id: NoteRef? {
switch self {
case .mention:
return nil
case .thread_id(let referencedId):
return referencedId
case .reply:
return nil
case .reply_to_root(let referencedId):
return referencedId
}
}
var is_reply: NoteRef? {
switch self {
case .mention:
return nil
case .thread_id:
return nil
case .reply(let refid):
return refid
case .reply_to_root(let refid):
return refid
}
}
}
func build_mention_indices(_ blocks: BlocksSequence, type: MentionType) -> Set<Int> {
return blocks.reduce(into: []) { acc, block in
switch block {
case .mention:
return
case .mention_index(let idx):
return
case .text:
return
case .hashtag:
return
case .url:
return
case .invoice:
return
}
}
}
func interp_event_refs_without_mentions(_ refs: [NoteRef]) -> [EventRef] {
if refs.count == 0 {
return []
}
if refs.count == 1 {
return [.reply_to_root(refs[0])]
}
var evrefs: [EventRef] = []
var first: Bool = true
for ref in refs {
if first {
evrefs.append(.thread_id(ref))
first = false
} else {
evrefs.append(.reply(ref))
}
}
return evrefs
}
func interp_event_refs_with_mentions(tags: Tags) -> [EventRef] {
var mentions: [EventRef] = []
var ev_refs: [NoteRef] = []
var i: Int = 0
for tag in tags {
if let ref = NoteRef.from_tag(tag: tag) {
ev_refs.append(ref)
}
i += 1
}
var replies = interp_event_refs_without_mentions(ev_refs)
replies.append(contentsOf: mentions)
return replies
}
func interpret_event_refs(blocks: BlocksSequence, tags: Tags) -> [EventRef] {
if tags.count == 0 {
return []
}
/// build a set of indices for each event mention
//let mention_indices = build_mention_indices(blocks, type: .e)
/// simpler case with no mentions
//if mention_indices.count == 0 {
//return interp_event_refs_without_mentions_ndb(References<NoteRef>(tags: tags))
//}
return interp_event_refs_with_mentions(tags: tags)
}
func event_is_reply(_ refs: [EventRef]) -> Bool {
return refs.contains { evref in
return evref.is_reply != nil
}
}
+2 -1
View File
@@ -118,7 +118,8 @@ func getUrlToOpen(invoice: String, with wallet: Wallet.Model) throws(OpenWalletE
}
}
let test_invoice = Invoice(description: .description("this is a description"), amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119)
let test_invoice = Invoice(description: .description("this is a description"), amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, created_at: 1666139119)
struct InvoiceView_Previews: PreviewProvider {
static var previews: some View {
+1 -1
View File
@@ -29,7 +29,7 @@ struct InvoicesView: View {
struct InvoicesView_Previews: PreviewProvider {
static var previews: some View {
InvoicesView(our_pubkey: test_note.pubkey, invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)], settings: test_damus_state.settings)
InvoicesView(our_pubkey: test_note.pubkey, invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, created_at: 1000000)], settings: test_damus_state.settings)
.frame(width: 300)
}
}
+126 -113
View File
@@ -10,38 +10,18 @@ import Foundation
fileprivate extension String {
/// Failable initializer to build a Swift.String from a C-backed `str_block_t`.
init?(_ s: str_block_t) {
let len = s.end - s.start
let bytes = Data(bytes: s.start, count: len)
let bytes = Data(bytes: s.str, count: Int(s.len))
self.init(bytes: bytes, encoding: .utf8)
}
}
struct NEvent : Equatable, Hashable {
let noteid: NoteId
let relays: [String]
let relays: [RelayURL]
let author: Pubkey?
let kind: UInt32?
init(noteid: NoteId, relays: [String]) {
self.noteid = noteid
self.relays = relays
self.author = nil
self.kind = nil
}
init(noteid: NoteId, relays: [String], author: Pubkey?) {
self.noteid = noteid
self.relays = relays
self.author = author
self.kind = nil
}
init(noteid: NoteId, relays: [String], kind: UInt32?) {
self.noteid = noteid
self.relays = relays
self.author = nil
self.kind = kind
}
init(noteid: NoteId, relays: [String], author: Pubkey?, kind: UInt32?) {
init(noteid: NoteId, relays: [RelayURL], author: Pubkey? = nil, kind: UInt32? = nil) {
self.noteid = noteid
self.relays = relays
self.author = author
@@ -55,17 +35,64 @@ struct NEvent : Equatable, Hashable {
struct NProfile : Equatable, Hashable {
let author: Pubkey
let relays: [String]
let relays: [RelayURL]
}
struct NAddr : Equatable, Hashable {
let identifier: String
let author: Pubkey
let relays: [String]
let relays: [RelayURL]
let kind: UInt32
}
enum Bech32Object : Equatable {
extension ndb_relays {
func as_urls() -> [RelayURL] {
var urls = [RelayURL]()
//
// This is so incredibly dumb but it's just what the Swift <-> C bridge
// does and I don't have a better way that doesn't involve complicated
// and slow stuff like reflection
//
let (r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20,r21,r22,r23) = self.relays
for i in 0..<self.num_relays {
switch i {
case 0: if let relay = RelayURL(r0.as_str()) { urls.append(relay) }
case 1: if let relay = RelayURL(r1.as_str()) { urls.append(relay) }
case 2: if let relay = RelayURL(r2.as_str()) { urls.append(relay) }
case 3: if let relay = RelayURL(r3.as_str()) { urls.append(relay) }
case 4: if let relay = RelayURL(r4.as_str()) { urls.append(relay) }
case 5: if let relay = RelayURL(r5.as_str()) { urls.append(relay) }
case 6: if let relay = RelayURL(r6.as_str()) { urls.append(relay) }
case 7: if let relay = RelayURL(r7.as_str()) { urls.append(relay) }
case 8: if let relay = RelayURL(r8.as_str()) { urls.append(relay) }
case 9: if let relay = RelayURL(r9.as_str()) { urls.append(relay) }
case 10: if let relay = RelayURL(r10.as_str()) { urls.append(relay) }
case 11: if let relay = RelayURL(r11.as_str()) { urls.append(relay) }
case 12: if let relay = RelayURL(r12.as_str()) { urls.append(relay) }
case 13: if let relay = RelayURL(r13.as_str()) { urls.append(relay) }
case 14: if let relay = RelayURL(r14.as_str()) { urls.append(relay) }
case 15: if let relay = RelayURL(r15.as_str()) { urls.append(relay) }
case 16: if let relay = RelayURL(r16.as_str()) { urls.append(relay) }
case 17: if let relay = RelayURL(r17.as_str()) { urls.append(relay) }
case 18: if let relay = RelayURL(r18.as_str()) { urls.append(relay) }
case 19: if let relay = RelayURL(r19.as_str()) { urls.append(relay) }
case 20: if let relay = RelayURL(r20.as_str()) { urls.append(relay) }
case 21: if let relay = RelayURL(r21.as_str()) { urls.append(relay) }
case 22: if let relay = RelayURL(r22.as_str()) { urls.append(relay) }
case 23: if let relay = RelayURL(r23.as_str()) { urls.append(relay) }
default:
break
}
}
return urls
}
}
enum Bech32Object : Equatable, Hashable {
case nsec(Privkey)
case npub(Pubkey)
case note(NoteId)
@@ -75,26 +102,68 @@ enum Bech32Object : Equatable {
case nrelay(String)
case naddr(NAddr)
init?(block: ndb_mention_bech32_block) {
let b32 = block.bech32
switch block.bech32_type {
case .note:
let data = b32.note.event_id.as_data(size: 32)
self = .note(NoteId(data))
case .npub:
let data = b32.npub.pubkey.as_data(size: 32)
self = .npub(Pubkey(data))
case .nprofile:
let pk = b32.nprofile.pubkey.as_data(size: 32)
let relays = b32.nprofile.relays.as_urls()
self = .nprofile(NProfile(author: Pubkey(pk), relays: relays))
case .nevent:
let nevent = b32.nevent
let note_id = NoteId(nevent.event_id.as_data(size: 32))
let relays = nevent.relays.as_urls()
var author: Pubkey? = nil
if nevent.pubkey != nil {
author = Pubkey(nevent.pubkey.as_data(size: 32))
}
var kind: UInt32? = nil
if nevent.has_kind != 0 {
kind = nevent.kind
}
self = .nevent(NEvent(noteid: note_id, relays: relays, author: author, kind: kind))
case .nrelay:
self = .nrelay(b32.nrelay.relay.as_str())
case .naddr:
let identifier = b32.naddr.identifier.as_str()
let author = Pubkey(b32.naddr.pubkey.as_data(size: 32))
let relays = b32.naddr.relays.as_urls()
self = .naddr(NAddr(identifier: identifier, author: author, relays: relays, kind: b32.naddr.kind))
case .nsec:
return nil
case .none:
return nil
}
}
static func parse(_ str: String) -> Bech32Object? {
if str.starts(with: "nscript"), let decoded = try? bech32_decode(str) {
return .nscript(decoded.data.bytes)
}
var b: nostr_bech32_t = nostr_bech32()
var bytes = Data(capacity: str.utf8.count)
let bytes = Array(str.utf8)
bytes.withUnsafeBufferPointer { buffer in
guard let baseAddress = buffer.baseAddress else { return }
var cursorInstance = cursor()
cursorInstance.start = UnsafeMutablePointer(mutating: baseAddress)
cursorInstance.p = UnsafeMutablePointer(mutating: baseAddress)
cursorInstance.end = cursorInstance.start.advanced(by: buffer.count)
parse_nostr_bech32(&cursorInstance, &b)
let ok = str.withCString { cstr in
let ok = bytes.withUnsafeMutableBytes { buffer -> Int32 in
guard let addr = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return 0
}
return parse_nostr_bech32(addr, Int32(buffer.count), cstr, str.utf8.count, &b)
}
return ok != 0
}
guard ok else { return nil }
return decodeCBech32(b)
}
@@ -117,25 +186,7 @@ enum Bech32Object : Equatable {
}
func toMentionRef() -> MentionRef? {
switch self {
case .nsec(let privkey):
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil }
return .pubkey(pubkey)
case .npub(let pubkey):
return .pubkey(pubkey)
case .note(let noteid):
return .note(noteid)
case .nscript(_):
return nil
case .nevent(let nevent):
return .nevent(nevent)
case .nprofile(let nprofile):
return .nprofile(nprofile)
case .nrelay(let relayURL):
return .nrelay(relayURL)
case .naddr(let naddr):
return .naddr(naddr)
}
MentionRef(nip19: self)
}
}
@@ -143,75 +194,37 @@ enum Bech32Object : Equatable {
func decodeCBech32(_ b: nostr_bech32_t) -> Bech32Object? {
switch b.type {
case NOSTR_BECH32_NOTE:
let note = b.data.note;
let note_id = NoteId(Data(bytes: note.event_id, count: 32))
let note_id = NoteId(Data(bytes: b.note.event_id, count: 32))
return .note(note_id)
case NOSTR_BECH32_NEVENT:
let nevent = b.data.nevent;
let note_id = NoteId(Data(bytes: nevent.event_id, count: 32))
let pubkey = nevent.pubkey != nil ? Pubkey(Data(bytes: nevent.pubkey, count: 32)) : nil
let kind: UInt32? = nevent.has_kind ? nevent.kind : nil
let relays = getRelayStrings(from: nevent.relays)
let note_id = NoteId(Data(bytes: b.nevent.event_id, count: 32))
let pubkey = b.nevent.pubkey != nil ? Pubkey(Data(bytes: b.nevent.pubkey, count: 32)) : nil
let kind: UInt32? = b.nevent.has_kind == 0 ? nil : b.nevent.kind
let relays = b.nevent.relays.as_urls()
return .nevent(NEvent(noteid: note_id, relays: relays, author: pubkey, kind: kind))
case NOSTR_BECH32_NPUB:
let npub = b.data.npub
let pubkey = Pubkey(Data(bytes: npub.pubkey, count: 32))
let pubkey = Pubkey(Data(bytes: b.npub.pubkey, count: 32))
return .npub(pubkey)
case NOSTR_BECH32_NSEC:
let nsec = b.data.nsec
let privkey = Privkey(Data(bytes: nsec.nsec, count: 32))
let privkey = Privkey(Data(bytes: b.nsec.nsec, count: 32))
guard let pubkey = privkey_to_pubkey(privkey: privkey) else { return nil }
return .npub(pubkey)
case NOSTR_BECH32_NPROFILE:
let nprofile = b.data.nprofile
let pubkey = Pubkey(Data(bytes: nprofile.pubkey, count: 32))
return .nprofile(NProfile(author: pubkey, relays: getRelayStrings(from: nprofile.relays)))
let pubkey = Pubkey(Data(bytes: b.nprofile.pubkey, count: 32))
return .nprofile(NProfile(author: pubkey, relays: b.nprofile.relays.as_urls()))
case NOSTR_BECH32_NRELAY:
let nrelay = b.data.nrelay
let str_relay: str_block = nrelay.relay
guard let relay_str = String(str_relay) else {
return nil
}
return .nrelay(relay_str)
return .nrelay(b.nrelay.relay.as_str())
case NOSTR_BECH32_NADDR:
let naddr = b.data.naddr
guard let identifier = String(naddr.identifier) else {
return nil
}
let pubkey = Pubkey(Data(bytes: naddr.pubkey, count: 32))
let kind = naddr.kind
return .naddr(NAddr(identifier: identifier, author: pubkey, relays: getRelayStrings(from: naddr.relays), kind: kind))
let pubkey = Pubkey(Data(bytes: b.naddr.pubkey, count: 32))
let kind = b.naddr.kind
let identifier = b.naddr.identifier.as_str()
return .naddr(NAddr(identifier: identifier, author: pubkey, relays: b.naddr.relays.as_urls(), kind: kind))
default:
return nil
}
}
private func getRelayStrings(from relays: relays) -> [String] {
var result = [String]()
let numRelays = Int(relays.num_relays)
func processRelay(_ relay: str_block) {
if let string = String(relay) {
result.append(string)
}
}
// Since relays is a C tuple, the indexes can't be iterated through so they need to be manually processed
if numRelays > 0 { processRelay(relays.relays.0) }
if numRelays > 1 { processRelay(relays.relays.1) }
if numRelays > 2 { processRelay(relays.relays.2) }
if numRelays > 3 { processRelay(relays.relays.3) }
if numRelays > 4 { processRelay(relays.relays.4) }
if numRelays > 5 { processRelay(relays.relays.5) }
if numRelays > 6 { processRelay(relays.relays.6) }
if numRelays > 7 { processRelay(relays.relays.7) }
if numRelays > 8 { processRelay(relays.relays.8) }
if numRelays > 9 { processRelay(relays.relays.9) }
return result
}
private enum TLVType: UInt8 {
case SPECIAL
case RELAY
@@ -225,9 +238,9 @@ private func writeBytesList(bytesList: inout [UInt8], tlvType: TLVType, data: [U
bytesList.append(contentsOf: data.bytes)
}
private func writeBytesRelays(bytesList: inout [UInt8], relays: [String]) {
for relay in relays where !relay.isEmpty {
guard let relayData = relay.data(using: .utf8) else {
private func writeBytesRelays(bytesList: inout [UInt8], relays: [RelayURL]) {
for relay in relays {
guard let relayData = relay.url.absoluteString.data(using: .utf8) else {
continue // skip relay if can't read data
}
writeBytesList(bytesList: &bytesList, tlvType: .RELAY, data: relayData.bytes)
+3 -3
View File
@@ -376,7 +376,7 @@ func preload_event(plan: PreloadPlan, state: DamusState) async {
//print("Preloading event \(plan.event.content)")
if artifacts == nil && plan.load_artifacts {
let arts = render_note_content(ev: plan.event, profiles: profiles, keypair: our_keypair)
let arts = render_note_content(ndb: state.ndb, ev: plan.event, profiles: profiles, keypair: our_keypair)
artifacts = arts
// we need these asap
@@ -397,7 +397,7 @@ func preload_event(plan: PreloadPlan, state: DamusState) async {
}
if plan.load_preview, note_artifact_is_separated(kind: plan.event.known_kind) {
let arts = artifacts ?? render_note_content(ev: plan.event, profiles: profiles, keypair: our_keypair)
let arts = artifacts ?? render_note_content(ndb: state.ndb, ev: plan.event, profiles: profiles, keypair: our_keypair)
// only separated artifacts have previews
if case .separated(let sep) = arts {
@@ -412,7 +412,7 @@ func preload_event(plan: PreloadPlan, state: DamusState) async {
}
}
let note_language = plan.data.translations_model.note_language ?? plan.event.note_language(our_keypair) ?? current_language()
let note_language = plan.data.translations_model.note_language ?? plan.event.note_language(ndb: state.ndb, our_keypair) ?? current_language()
var translations: TranslateStatus? = nil
// We have to recheck should_translate here now that we have note_language
@@ -25,7 +25,7 @@ struct LossyLocalNotification {
return self.from(json_encoded_ndb_note: encoded_ndb_note)
}
guard let id = user_info["id"] as? String,
let target_id = MentionRef.from_bech32(str: id) else {
let target_id = MentionRef(bech32_str: id) else {
return nil
}
let typestr = user_info["type"] as! String
@@ -42,8 +42,11 @@ struct LossyLocalNotification {
}
static func from(ndb_note: NdbNote) -> LossyLocalNotification? {
guard let known_kind = ndb_note.known_kind, let type = LocalNotificationType.from(nostr_kind: known_kind) else { return nil }
let target: MentionRef = .note(ndb_note.id)
guard let known_kind = ndb_note.known_kind,
let type = LocalNotificationType.from(nostr_kind: known_kind) else {
return nil
}
let target: MentionRef = .init(nip19: .note(ndb_note.id))
return LossyLocalNotification(type: type, mention: target)
}
}
@@ -69,7 +72,7 @@ struct LocalNotification {
let content: String
func to_lossy() -> LossyLocalNotification {
return LossyLocalNotification(type: self.type, mention: .note(self.target.id))
return LossyLocalNotification(type: self.type, mention: .init(nip19: .note(self.target.id)))
}
}
+1 -1
View File
@@ -54,7 +54,7 @@ let test_repost_2 = NostrEvent(content: test_encoded_post, keypair: test_keypair
let test_reposts = [test_repost_1, test_repost_2]
let test_event_group = EventGroup(events: test_reposts)
let test_zap_invoice = ZapInvoice(description: .description("description"), amount: 10000, string: "lnbc1", expiry: 1000000, payment_hash: Data(), created_at: 1000000)
let test_zap_invoice = ZapInvoice(description: .description("description"), amount: 10000, string: "lnbc1", expiry: 1000000, created_at: 1000000)
let test_zap_request_ev = NostrEvent(content: "hi", keypair: test_keypair, kind: 9734)!
let test_zap_request = ZapRequest(ev: test_zap_request_ev)
let test_zap = Zap(event: test_note, invoice: test_zap_invoice, zapper: test_note.pubkey, target: .profile(test_pubkey), raw_request: test_zap_request, is_anon: false, private_request: nil)
+18 -2
View File
@@ -206,13 +206,28 @@ class Ndb {
self.ndb = db
return true
}
func lookup_blocks_by_key_with_txn<Y>(_ key: NoteKey, txn: NdbTxn<Y>) -> NdbBlocks? {
guard let blocks = ndb_get_blocks_by_key(self.ndb.ndb, &txn.txn, key) else {
return nil
}
return NdbBlocks(ptr: blocks)
}
func lookup_blocks_by_key(_ key: NoteKey) -> NdbTxn<NdbBlocks?>? {
NdbTxn(ndb: self) { txn in
lookup_blocks_by_key_with_txn(key, txn: txn)
}
}
func lookup_note_by_key_with_txn<Y>(_ key: NoteKey, txn: NdbTxn<Y>) -> NdbNote? {
var size: Int = 0
guard let note_p = ndb_get_note_by_key(&txn.txn, key, &size) else {
return nil
}
return NdbNote(note: note_p, size: size, owned: false, key: key)
let ptr = ndb_note_ptr(ptr: note_p)
return NdbNote(note: ptr, size: size, owned: false, key: key)
}
func text_search(query: String, limit: Int = 128, order: NdbSearchOrder = .newest_first) -> [NoteKey] {
@@ -403,7 +418,8 @@ class Ndb {
let note_p = ndb_get_note_by_id(&txn.txn, baseAddress, &size, &key) else {
return nil
}
return NdbNote(note: note_p, size: size, owned: false, key: key)
let ptr = ndb_note_ptr(ptr: note_p)
return NdbNote(note: ptr, size: size, owned: false, key: key)
}
}
+124
View File
@@ -0,0 +1,124 @@
//
// NdbBlock.swift
// damus
//
// Created by William Casarin on 2024-01-25.
//
import Foundation
enum NdbBlockType: UInt32 {
case hashtag = 1
case text = 2
case mention_index = 3
case mention_bech32 = 4
case url = 5
case invoice = 6
}
extension ndb_mention_bech32_block {
var bech32_type: NdbBech32Type? {
NdbBech32Type(rawValue: self.bech32.type.rawValue)
}
}
enum NdbBech32Type: UInt32 {
case note = 1
case npub = 2
case nprofile = 3
case nevent = 4
case nrelay = 5
case naddr = 6
case nsec = 7
var is_notelike: Bool {
return self == .note || self == .nevent
}
}
extension ndb_invoice_block {
func as_invoice() -> Invoice? {
let b11 = self.invoice
let invstr = self.invstr.as_str()
guard let description = convert_invoice_description(b11: b11) else {
return nil
}
let amount: Amount = b11.amount == 0 ? .any : .specific(Int64(b11.amount))
return Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, created_at: b11.timestamp)
}
}
enum NdbBlock {
case text(ndb_str_block)
case mention(ndb_mention_bech32_block)
case hashtag(ndb_str_block)
case url(ndb_str_block)
case invoice(ndb_invoice_block)
case mention_index(UInt32)
init?(_ ptr: ndb_block_ptr) {
guard let type = NdbBlockType(rawValue: ndb_get_block_type(ptr.ptr).rawValue) else {
return nil
}
switch type {
case .hashtag: self = .hashtag(ptr.block.str)
case .text: self = .text(ptr.block.str)
case .invoice: self = .invoice(ptr.block.invoice)
case .url: self = .url(ptr.block.str)
case .mention_bech32: self = .mention(ptr.block.mention_bech32)
case .mention_index: self = .mention_index(ptr.block.mention_index)
}
}
var is_previewable: Bool {
switch self {
case .mention(let m):
switch m.bech32_type {
case .note, .nevent: return true
default: return false
}
case .invoice:
return true
case .url:
return true
default:
return false
}
}
static func convertToStringCopy(from block: str_block_t) -> String? {
guard let cString = block.str else {
return nil
}
let byteBuffer = UnsafeBufferPointer(start: cString, count: Int(block.len)).map { UInt8(bitPattern: $0) }
// Create a Swift String from the byte array
return String(bytes: byteBuffer, encoding: .utf8)
}
}
struct NdbBlocks {
private let blocks_ptr: ndb_blocks_ptr
init(ptr: OpaquePointer?) {
self.blocks_ptr = ndb_blocks_ptr(ptr: ptr)
}
var words: Int {
Int(ndb_blocks_word_count(blocks_ptr.ptr))
}
func iter(note: NdbNote) -> BlocksSequence {
BlocksSequence(note: note, blocks: self)
}
func as_ptr() -> OpaquePointer? {
return self.blocks_ptr.ptr
}
}
+59
View File
@@ -0,0 +1,59 @@
//
// NdbBlockIterator.swift
// damus
//
// Created by William Casarin on 2024-01-25.
//
import Foundation
struct BlocksIterator: IteratorProtocol {
typealias Element = NdbBlock
var done: Bool
var iter: ndb_block_iterator
var note: NdbNote
mutating func next() -> NdbBlock? {
guard iter.blocks != nil,
let ptr = ndb_blocks_iterate_next(&iter) else {
done = true
return nil
}
let block_ptr = ndb_block_ptr(ptr: ptr)
return NdbBlock(block_ptr)
}
init(note: NdbNote, blocks: NdbBlocks) {
let content = ndb_note_content(note.note.ptr)
self.iter = ndb_block_iterator(content: content, blocks: nil, block: ndb_block(), p: nil)
ndb_blocks_iterate_start(content, blocks.as_ptr(), &self.iter)
self.done = false
self.note = note
}
}
struct BlocksSequence: Sequence {
let blocks: NdbBlocks
let note: NdbNote
init(note: NdbNote, blocks: NdbBlocks) {
self.blocks = blocks
self.note = note
}
func makeIterator() -> BlocksIterator {
return .init(note: note, blocks: blocks)
}
func collect() -> [NdbBlock] {
var xs = [NdbBlock]()
for x in self {
xs.append(x)
}
return xs
}
}
+67 -52
View File
@@ -47,7 +47,7 @@ class NdbNote: Codable, Equatable, Hashable {
private(set) var owned: Bool
let count: Int
let key: NoteKey?
let note: UnsafeMutablePointer<ndb_note>
let note: ndb_note_ptr
// cached stuff (TODO: remove these)
var decrypted_content: String? = nil
@@ -58,7 +58,7 @@ class NdbNote: Codable, Equatable, Hashable {
}
}
init(note: UnsafeMutablePointer<ndb_note>, size: Int, owned: Bool, key: NoteKey?) {
init(note: ndb_note_ptr, size: Int, owned: Bool, key: NoteKey?) {
self.note = note
self.owned = owned
self.count = size
@@ -80,9 +80,9 @@ class NdbNote: Codable, Equatable, Hashable {
}
let buf = malloc(self.count)!
memcpy(buf, &self.note.pointee, self.count)
let new_note = buf.assumingMemoryBound(to: ndb_note.self)
memcpy(buf, UnsafeRawPointer(self.note.ptr), self.count)
let new_note = ndb_note_ptr(ptr: OpaquePointer(buf))
return NdbNote(note: new_note, size: self.count, owned: true, key: self.key)
}
@@ -95,16 +95,16 @@ class NdbNote: Codable, Equatable, Hashable {
}
var content_raw: UnsafePointer<CChar> {
ndb_note_content(note)
ndb_note_content(note.ptr)
}
var content_len: UInt32 {
ndb_note_content_length(note)
ndb_note_content_length(note.ptr)
}
/// NDBTODO: make this into data
var id: NoteId {
.init(Data(bytes: ndb_note_id(note), count: 32))
.init(Data(bytes: ndb_note_id(note.ptr), count: 32))
}
var raw_note_id: UnsafeMutablePointer<UInt8> {
@@ -116,20 +116,20 @@ class NdbNote: Codable, Equatable, Hashable {
}
var sig: Signature {
.init(Data(bytes: ndb_note_sig(note), count: 64))
.init(Data(bytes: ndb_note_sig(note.ptr), count: 64))
}
/// NDBTODO: make this into data
var pubkey: Pubkey {
.init(Data(bytes: ndb_note_pubkey(note), count: 32))
.init(Data(bytes: ndb_note_pubkey(note.ptr), count: 32))
}
var created_at: UInt32 {
ndb_note_created_at(note)
ndb_note_created_at(note.ptr)
}
var kind: UInt32 {
ndb_note_kind(note)
ndb_note_kind(note.ptr)
}
var tags: TagsSequence {
@@ -144,7 +144,7 @@ class NdbNote: Codable, Equatable, Hashable {
print("\(NdbNote.notes_created) ndb_notes, \(NdbNote.total_ndb_size) bytes")
#endif
free(note)
free(UnsafeMutableRawPointer(note.ptr))
}
}
@@ -234,7 +234,7 @@ class NdbNote: Codable, Equatable, Hashable {
let buflen = MAX_NOTE_SIZE
let buf = malloc(buflen)
ndb_builder_init(&builder, buf, Int32(buflen))
ndb_builder_init(&builder, buf, buflen)
var pk_raw = noteConstructionMaterial.pubkey.bytes
@@ -262,9 +262,27 @@ class NdbNote: Codable, Equatable, Hashable {
return nil
}
var n = UnsafeMutablePointer<ndb_note>?(nil)
var n = ndb_note_ptr()
var the_kp: ndb_keypair? = nil
if let sec = keypair.privkey {
var kp = ndb_keypair()
memcpy(&kp.secret.0, sec.id.bytes, 32);
if ndb_create_keypair(&kp) <= 0 {
print("bad keypair")
} else {
the_kp = kp
}
}
var len: Int32 = 0
if var the_kp {
len = ndb_builder_finalize(&builder, &n.ptr, &the_kp)
} else {
len = ndb_builder_finalize(&builder, &n.ptr, nil)
}
switch noteConstructionMaterial {
case .keypair(let keypair):
@@ -328,7 +346,7 @@ class NdbNote: Codable, Equatable, Hashable {
return nil
}
self.note = r.assumingMemoryBound(to: ndb_note.self)
self.note = ndb_note_ptr(ptr: OpaquePointer(r))
self.key = nil
}
@@ -352,9 +370,9 @@ class NdbNote: Codable, Equatable, Hashable {
//guard var json_cstr = json.cString(using: .utf8) else { return nil }
//json_cs
var note: UnsafeMutablePointer<ndb_note>?
let len = ndb_note_from_json(json, Int32(json_len), &note, data, Int32(bufsize))
var note = ndb_note_ptr()
let len = ndb_note_from_json(json, Int32(json_len), &note.ptr, data, Int32(bufsize))
if len == 0 {
free(data)
@@ -362,10 +380,9 @@ class NdbNote: Codable, Equatable, Hashable {
}
// Create new Data with just the valid bytes
guard let note_data = realloc(data, Int(len)) else { return nil }
let new_note = note_data.assumingMemoryBound(to: ndb_note.self)
return NdbNote(note: new_note, size: Int(len), owned: true, key: nil)
guard let new_note = realloc(data, Int(len)) else { return nil }
let new_note_ptr = ndb_note_ptr(ptr: OpaquePointer(new_note))
return NdbNote(note: new_note_ptr, size: Int(len), owned: true, key: nil)
}
func get_inner_event() -> NdbNote? {
@@ -397,7 +414,7 @@ extension NdbNote {
var should_show_event: Bool {
return !too_big
}
func is_hellthread(max_pubkeys: Int) -> Bool {
switch known_kind {
case .text, .boost, .like, .zap:
@@ -407,10 +424,6 @@ extension NdbNote {
}
}
func get_blocks(keypair: Keypair) -> Blocks {
return parse_note_content(content: .init(note: self, keypair: keypair))
}
// TODO: References iterator
public var referenced_ids: References<NoteId> {
References<NoteId>(tags: self.tags)
@@ -451,7 +464,7 @@ extension NdbNote {
public var references: References<RefId> {
References<RefId>(tags: self.tags)
}
func thread_reply() -> ThreadReply? {
if self.known_kind != .highlight {
return ThreadReply(tags: self.tags)
@@ -463,6 +476,21 @@ extension NdbNote {
return ThreadReply(tags: self.tags)?.reply.note_id
}
func blocks(ndb: Ndb) -> NdbTxn<NdbBlocks>? {
let blocks_txn = NdbTxn<NdbBlocks?>(ndb: ndb) { txn in
guard let key = ndb.lookup_note_key_with_txn(self.id, txn: txn) else {
return nil
}
return ndb.lookup_blocks_by_key_with_txn(key, txn: txn)
}
guard let blocks_txn else {
return nil
}
return blocks_txn.collect()
}
func get_content(_ keypair: Keypair) -> String {
if known_kind == .dm {
return decrypted(keypair: keypair) ?? "*failed to decrypt content*"
@@ -482,10 +510,6 @@ extension NdbNote {
return content
}
func blocks(_ keypair: Keypair) -> Blocks {
return get_blocks(keypair: keypair)
}
// NDBTODO: switch this to operating on bytes not strings
func decrypted(keypair: Keypair) -> String? {
if let decrypted_content {
@@ -526,34 +550,22 @@ extension NdbNote {
return self.referenced_ids.last
}
// NDBTODO: id -> data
/*
public func references(id: String, key: AsciiCharacter) -> Bool {
var matcher: (Reference) -> Bool = { ref in ref.ref_id.matches_str(id) }
if id.count == 64, let decoded = hex_decode(id) {
matcher = { ref in ref.ref_id.matches_id(decoded) }
}
for ref in References(tags: self.tags) {
if ref.key == key && matcher(ref) {
return true
}
}
return false
}
*/
func is_reply() -> Bool {
return thread_reply() != nil
}
func note_language(_ keypair: Keypair) -> String? {
func note_language(ndb: Ndb, _ keypair: Keypair) -> String? {
assert(!Thread.isMainThread, "This function must not be run on the main thread.")
// Rely on Apple's NLLanguageRecognizer to tell us which language it thinks the note is in
// and filter on only the text portions of the content as URLs and hashtags confuse the language recognizer.
let originalBlocks = self.blocks(keypair).blocks
let originalOnlyText = originalBlocks.compactMap {
/*
guard let blocks_txn = self.blocks(ndb: ndb) else {
return nil
}
let blocks = blocks_txn.unsafeUnownedValue
let originalOnlyText = blocks.blocks(note: self).compactMap {
if case .text(let txt) = $0 {
// Replacing right single quotation marks () with "typewriter or ASCII apostrophes" (')
// as a workaround to get Apple's language recognizer to predict language the correctly.
@@ -580,6 +592,9 @@ extension NdbNote {
}
}
.joined(separator: " ")
*/
let originalOnlyText = self.get_content(keypair)
// If there is no text, there's nothing to use to detect language.
guard !originalOnlyText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
+4 -4
View File
@@ -25,7 +25,7 @@ struct NdbStrIter: IteratorProtocol {
}
init(tag: NdbTagElem) {
self.str = ndb_tag_str(tag.note.note, tag.tag, tag.index)
self.str = ndb_tag_str(tag.note.note.ptr, tag.tag.ptr, tag.index)
self.ind = 0
self.tag = tag
}
@@ -33,7 +33,7 @@ struct NdbStrIter: IteratorProtocol {
struct NdbTagElem: Sequence, Hashable, Equatable {
let note: NdbNote
let tag: UnsafeMutablePointer<ndb_tag>
let tag: ndb_tag_ptr
let index: Int32
let str: ndb_str
@@ -59,11 +59,11 @@ struct NdbTagElem: Sequence, Hashable, Equatable {
return memcmp(lhs.str.str, rhs.str.str, r) == 0
}
init(note: NdbNote, tag: UnsafeMutablePointer<ndb_tag>, index: Int32) {
init(note: NdbNote, tag: ndb_tag_ptr, index: Int32) {
self.note = note
self.tag = tag
self.index = index
self.str = ndb_tag_str(note.note, tag, index)
self.str = ndb_tag_str(note.note.ptr, tag.ptr, index)
}
var is_id: Bool {
+6 -6
View File
@@ -21,10 +21,10 @@ import Foundation
/// ```
struct TagSequence: Sequence {
let note: NdbNote
let tag: UnsafeMutablePointer<ndb_tag>
let tag: ndb_tag_ptr
var count: UInt16 {
tag.pointee.count
ndb_tag_count(tag.ptr)
}
func strings() -> [String] {
@@ -46,7 +46,7 @@ struct TagIterator: IteratorProtocol {
typealias Element = NdbTagElem
mutating func next() -> NdbTagElem? {
guard index < tag.pointee.count else { return nil }
guard index < ndb_tag_count(tag.ptr) else { return nil }
let el = NdbTagElem(note: note, tag: tag, index: index)
index += 1
@@ -56,13 +56,13 @@ struct TagIterator: IteratorProtocol {
var index: Int32
let note: NdbNote
var tag: UnsafeMutablePointer<ndb_tag>
var tag: ndb_tag_ptr
var count: UInt16 {
tag.pointee.count
ndb_tag_count(tag.ptr)
}
init(note: NdbNote, tag: UnsafeMutablePointer<ndb_tag>) {
init(note: NdbNote, tag: ndb_tag_ptr) {
self.note = note
self.tag = tag
self.index = 0
+7 -8
View File
@@ -20,16 +20,13 @@ struct TagsIterator: IteratorProtocol {
return nil
}
return TagSequence(note: note, tag: self.iter.tag)
}
var count: UInt16 {
return note.note.pointee.tags.count
let tag_ptr = ndb_tag_ptr(ptr: self.iter.tag)
return TagSequence(note: note, tag: tag_ptr)
}
init(note: NdbNote) {
self.iter = ndb_iterator()
ndb_tags_iterate_start(note.note, &self.iter)
ndb_tags_iterate_start(note.note.ptr, &self.iter)
self.done = false
self.note = note
}
@@ -39,7 +36,8 @@ struct TagsSequence: Encodable, Sequence {
let note: NdbNote
var count: UInt16 {
note.note.pointee.tags.count
let tags_ptr = ndb_note_tags(note.note.ptr)
return ndb_tags_count(tags_ptr)
}
func strings() -> [[String]] {
@@ -70,7 +68,8 @@ struct TagsSequence: Encodable, Sequence {
}
precondition(false, "sequence subscript oob")
// it seems like the compiler needs this or it gets bitchy
return .init(note: .init(note: .allocate(capacity: 1), size: 0, owned: true, key: nil), tag: .allocate(capacity: 1))
let nil_ptr = OpaquePointer(bitPattern: 0)
return .init(note: .init(note: .init(ptr: nil_ptr), size: 0, owned: true, key: nil), tag: .init(ptr: nil_ptr))
}
func makeIterator() -> TagsIterator {
+4
View File
@@ -21,6 +21,10 @@ class NdbTxn<T> {
var generation: Int
var name: String
static func pure(ndb: Ndb, val: T) -> NdbTxn<T> {
.init(ndb: ndb, txn: ndb_txn(), val: val, generation: ndb.generation, inherited: true, name: "pure_txn")
}
init?(ndb: Ndb, with: (NdbTxn<T>) -> T = { _ in () }, name: String? = nil) {
guard !ndb.is_closed else { return nil }
self.name = name ?? "txn"
@@ -5,9 +5,9 @@
/* Common FlatBuffers build functionality for C. */
#include "flatcc/flatcc_prologue.h"
#include "flatcc_prologue.h"
#ifndef FLATBUILDER_H
#include "flatcc/flatcc_builder.h"
#include "flatcc_builder.h"
#endif
typedef flatcc_builder_t flatbuffers_builder_t;
typedef flatcc_builder_ref_t flatbuffers_ref_t;
@@ -681,5 +681,5 @@ __flatbuffers_build_scalar(flatbuffers_, flatbuffers_double, double)
__flatbuffers_build_string(flatbuffers_)
__flatbuffers_build_buffer(flatbuffers_)
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* FLATBUFFERS_COMMON_BUILDER_H */
@@ -5,8 +5,8 @@
/* Common FlatBuffers read functionality for C. */
#include "flatcc/flatcc_prologue.h"
#include "flatcc/flatcc_flatbuffers.h"
#include "flatcc_prologue.h"
#include "flatcc_flatbuffers.h"
#define __flatbuffers_read_scalar_at_byteoffset(N, p, o) N ## _read_from_pe((uint8_t *)(p) + (o))
@@ -574,5 +574,5 @@ static inline N ## _ ## K ## t N ## _as_typed_root(const void *buffer__tmp)\
#define __flatbuffers_struct_as_root(N) __flatbuffers_buffer_as_root(N, struct_)
#define __flatbuffers_table_as_root(N) __flatbuffers_buffer_as_root(N, table_)
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* FLATBUFFERS_COMMON_H */
+2 -2
View File
@@ -9,7 +9,7 @@
#ifndef FLATBUFFERS_COMMON_BUILDER_H
#include "flatbuffers_common_builder.h"
#endif
#include "flatcc/flatcc_prologue.h"
#include "flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -65,5 +65,5 @@ static NdbEventMeta_ref_t NdbEventMeta_clone(flatbuffers_builder_t *B, NdbEventM
__flatbuffers_memoize_end(B, t, NdbEventMeta_end(B));
}
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* META_BUILDER_H */
+3 -3
View File
@@ -6,11 +6,11 @@
#ifndef FLATBUFFERS_COMMON_READER_H
#include "flatbuffers_common_reader.h"
#endif
#include "flatcc/flatcc_flatbuffers.h"
#include "flatcc_flatbuffers.h"
#ifndef __alignas_is_defined
#include <stdalign.h>
#endif
#include "flatcc/flatcc_prologue.h"
#include "flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -54,5 +54,5 @@ __flatbuffers_define_scalar_field(4, NdbEventMeta, zaps, flatbuffers_int32, int3
__flatbuffers_define_scalar_field(5, NdbEventMeta, zap_total, flatbuffers_int64, int64_t, INT64_C(0))
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* META_READER_H */
+2 -2
View File
@@ -9,7 +9,7 @@
#ifndef FLATBUFFERS_COMMON_BUILDER_H
#include "flatbuffers_common_builder.h"
#endif
#include "flatcc/flatcc_prologue.h"
#include "flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -127,5 +127,5 @@ static NdbProfileRecord_ref_t NdbProfileRecord_clone(flatbuffers_builder_t *B, N
__flatbuffers_memoize_end(B, t, NdbProfileRecord_end(B));
}
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* PROFILE_BUILDER_H */
+3 -3
View File
@@ -3,8 +3,8 @@
/* Generated by flatcc 0.6.1 FlatBuffers schema compiler for C by dvide.com */
#include "flatcc/flatcc_json_parser.h"
#include "flatcc/flatcc_prologue.h"
#include "flatcc_json_parser.h"
#include "flatcc_prologue.h"
/*
* Parses the default root table or struct of the schema and constructs a FlatBuffer.
@@ -408,5 +408,5 @@ static int profile_parse_json(flatcc_builder_t *B, flatcc_json_parser_t *ctx,
return 0;
}
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* PROFILE_JSON_PARSER_H */
+3 -3
View File
@@ -6,11 +6,11 @@
#ifndef FLATBUFFERS_COMMON_READER_H
#include "flatbuffers_common_reader.h"
#endif
#include "flatcc/flatcc_flatbuffers.h"
#include "flatcc_flatbuffers.h"
#ifndef __alignas_is_defined
#include <stdalign.h>
#endif
#include "flatcc/flatcc_prologue.h"
#include "flatcc_prologue.h"
#ifndef flatbuffers_identifier
#define flatbuffers_identifier 0
#endif
@@ -89,5 +89,5 @@ __flatbuffers_define_scalar_field(2, NdbProfileRecord, note_key, flatbuffers_uin
__flatbuffers_define_string_field(3, NdbProfileRecord, lnurl, 0)
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* PROFILE_READER_H */
+3 -3
View File
@@ -6,8 +6,8 @@
#ifndef PROFILE_READER_H
#include "profile_reader.h"
#endif
#include "flatcc/flatcc_verifier.h"
#include "flatcc/flatcc_prologue.h"
#include "flatcc_verifier.h"
#include "flatcc_prologue.h"
static int NdbProfile_verify_table(flatcc_table_verifier_descriptor_t *td);
static int NdbProfileRecord_verify_table(flatcc_table_verifier_descriptor_t *td);
@@ -80,5 +80,5 @@ static inline int NdbProfileRecord_verify_as_root_with_type_hash(const void *buf
return flatcc_verify_table_as_typed_root(buf, bufsiz, thash, &NdbProfileRecord_verify_table);
}
#include "flatcc/flatcc_epilogue.h"
#include "flatcc_epilogue.h"
#endif /* PROFILE_VERIFIER_H */
-2
View File
@@ -2,8 +2,6 @@
// swiftlint:disable all
// swiftformat:disable all
import FlatBuffers
public struct NdbEventMeta: FlatBufferObject, Verifiable {
static func validateVersion() { FlatBuffersVersion_23_5_26() }
@@ -2,8 +2,6 @@
// swiftlint:disable all
// swiftformat:disable all
import FlatBuffers
public struct NdbProfile: FlatBufferObject, Verifiable {
static func validateVersion() { FlatBuffersVersion_23_5_26() }
+2 -1
View File
@@ -133,7 +133,8 @@ static struct bolt11 *decode_fail(struct bolt11 *b11, char **fail,
va_list ap;
va_start(ap, fmt);
*fail = tal_vfmt(tal_parent(b11), fmt, ap);
if (fail)
*fail = tal_vfmt(tal_parent(b11), fmt, ap);
va_end(ap);
return tal_free(b11);
}
+4 -4
View File
@@ -5,8 +5,8 @@
// Created by William Casarin on 2023-04-09.
//
#include "nostr_bech32.h"
#include <stdlib.h>
#include "nostr_bech32.h"
#include "cursor.h"
#include "str_block.h"
#include "ccan/endian/endian.h"
@@ -151,12 +151,12 @@ static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *n
static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *naddr) {
struct nostr_tlv tlv;
int i;
int i, has_kind;
has_kind = 0;
naddr->identifier.str = NULL;
naddr->identifier.len = 0;
naddr->pubkey = NULL;
naddr->has_kind = 0;
naddr->relays.num_relays = 0;
for (i = 0; i < MAX_TLVS; i++) {
@@ -183,7 +183,7 @@ static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *nad
}
}
return naddr->identifier.str != NULL;
return naddr->identifier.str != NULL && has_kind;
}
static int parse_nostr_bech32_nprofile(struct cursor *cur, struct bech32_nprofile *nprofile) {
-1
View File
@@ -384,7 +384,6 @@ struct bech32_naddr {
struct ndb_str_block identifier;
const unsigned char *pubkey;
uint32_t kind;
int has_kind;
};
struct bech32_nrelay {