Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
685eb3e277
|
@@ -1,4 +1,4 @@
|
|||||||
use nix
|
#use nix
|
||||||
|
|
||||||
export TODO_FILE=$PWD/TODO
|
export TODO_FILE=$PWD/TODO
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "master"
|
- "master"
|
||||||
- "ci"
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- "*"
|
- "*"
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ xcuserdata
|
|||||||
damus/TestingPrivate.swift
|
damus/TestingPrivate.swift
|
||||||
.DS_Store
|
.DS_Store
|
||||||
TODO.bak
|
TODO.bak
|
||||||
tags
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
Terry Yiu <git@tyiu.xyz> <963907+tyiu@users.noreply.github.com>
|
|
||||||
Ben Weeks <ben.weeks@knowall.ai> <ben.weeks@outlook.com>
|
|
||||||
Suhail Saqan <suhail.saqan@gmail.com> <43693074+suhailsaqan@users.noreply.github.com>
|
|
||||||
cr0bar <cr0bar@cr0.bar> <cr0bar@users.noreply.github.com>
|
|
||||||
Swift <scoder1747@gmail.com> <120697811+scoder1747@users.noreply.github.com>
|
|
||||||
@@ -1,99 +1,3 @@
|
|||||||
## [1.6-4] - 2023-07-13
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Add the ability to follow hashtags (William Casarin)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Remove note size restriction for longform events (William Casarin)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Hide users and hashtags from home timeline when you unfollow (William Casarin)
|
|
||||||
- Fixed a bug where following a user might not work due to poor connectivity (William Casarin)
|
|
||||||
- Icon color for developer mode setting is incorrect in low-light mode (Bryan Montz)
|
|
||||||
- Fixed nav bar color on login, eula, and account creation (ericholguin)
|
|
||||||
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Remove following Damus Will by default (William Casarin)
|
|
||||||
|
|
||||||
[1.6-4]: https://github.com/damus-io/damus/releases/tag/v1.6-4
|
|
||||||
|
|
||||||
## [1.6-3] - 2023-07-11
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Start at top when reading longform events (William Casarin)
|
|
||||||
- Allow reposting and quote reposting multiple times (William Casarin)
|
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Show longform previews in notifications instead of the entire post (William Casarin)
|
|
||||||
- Fix padding on longform events (William Casarin)
|
|
||||||
- Fix action bar appearing on quoted longform previews (William Casarin)
|
|
||||||
|
|
||||||
|
|
||||||
[1.6-3]: https://github.com/damus-io/damus/releases/tag/v1.6-3
|
|
||||||
## [1.6-2] - 2023-07-11
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Add support for multilingual hashtags (cr0bar)
|
|
||||||
- Add r tag when mentioning a url (William Casarin)
|
|
||||||
- Add initial longform note support (William Casarin)
|
|
||||||
- Enable banner image editing (Joel Klabo)
|
|
||||||
- Add relay log in developer mode (Bryan Montz)
|
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix lag when creating large posts (William Casarin)
|
|
||||||
- Fix npub mentions failing to parse in some cases (William Casarin)
|
|
||||||
- Fix PostView initial string to skip mentioning self when on own profile (Terry Yiu)
|
|
||||||
- Fix freezing bug when tapping Developer settings menu (Terry Yiu)
|
|
||||||
- Fix potential fake profile zap attacks (William Casarin)
|
|
||||||
- Fix issue where malicious zappers can send fake zaps to another user's posts (William Casarin)
|
|
||||||
- Fix profile post button mentions (cr0bar)
|
|
||||||
- Fix icons on settings view (cr0bar)
|
|
||||||
- Fix Invalid Zap bug in reposts (William Casarin)
|
|
||||||
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Remove old @ and & hex key mentions (William Casarin)
|
|
||||||
|
|
||||||
|
|
||||||
[1.6-2]: https://github.com/damus-io/damus/releases/tag/v1.6-2
|
|
||||||
## [1.6] - 2023-07-04
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Speed up user search (Terry Yiu)
|
|
||||||
- Add post button to profile pages (William Casarin)
|
|
||||||
- Add post button when logged in with private key and on own profile view (Terry Yiu)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Drop iOS15 support (Scott Penrose)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Load more content on profile view (William Casarin)
|
|
||||||
- Fix reports to conform to NIP-56 (Terry Yiu)
|
|
||||||
- Fix profile navigation bugs from muted users list and relay list views (Terry Yiu)
|
|
||||||
- Fix navigation to translation settings view (Terry Yiu)
|
|
||||||
- Fixed all navigation issues (Scott Penrose)
|
|
||||||
- Disable post button when media upload in progress (Terry Yiu)
|
|
||||||
- Fix taps on mentions in note drafts to not redirect to other Nostr clients (Terry Yiu)
|
|
||||||
- Fix missing profile zap notification text (Terry Yiu)
|
|
||||||
|
|
||||||
|
|
||||||
[1.6]: https://github.com/damus-io/damus/releases/tag/v1.6
|
|
||||||
|
|
||||||
## [1.5-5] - 2023-06-24
|
## [1.5-5] - 2023-06-24
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
all: nostrscript/primal.wasm
|
|
||||||
|
|
||||||
nostrscript/%.wasm: nostrscript/%.ts nostrscript/nostr.ts Makefile
|
|
||||||
asc $< --runtime stub --outFile $@ --optimize
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm nostrscript/*.wasm
|
|
||||||
@@ -94,23 +94,12 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
|||||||
|
|
||||||
Contributors welcome! Start by examining known issues: https://github.com/damus-io/damus/issues.
|
Contributors welcome! Start by examining known issues: https://github.com/damus-io/damus/issues.
|
||||||
|
|
||||||
### Mailing lists
|
### Code
|
||||||
|
|
||||||
We have a few mailing lists that anyone can join to get involved in damus development:
|
[Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on GitHub as well. 50,000 sats will be rewarded for patches sent via email or [nostr][patchstr]
|
||||||
|
|
||||||
- [dev][dev-list] - development discussions
|
[git-send-email]: http://git-send-email.io
|
||||||
- [patches][patches-list] - code submission and review
|
[patchstr]: https://patchstr.com
|
||||||
- [product][product-list] - product discussions
|
|
||||||
- [design][design-list] - design discussions
|
|
||||||
|
|
||||||
[dev-list]: https://damus.io/list/dev
|
|
||||||
[patches-list]: https://damus.io/list/patches
|
|
||||||
[product-list]: https://damus.io/list/product
|
|
||||||
[design-list]: https://damus.io/list/design
|
|
||||||
|
|
||||||
### Contributing
|
|
||||||
|
|
||||||
See [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md)
|
|
||||||
|
|
||||||
### Privacy
|
### Privacy
|
||||||
Your internet protocol (IP) address is exposed to the relays you connect to, and third party media hosters (e.g. nostr.build, imgur.com, giphy.com, youtube.com etc.) that render on Damus. If you want to improve your privacy, consider utilizing a service that masks your IP address (e.g. a VPN) from trackers online.
|
Your internet protocol (IP) address is exposed to the relays you connect to, and third party media hosters (e.g. nostr.build, imgur.com, giphy.com, youtube.com etc.) that render on Damus. If you want to improve your privacy, consider utilizing a service that masks your IP address (e.g. a VPN) from trackers online.
|
||||||
|
|||||||
+5
-6
@@ -35,7 +35,7 @@ typedef struct mention_bech32_block {
|
|||||||
struct nostr_bech32 bech32;
|
struct nostr_bech32 bech32;
|
||||||
} mention_bech32_block_t;
|
} mention_bech32_block_t;
|
||||||
|
|
||||||
typedef struct note_block {
|
typedef struct block {
|
||||||
enum block_type type;
|
enum block_type type;
|
||||||
union {
|
union {
|
||||||
struct str_block str;
|
struct str_block str;
|
||||||
@@ -45,13 +45,12 @@ typedef struct note_block {
|
|||||||
} block;
|
} block;
|
||||||
} block_t;
|
} block_t;
|
||||||
|
|
||||||
typedef struct note_blocks {
|
typedef struct blocks {
|
||||||
int words;
|
|
||||||
int num_blocks;
|
int num_blocks;
|
||||||
struct note_block *blocks;
|
struct block *blocks;
|
||||||
} blocks_t;
|
} blocks_t;
|
||||||
|
|
||||||
void blocks_init(struct note_blocks *blocks);
|
void blocks_init(struct blocks *blocks);
|
||||||
void blocks_free(struct note_blocks *blocks);
|
void blocks_free(struct blocks *blocks);
|
||||||
|
|
||||||
#endif /* block_h */
|
#endif /* block_h */
|
||||||
|
|||||||
+64
-422
@@ -1,438 +1,30 @@
|
|||||||
|
//
|
||||||
|
// cursor.h
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-04-09.
|
||||||
|
//
|
||||||
|
|
||||||
#ifndef PROTOVERSE_CURSOR_H
|
#ifndef cursor_h
|
||||||
#define PROTOVERSE_CURSOR_H
|
#define cursor_h
|
||||||
|
|
||||||
#include "typedefs.h"
|
|
||||||
#include "varint.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define unlikely(x) __builtin_expect((x),0)
|
typedef unsigned char u8;
|
||||||
#define likely(x) __builtin_expect((x),1)
|
|
||||||
|
|
||||||
struct cursor {
|
struct cursor {
|
||||||
unsigned char *start;
|
const u8 *p;
|
||||||
unsigned char *p;
|
const u8 *start;
|
||||||
unsigned char *end;
|
const u8 *end;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct array {
|
|
||||||
struct cursor cur;
|
|
||||||
unsigned int elem_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void reset_cursor(struct cursor *cursor)
|
|
||||||
{
|
|
||||||
cursor->p = cursor->start;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void wipe_cursor(struct cursor *cursor)
|
|
||||||
{
|
|
||||||
reset_cursor(cursor);
|
|
||||||
memset(cursor->start, 0, cursor->end - cursor->start);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void make_cursor(u8 *start, u8 *end, struct cursor *cursor)
|
|
||||||
{
|
|
||||||
cursor->start = start;
|
|
||||||
cursor->p = start;
|
|
||||||
cursor->end = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void make_array(struct array *a, u8* start, u8 *end, unsigned int elem_size)
|
|
||||||
{
|
|
||||||
make_cursor(start, end, &a->cur);
|
|
||||||
a->elem_size = elem_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_eof(struct cursor *c)
|
|
||||||
{
|
|
||||||
return c->p == c->end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void *cursor_malloc(struct cursor *mem, unsigned long size)
|
|
||||||
{
|
|
||||||
void *ret;
|
|
||||||
|
|
||||||
if (mem->p + size > mem->end) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mem->p;
|
|
||||||
mem->p += size;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
|
|
||||||
{
|
|
||||||
void *ret;
|
|
||||||
if (!(ret = cursor_malloc(mem, size))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(ret, 0, size);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
|
|
||||||
{
|
|
||||||
u8 *p;
|
|
||||||
if (!(p = cursor_alloc(mem, size))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
make_cursor(p, mem->p, slice);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void copy_cursor(struct cursor *src, struct cursor *dest)
|
|
||||||
{
|
|
||||||
dest->start = src->start;
|
|
||||||
dest->p = src->p;
|
|
||||||
dest->end = src->end;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int pull_byte(struct cursor *cursor, u8 *c)
|
|
||||||
{
|
|
||||||
if (unlikely(cursor->p >= cursor->end))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
*c = *cursor->p;
|
|
||||||
cursor->p++;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_pull_c_str(struct cursor *cursor, const char **str)
|
|
||||||
{
|
|
||||||
*str = (const char*)cursor->p;
|
|
||||||
|
|
||||||
for (; cursor->p < cursor->end; cursor->p++) {
|
|
||||||
if (*cursor->p == 0) {
|
|
||||||
cursor->p++;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline int cursor_push_byte(struct cursor *cursor, u8 c)
|
|
||||||
{
|
|
||||||
if (unlikely(cursor->p + 1 > cursor->end)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*cursor->p = c;
|
|
||||||
cursor->p++;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_pull(struct cursor *cursor, u8 *data, int len)
|
|
||||||
{
|
|
||||||
if (unlikely(cursor->p + len > cursor->end)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(data, cursor->p, len);
|
|
||||||
cursor->p += len;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int pull_data_into_cursor(struct cursor *cursor,
|
|
||||||
struct cursor *dest,
|
|
||||||
unsigned char **data,
|
|
||||||
int len)
|
|
||||||
{
|
|
||||||
int ok;
|
|
||||||
|
|
||||||
if (unlikely(dest->p + len > dest->end)) {
|
|
||||||
printf("not enough room in dest buffer\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = cursor_pull(cursor, dest->p, len);
|
|
||||||
if (!ok) return 0;
|
|
||||||
|
|
||||||
*data = dest->p;
|
|
||||||
dest->p += len;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_dropn(struct cursor *cur, int size, int n)
|
|
||||||
{
|
|
||||||
if (n == 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (unlikely(cur->p - size*n < cur->start)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur->p -= size*n;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_drop(struct cursor *cur, int size)
|
|
||||||
{
|
|
||||||
return cursor_dropn(cur, size, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned char *cursor_topn(struct cursor *cur, int len, int n)
|
|
||||||
{
|
|
||||||
n += 1;
|
|
||||||
if (unlikely(cur->p - len*n < cur->start)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return cur->p - len*n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned char *cursor_top(struct cursor *cur, int len)
|
|
||||||
{
|
|
||||||
if (unlikely(cur->p - len < cur->start)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return cur->p - len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_top_int(struct cursor *cur, int *i)
|
|
||||||
{
|
|
||||||
u8 *p;
|
|
||||||
if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*i = *((int*)p);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_pop(struct cursor *cur, u8 *data, int len)
|
|
||||||
{
|
|
||||||
if (unlikely(cur->p - len < cur->start)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur->p -= len;
|
|
||||||
memcpy(data, cur->p, len);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_push(struct cursor *cursor, u8 *data, int len)
|
|
||||||
{
|
|
||||||
if (unlikely(cursor->p + len >= cursor->end)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor->p != data)
|
|
||||||
memcpy(cursor->p, data, len);
|
|
||||||
|
|
||||||
cursor->p += len;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_push_int(struct cursor *cursor, int i)
|
|
||||||
{
|
|
||||||
return cursor_push(cursor, (u8*)&i, sizeof(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
|
|
||||||
{
|
|
||||||
return (cursor->p - cursor->start)/elem_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: push_varint */
|
|
||||||
static inline int push_varint(struct cursor *cursor, int n)
|
|
||||||
{
|
|
||||||
int ok, len;
|
|
||||||
unsigned char b;
|
|
||||||
len = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
b = (n & 0xFF) | 0x80;
|
|
||||||
n >>= 7;
|
|
||||||
if (n == 0) {
|
|
||||||
b &= 0x7F;
|
|
||||||
ok = cursor_push_byte(cursor, b);
|
|
||||||
len++;
|
|
||||||
if (!ok) return 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = cursor_push_byte(cursor, b);
|
|
||||||
len++;
|
|
||||||
if (!ok) return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: pull_varint */
|
|
||||||
static inline int pull_varint(struct cursor *cursor, int *n)
|
|
||||||
{
|
|
||||||
int ok, i;
|
|
||||||
unsigned char b;
|
|
||||||
*n = 0;
|
|
||||||
|
|
||||||
for (i = 0;; i++) {
|
|
||||||
ok = pull_byte(cursor, &b);
|
|
||||||
if (!ok) return 0;
|
|
||||||
|
|
||||||
*n |= ((int)b & 0x7F) << (i * 7);
|
|
||||||
|
|
||||||
/* is_last */
|
|
||||||
if ((b & 0x80) == 0) {
|
|
||||||
return i+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 4) return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_pull_int(struct cursor *cursor, int *i)
|
|
||||||
{
|
|
||||||
return cursor_pull(cursor, (u8*)i, sizeof(*i));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_push_u16(struct cursor *cursor, u16 i)
|
|
||||||
{
|
|
||||||
return cursor_push(cursor, (u8*)&i, sizeof(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size)
|
|
||||||
{
|
|
||||||
u8 *p;
|
|
||||||
p = &cursor->start[elem_size * index];
|
|
||||||
|
|
||||||
if (unlikely(p >= cursor->end))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return (void*)p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline int push_sized_str(struct cursor *cursor, const char *str, int len)
|
|
||||||
{
|
|
||||||
return cursor_push(cursor, (u8*)str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_push_str(struct cursor *cursor, const char *str)
|
|
||||||
{
|
|
||||||
return cursor_push(cursor, (u8*)str, (int)strlen(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_push_c_str(struct cursor *cursor, const char *str)
|
|
||||||
{
|
|
||||||
return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: push varint size */
|
|
||||||
static inline int push_prefixed_str(struct cursor *cursor, const char *str)
|
|
||||||
{
|
|
||||||
int ok, len;
|
|
||||||
len = (int)strlen(str);
|
|
||||||
ok = push_varint(cursor, len);
|
|
||||||
if (!ok) return 0;
|
|
||||||
return push_sized_str(cursor, str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int pull_prefixed_str(struct cursor *cursor, struct cursor *dest_buf, const char **str)
|
|
||||||
{
|
|
||||||
int len, ok;
|
|
||||||
|
|
||||||
ok = pull_varint(cursor, &len);
|
|
||||||
if (!ok) return 0;
|
|
||||||
|
|
||||||
if (unlikely(dest_buf->p + len > dest_buf->end)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = pull_data_into_cursor(cursor, dest_buf, (unsigned char**)str, len);
|
|
||||||
if (!ok) return 0;
|
|
||||||
|
|
||||||
ok = cursor_push_byte(dest_buf, 0);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_remaining_capacity(struct cursor *cursor)
|
|
||||||
{
|
|
||||||
return (int)(cursor->end - cursor->p);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
|
||||||
static inline void cursor_print_around(struct cursor *cur, int range)
|
|
||||||
{
|
|
||||||
unsigned char *c;
|
|
||||||
|
|
||||||
printf("[%ld/%ld]\n", cur->p - cur->start, cur->end - cur->start);
|
|
||||||
|
|
||||||
c = max(cur->p - range, cur->start);
|
|
||||||
for (; c < cur->end && c < (cur->p + range); c++) {
|
|
||||||
printf("%02x", *c);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
c = max(cur->p - range, cur->start);
|
|
||||||
for (; c < cur->end && c < (cur->p + range); c++) {
|
|
||||||
if (c == cur->p) {
|
|
||||||
printf("^");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
#undef max
|
|
||||||
|
|
||||||
static inline int pull_bytes(struct cursor *cur, int count, const u8 **bytes) {
|
|
||||||
if (cur->p + count > cur->end)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
*bytes = cur->p;
|
|
||||||
cur->p += count;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int parse_str(struct cursor *cur, const char *str) {
|
|
||||||
int i;
|
|
||||||
char c, cs;
|
|
||||||
unsigned long len;
|
|
||||||
|
|
||||||
len = strlen(str);
|
|
||||||
|
|
||||||
if (cur->p + len >= cur->end)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
c = tolower(cur->p[i]);
|
|
||||||
cs = tolower(str[i]);
|
|
||||||
|
|
||||||
if (c != cs)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cur->p += len;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int is_whitespace(char c) {
|
static inline int is_whitespace(char c) {
|
||||||
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
|
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_boundary(char c) {
|
static inline int is_boundary(char c) {
|
||||||
return is_whitespace(c) || ispunct(c);
|
return !isalnum(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_invalid_url_ending(char c) {
|
static inline int is_invalid_url_ending(char c) {
|
||||||
@@ -443,6 +35,13 @@ static inline int is_alphanumeric(char c) {
|
|||||||
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void make_cursor(struct cursor *c, const u8 *content, size_t len)
|
||||||
|
{
|
||||||
|
c->start = content;
|
||||||
|
c->end = content + len;
|
||||||
|
c->p = content;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int consume_until_boundary(struct cursor *cur) {
|
static inline int consume_until_boundary(struct cursor *cur) {
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
@@ -511,4 +110,47 @@ static inline int peek_char(struct cursor *cur, int ind) {
|
|||||||
return *(cur->p + ind);
|
return *(cur->p + ind);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
static inline int pull_byte(struct cursor *cur, u8 *byte) {
|
||||||
|
if (cur->p >= cur->end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*byte = *cur->p;
|
||||||
|
cur->p++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pull_bytes(struct cursor *cur, int count, const u8 **bytes) {
|
||||||
|
if (cur->p + count > cur->end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*bytes = cur->p;
|
||||||
|
cur->p += count;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int parse_str(struct cursor *cur, const char *str) {
|
||||||
|
int i;
|
||||||
|
char c, cs;
|
||||||
|
unsigned long len;
|
||||||
|
|
||||||
|
len = strlen(str);
|
||||||
|
|
||||||
|
if (cur->p + len >= cur->end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
c = tolower(cur->p[i]);
|
||||||
|
cs = tolower(str[i]);
|
||||||
|
|
||||||
|
if (c != cs)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur->p += len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* cursor_h */
|
||||||
|
|||||||
@@ -5,7 +5,3 @@
|
|||||||
#include "damus.h"
|
#include "damus.h"
|
||||||
#include "bolt11.h"
|
#include "bolt11.h"
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "nostr_bech32.h"
|
|
||||||
#include "wasm.h"
|
|
||||||
#include "nostrscript.h"
|
|
||||||
|
|
||||||
|
|||||||
+26
-32
@@ -28,9 +28,9 @@ static int parse_digit(struct cursor *cur, int *digit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int parse_mention_index(struct cursor *cur, struct note_block *block) {
|
static int parse_mention_index(struct cursor *cur, struct block *block) {
|
||||||
int d1, d2, d3, ind;
|
int d1, d2, d3, ind;
|
||||||
u8 *start = cur->p;
|
const u8 *start = cur->p;
|
||||||
|
|
||||||
if (!parse_str(cur, "#["))
|
if (!parse_str(cur, "#["))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -59,9 +59,9 @@ static int parse_mention_index(struct cursor *cur, struct note_block *block) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
static int parse_hashtag(struct cursor *cur, struct block *block) {
|
||||||
int c;
|
int c;
|
||||||
u8 *start = cur->p;
|
const u8 *start = cur->p;
|
||||||
|
|
||||||
if (!parse_char(cur, '#'))
|
if (!parse_char(cur, '#'))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -81,7 +81,7 @@ static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_block(struct note_blocks *blocks, struct note_block block)
|
static int add_block(struct blocks *blocks, struct block block)
|
||||||
{
|
{
|
||||||
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
|
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -90,9 +90,9 @@ static int add_block(struct note_blocks *blocks, struct note_block block)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8 *end)
|
static int add_text_block(struct blocks *blocks, const u8 *start, const u8 *end)
|
||||||
{
|
{
|
||||||
struct note_block b;
|
struct block b;
|
||||||
|
|
||||||
if (start == end)
|
if (start == end)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -104,8 +104,8 @@ static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8
|
|||||||
return add_block(blocks, b);
|
return add_block(blocks, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_url(struct cursor *cur, struct note_block *block) {
|
static int parse_url(struct cursor *cur, struct block *block) {
|
||||||
u8 *start = cur->p;
|
const u8 *start = cur->p;
|
||||||
|
|
||||||
if (!parse_str(cur, "http"))
|
if (!parse_str(cur, "http"))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -137,8 +137,8 @@ static int parse_url(struct cursor *cur, struct note_block *block) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_invoice(struct cursor *cur, struct note_block *block) {
|
static int parse_invoice(struct cursor *cur, struct block *block) {
|
||||||
u8 *start, *end;
|
const u8 *start, *end;
|
||||||
char *fail;
|
char *fail;
|
||||||
struct bolt11 *bolt11;
|
struct bolt11 *bolt11;
|
||||||
// optional
|
// optional
|
||||||
@@ -177,12 +177,12 @@ static int parse_invoice(struct cursor *cur, struct note_block *block) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
|
static int parse_mention_bech32(struct cursor *cur, struct block *block) {
|
||||||
u8 *start = cur->p;
|
const u8 *start = cur->p;
|
||||||
|
|
||||||
|
if (!parse_str(cur, "nostr:"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
parse_char(cur, '@');
|
|
||||||
parse_str(cur, "nostr:");
|
|
||||||
|
|
||||||
block->block.str.start = (const char *)cur->p;
|
block->block.str.start = (const char *)cur->p;
|
||||||
|
|
||||||
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
|
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
|
||||||
@@ -197,7 +197,7 @@ static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, struct note_block block, u8 **start, const u8 *pre_mention)
|
static int add_text_then_block(struct cursor *cur, struct blocks *blocks, struct block block, const u8 **start, const u8 *pre_mention)
|
||||||
{
|
{
|
||||||
if (!add_text_block(blocks, *start, pre_mention))
|
if (!add_text_block(blocks, *start, pre_mention))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -210,28 +210,22 @@ static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, s
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
int damus_parse_content(struct blocks *blocks, const char *content) {
|
||||||
int cp, c;
|
int cp, c;
|
||||||
struct cursor cur;
|
struct cursor cur;
|
||||||
struct note_block block;
|
struct block block;
|
||||||
u8 *start, *pre_mention;
|
const u8 *start, *pre_mention;
|
||||||
|
|
||||||
blocks->words = 0;
|
|
||||||
blocks->num_blocks = 0;
|
blocks->num_blocks = 0;
|
||||||
make_cursor((u8*)content, (u8*)content + strlen(content), &cur);
|
make_cursor(&cur, (const u8*)content, strlen(content));
|
||||||
|
|
||||||
start = cur.p;
|
start = cur.p;
|
||||||
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
|
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
|
||||||
cp = peek_char(&cur, -1);
|
cp = peek_char(&cur, -1);
|
||||||
c = peek_char(&cur, 0);
|
c = peek_char(&cur, 0);
|
||||||
|
|
||||||
// new word
|
|
||||||
if (is_whitespace(cp) && !is_whitespace(c)) {
|
|
||||||
blocks->words++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre_mention = cur.p;
|
pre_mention = cur.p;
|
||||||
if (cp == -1 || is_boundary(cp) || c == '#') {
|
if (cp == -1 || is_whitespace(cp) || c == '#') {
|
||||||
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
|
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
|
||||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -244,7 +238,7 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
|||||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
return 0;
|
return 0;
|
||||||
continue;
|
continue;
|
||||||
} else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) {
|
} else if (c == 'n' && parse_mention_bech32(&cur, &block)) {
|
||||||
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
return 0;
|
return 0;
|
||||||
continue;
|
continue;
|
||||||
@@ -262,12 +256,12 @@ int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blocks_init(struct note_blocks *blocks) {
|
void blocks_init(struct blocks *blocks) {
|
||||||
blocks->blocks = malloc(sizeof(struct note_block) * MAX_BLOCKS);
|
blocks->blocks = malloc(sizeof(struct block) * MAX_BLOCKS);
|
||||||
blocks->num_blocks = 0;
|
blocks->num_blocks = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blocks_free(struct note_blocks *blocks) {
|
void blocks_free(struct blocks *blocks) {
|
||||||
if (!blocks->blocks) {
|
if (!blocks->blocks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -9,10 +9,10 @@
|
|||||||
#define damus_h
|
#define damus_h
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "nostr_bech32.h"
|
||||||
#include "block.h"
|
#include "block.h"
|
||||||
|
|
||||||
typedef unsigned char u8;
|
typedef unsigned char u8;
|
||||||
|
|
||||||
int damus_parse_content(struct note_blocks *blocks, const char *content);
|
int damus_parse_content(struct blocks *blocks, const char *content);
|
||||||
|
|
||||||
#endif /* damus_h */
|
#endif /* damus_h */
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PROTOVERSE_DEBUG_H
|
|
||||||
#define PROTOVERSE_DEBUG_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define unusual(...) fprintf(stderr, "UNUSUAL: " __VA_ARGS__)
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define debug(...) printf(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define debug(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* PROTOVERSE_DEBUG_H */
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
|
|
||||||
#include "error.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
int note_error_(struct errors *errs_, struct cursor *p, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
static char buf[512];
|
|
||||||
struct error err;
|
|
||||||
struct cursor *errs;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
errs = &errs_->cur;
|
|
||||||
|
|
||||||
if (errs_->enabled == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vsprintf(buf, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
err.msg = buf;
|
|
||||||
err.pos = p ? (int)(p->p - p->start) : 0;
|
|
||||||
|
|
||||||
if (!cursor_push_error(errs, &err)) {
|
|
||||||
fprintf(stderr, "arena OOM when recording error, ");
|
|
||||||
fprintf(stderr, "errs->p at %ld, remaining %ld, strlen %ld\n",
|
|
||||||
errs->p - errs->start, errs->end - errs->p, strlen(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PROTOVERSE_ERROR_H
|
|
||||||
#define PROTOVERSE_ERROR_H
|
|
||||||
|
|
||||||
#include "cursor.h"
|
|
||||||
|
|
||||||
struct error {
|
|
||||||
int pos;
|
|
||||||
const char *msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct errors {
|
|
||||||
struct cursor cur;
|
|
||||||
int enabled;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define note_error(errs, p, fmt, ...) note_error_(errs, p, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
static inline int cursor_push_error(struct cursor *cur, struct error *err)
|
|
||||||
{
|
|
||||||
return cursor_push_int(cur, err->pos) &&
|
|
||||||
cursor_push_c_str(cur, err->msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_pull_error(struct cursor *cur, struct error *err)
|
|
||||||
{
|
|
||||||
return cursor_pull_int(cur, &err->pos) &&
|
|
||||||
cursor_pull_c_str(cur, &err->msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int note_error_(struct errors *errs, struct cursor *p, const char *fmt, ...);
|
|
||||||
|
|
||||||
#endif /* PROTOVERSE_ERROR_H */
|
|
||||||
@@ -52,13 +52,9 @@
|
|||||||
*/
|
*/
|
||||||
#define unlikely(cond) __builtin_expect(!!(cond), 0)
|
#define unlikely(cond) __builtin_expect(!!(cond), 0)
|
||||||
#else
|
#else
|
||||||
#ifndef likely
|
|
||||||
#define likely(cond) (!!(cond))
|
#define likely(cond) (!!(cond))
|
||||||
#endif
|
|
||||||
#ifndef unlikely
|
|
||||||
#define unlikely(cond) (!!(cond))
|
#define unlikely(cond) (!!(cond))
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
#else /* CCAN_LIKELY_DEBUG versions */
|
#else /* CCAN_LIKELY_DEBUG versions */
|
||||||
#include <ccan/str/str.h>
|
#include <ccan/str/str.h>
|
||||||
|
|
||||||
|
|||||||
+2
-13
@@ -91,9 +91,6 @@ static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *t
|
|||||||
} else if (strcmp(prefix, "npub") == 0) {
|
} else if (strcmp(prefix, "npub") == 0) {
|
||||||
*type = NOSTR_BECH32_NPUB;
|
*type = NOSTR_BECH32_NPUB;
|
||||||
return 1;
|
return 1;
|
||||||
} else if (strcmp(prefix, "nsec") == 0) {
|
|
||||||
*type = NOSTR_BECH32_NSEC;
|
|
||||||
return 1;
|
|
||||||
} else if (strcmp(prefix, "nprofile") == 0) {
|
} else if (strcmp(prefix, "nprofile") == 0) {
|
||||||
*type = NOSTR_BECH32_NPROFILE;
|
*type = NOSTR_BECH32_NPROFILE;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -119,10 +116,6 @@ static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub)
|
|||||||
return pull_bytes(cur, 32, &npub->pubkey);
|
return pull_bytes(cur, 32, &npub->pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_nostr_bech32_nsec(struct cursor *cur, struct bech32_nsec *nsec) {
|
|
||||||
return pull_bytes(cur, 32, &nsec->nsec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
|
static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
|
||||||
struct nostr_tlv *tlv;
|
struct nostr_tlv *tlv;
|
||||||
struct str_block *str;
|
struct str_block *str;
|
||||||
@@ -225,7 +218,7 @@ static int parse_nostr_bech32_nrelay(struct cursor *cur, struct bech32_nrelay *n
|
|||||||
}
|
}
|
||||||
|
|
||||||
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
||||||
u8 *start, *end;
|
const u8 *start, *end;
|
||||||
|
|
||||||
start = cur->p;
|
start = cur->p;
|
||||||
|
|
||||||
@@ -264,7 +257,7 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct cursor bcur;
|
struct cursor bcur;
|
||||||
make_cursor(obj->buffer, obj->buffer + obj->buflen, &bcur);
|
make_cursor(&bcur, obj->buffer, obj->buflen);
|
||||||
|
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case NOSTR_BECH32_NOTE:
|
case NOSTR_BECH32_NOTE:
|
||||||
@@ -275,10 +268,6 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
|||||||
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
|
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case NOSTR_BECH32_NSEC:
|
|
||||||
if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec))
|
|
||||||
goto fail;
|
|
||||||
break;
|
|
||||||
case NOSTR_BECH32_NEVENT:
|
case NOSTR_BECH32_NEVENT:
|
||||||
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
|
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ enum nostr_bech32_type {
|
|||||||
NOSTR_BECH32_NEVENT = 4,
|
NOSTR_BECH32_NEVENT = 4,
|
||||||
NOSTR_BECH32_NRELAY = 5,
|
NOSTR_BECH32_NRELAY = 5,
|
||||||
NOSTR_BECH32_NADDR = 6,
|
NOSTR_BECH32_NADDR = 6,
|
||||||
NOSTR_BECH32_NSEC = 7,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bech32_note {
|
struct bech32_note {
|
||||||
@@ -37,10 +36,6 @@ struct bech32_npub {
|
|||||||
const u8 *pubkey;
|
const u8 *pubkey;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bech32_nsec {
|
|
||||||
const u8 *nsec;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bech32_nevent {
|
struct bech32_nevent {
|
||||||
struct relays relays;
|
struct relays relays;
|
||||||
const u8 *event_id;
|
const u8 *event_id;
|
||||||
@@ -70,7 +65,6 @@ typedef struct nostr_bech32 {
|
|||||||
union {
|
union {
|
||||||
struct bech32_note note;
|
struct bech32_note note;
|
||||||
struct bech32_npub npub;
|
struct bech32_npub npub;
|
||||||
struct bech32_nsec nsec;
|
|
||||||
struct bech32_nevent nevent;
|
struct bech32_nevent nevent;
|
||||||
struct bech32_nprofile nprofile;
|
struct bech32_nprofile nprofile;
|
||||||
struct bech32_naddr naddr;
|
struct bech32_naddr naddr;
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
//
|
|
||||||
// nostrscript.c
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-02.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "nostrscript.h"
|
|
||||||
#include "wasm.h"
|
|
||||||
#include "array_size.h"
|
|
||||||
|
|
||||||
// function to check if the character is in surrogate pair range
|
|
||||||
static INLINE int is_surrogate(uint16_t uc) {
|
|
||||||
return (uc - 0xd800u) < 2048u;
|
|
||||||
}
|
|
||||||
|
|
||||||
// function to convert utf16 to utf8
|
|
||||||
static int utf16_to_utf8(u16 utf16, u8 *utf8) {
|
|
||||||
if (utf16 < 0x80) { // 1-byte sequence
|
|
||||||
utf8[0] = (uint8_t) utf16;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (utf16 < 0x800) { // 2-byte sequence
|
|
||||||
utf8[0] = (uint8_t) (0xc0 | (utf16 >> 6));
|
|
||||||
utf8[1] = (uint8_t) (0x80 | (utf16 & 0x3f));
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
else if (!is_surrogate(utf16)) { // 3-byte sequence
|
|
||||||
utf8[0] = (uint8_t) (0xe0 | (utf16 >> 12));
|
|
||||||
utf8[1] = (uint8_t) (0x80 | ((utf16 >> 6) & 0x3f));
|
|
||||||
utf8[2] = (uint8_t) (0x80 | (utf16 & 0x3f));
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
else { // surrogate pair, return error
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nostr_cmd(struct wasm_interp *interp) {
|
|
||||||
struct val *params = NULL;
|
|
||||||
const char *val = NULL;
|
|
||||||
int len, cmd, ival;
|
|
||||||
|
|
||||||
if (!get_params(interp, ¶ms, 3) || params == NULL)
|
|
||||||
return interp_error(interp, "get params");
|
|
||||||
|
|
||||||
// command
|
|
||||||
cmd = params[0].num.i32;
|
|
||||||
|
|
||||||
// value
|
|
||||||
|
|
||||||
ival = params[1].num.i32;
|
|
||||||
if (!mem_ptr_str(interp, ival, &val))
|
|
||||||
val = 0;
|
|
||||||
|
|
||||||
// length
|
|
||||||
len = params[2].num.i32;
|
|
||||||
|
|
||||||
intptr_t iptr = ival;
|
|
||||||
return nscript_nostr_cmd(interp, cmd, val ? (void*)val : (void*)iptr, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int print_utf16_str(u16 *chars) {
|
|
||||||
u16 *p = chars;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
while (*p) {
|
|
||||||
if (utf16_to_utf8(*p, (u8*)&c) == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
printf("%c", c);
|
|
||||||
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nostr_log(struct wasm_interp *interp) {
|
|
||||||
struct val *vals;
|
|
||||||
const char *str;
|
|
||||||
struct callframe *callframe;
|
|
||||||
|
|
||||||
if (!get_params(interp, &vals, 1))
|
|
||||||
return interp_error(interp, "nostr_log get params");
|
|
||||||
|
|
||||||
if (!mem_ptr_str(interp, vals[0].num.i32, &str))
|
|
||||||
return interp_error(interp, "nostr_log log param");
|
|
||||||
|
|
||||||
if (!(callframe = top_callframes(&interp->callframes, 2)))
|
|
||||||
return interp_error(interp, "nostr_log callframe");
|
|
||||||
|
|
||||||
printf("nostr_log:%s: ", callframe->func->name);
|
|
||||||
|
|
||||||
print_utf16_str((u16*)str);
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nostr_set_bool(struct wasm_interp *interp) {
|
|
||||||
struct val *params = NULL;
|
|
||||||
const u16 *setting;
|
|
||||||
u32 val, len;
|
|
||||||
|
|
||||||
if (!get_params(interp, ¶ms, 3) || params == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!mem_ptr_str(interp, params[0].num.i32, (const char**)&setting))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
len = params[1].num.i32;
|
|
||||||
val = params[2].num.i32 > 0 ? 1 : 0;
|
|
||||||
|
|
||||||
return nscript_set_bool(interp, setting, len, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nostr_pool_send_to(struct wasm_interp *interp) {
|
|
||||||
struct val *params = NULL;
|
|
||||||
const u16 *req, *to;
|
|
||||||
int req_len, to_len;
|
|
||||||
|
|
||||||
if (!get_params(interp, ¶ms, 4) || params == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!mem_ptr_str(interp, params[0].num.i32, (const char**)&req))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
req_len = params[1].num.i32;
|
|
||||||
|
|
||||||
if (!mem_ptr_str(interp, params[2].num.i32, (const char**)&to))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
to_len = params[3].num.i32;
|
|
||||||
|
|
||||||
return nscript_pool_send_to(interp, req, req_len, to, to_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nscript_abort(struct wasm_interp *interp) {
|
|
||||||
struct val *params = NULL;
|
|
||||||
const char *msg = "", *filename;
|
|
||||||
int line, col;
|
|
||||||
|
|
||||||
if (!get_params(interp, ¶ms, 4) || params == NULL)
|
|
||||||
return interp_error(interp, "get params");
|
|
||||||
|
|
||||||
if (params[0].ref.addr != 0 && !mem_ptr_str(interp, params[0].ref.addr, &msg))
|
|
||||||
return interp_error(interp, "abort msg");
|
|
||||||
|
|
||||||
if (!mem_ptr_str(interp, params[1].ref.addr, &filename))
|
|
||||||
return interp_error(interp, "abort filename");
|
|
||||||
|
|
||||||
line = params[2].num.i32;
|
|
||||||
col = params[3].num.i32;
|
|
||||||
|
|
||||||
printf("nscript_abort:");
|
|
||||||
print_utf16_str((u16*)filename);
|
|
||||||
printf(":%d:%d: ", line, col);
|
|
||||||
print_utf16_str((u16*)msg);
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct builtin nscript_builtins[] = {
|
|
||||||
{ .name = "null", .fn = 0 },
|
|
||||||
{ .name = "nostr_log", .fn = nostr_log },
|
|
||||||
{ .name = "nostr_cmd", .fn = nostr_cmd },
|
|
||||||
{ .name = "nostr_pool_send_to", .fn = nostr_pool_send_to },
|
|
||||||
{ .name = "nostr_set_bool", .fn = nostr_set_bool },
|
|
||||||
{ .name = "abort", .fn = nscript_abort },
|
|
||||||
};
|
|
||||||
|
|
||||||
int nscript_load(struct wasm_parser *p, struct wasm_interp *interp, unsigned char *wasm, unsigned long len) {
|
|
||||||
wasm_parser_init(p, wasm, len, len * 16, nscript_builtins, ARRAY_SIZE(nscript_builtins));
|
|
||||||
|
|
||||||
if (!parse_wasm(p)) {
|
|
||||||
wasm_parser_free(p);
|
|
||||||
return NSCRIPT_PARSE_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wasm_interp_init(interp, &p->module)) {
|
|
||||||
print_error_backtrace(&interp->errors);
|
|
||||||
wasm_parser_free(p);
|
|
||||||
return NSCRIPT_INIT_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
//setup_wasi(&interp, argc, argv, env);
|
|
||||||
//wasm_parser_free(&p);
|
|
||||||
|
|
||||||
return NSCRIPT_LOADED;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// nostrscript.h
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-02.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef nostrscript_h
|
|
||||||
#define nostrscript_h
|
|
||||||
|
|
||||||
#define NSCRIPT_LOADED 1
|
|
||||||
#define NSCRIPT_PARSE_ERR 2
|
|
||||||
#define NSCRIPT_INIT_ERR 3
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "wasm.h"
|
|
||||||
|
|
||||||
int nscript_load(struct wasm_parser *p, struct wasm_interp *interp, unsigned char *wasm, unsigned long len);
|
|
||||||
int nscript_nostr_cmd(struct wasm_interp *interp, int, void*, int);
|
|
||||||
int nscript_pool_send_to(struct wasm_interp *interp, const u16*, int, const u16 *, int);
|
|
||||||
int nscript_set_bool(struct wasm_interp *interp, const u16*, int, int);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* nostrscript_h */
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
#ifndef CURSOR_PARSER
|
|
||||||
#define CURSOR_PARSER
|
|
||||||
|
|
||||||
#include "cursor.h"
|
|
||||||
|
|
||||||
static int consume_bytes(struct cursor *cursor, const unsigned char *match, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (cursor->p + len > cursor->end) {
|
|
||||||
fprintf(stderr, "consume_bytes overflow\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
if (cursor->p[i] != match[i])
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor->p += len;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int consume_byte(struct cursor *cursor, unsigned char match)
|
|
||||||
{
|
|
||||||
if (unlikely(cursor->p >= cursor->end))
|
|
||||||
return 0;
|
|
||||||
if (*cursor->p != match)
|
|
||||||
return 0;
|
|
||||||
cursor->p++;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int consume_u32(struct cursor *cursor, unsigned int match)
|
|
||||||
{
|
|
||||||
return consume_bytes(cursor, (unsigned char*)&match, sizeof(match));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CURSOR_PARSER */
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PROTOVERSE_TYPEDEFS_H
|
|
||||||
#define PROTOVERSE_TYPEDEFS_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef unsigned char u8;
|
|
||||||
typedef unsigned int u32;
|
|
||||||
typedef unsigned short u16;
|
|
||||||
typedef uint64_t u64;
|
|
||||||
typedef int64_t s64;
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* PROTOVERSE_TYPEDEFS_H */
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PROTOVERSE_VARINT_H
|
|
||||||
#define PROTOVERSE_VARINT_H
|
|
||||||
|
|
||||||
#define VARINT_MAX_LEN 9
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
size_t varint_put(unsigned char buf[VARINT_MAX_LEN], uint64_t v);
|
|
||||||
size_t varint_size(uint64_t v);
|
|
||||||
size_t varint_get(const unsigned char *p, size_t max, int64_t *val);
|
|
||||||
|
|
||||||
#endif /* PROTOVERSE_VARINT_H */
|
|
||||||
-7298
File diff suppressed because it is too large
Load Diff
-850
@@ -1,850 +0,0 @@
|
|||||||
|
|
||||||
#ifndef PROTOVERSE_WASM_H
|
|
||||||
#define PROTOVERSE_WASM_H
|
|
||||||
|
|
||||||
static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
|
|
||||||
|
|
||||||
#define WASM_VERSION 0x01
|
|
||||||
#define MAX_U32_LEB128_BYTES 5
|
|
||||||
#define MAX_U64_LEB128_BYTES 10
|
|
||||||
#define MAX_CUSTOM_SECTIONS 32
|
|
||||||
#define MAX_BUILTINS 64
|
|
||||||
#define BUILTIN_SUSPEND 42
|
|
||||||
|
|
||||||
#define FUNC_TYPE_TAG 0x60
|
|
||||||
|
|
||||||
|
|
||||||
#include "cursor.h"
|
|
||||||
#include "error.h"
|
|
||||||
|
|
||||||
#ifdef NOINLINE
|
|
||||||
#define INLINE __attribute__((noinline))
|
|
||||||
#else
|
|
||||||
#define INLINE inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#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__)
|
|
||||||
|
|
||||||
enum valtype {
|
|
||||||
val_i32 = 0x7F,
|
|
||||||
val_i64 = 0x7E,
|
|
||||||
val_f32 = 0x7D,
|
|
||||||
val_f64 = 0x7C,
|
|
||||||
val_ref_null = 0xD0,
|
|
||||||
val_ref_func = 0x70,
|
|
||||||
val_ref_extern = 0x6F,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum const_instr {
|
|
||||||
ci_const_i32 = 0x41,
|
|
||||||
ci_const_i64 = 0x42,
|
|
||||||
ci_const_f32 = 0x43,
|
|
||||||
ci_const_f64 = 0x44,
|
|
||||||
ci_ref_null = 0xD0,
|
|
||||||
ci_ref_func = 0xD2,
|
|
||||||
ci_global_get = 0x23,
|
|
||||||
ci_end = 0x0B,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum limit_type {
|
|
||||||
limit_min = 0x00,
|
|
||||||
limit_min_max = 0x01,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct limits {
|
|
||||||
u32 min;
|
|
||||||
u32 max;
|
|
||||||
enum limit_type type;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum section_tag {
|
|
||||||
section_custom,
|
|
||||||
section_type,
|
|
||||||
section_import,
|
|
||||||
section_function,
|
|
||||||
section_table,
|
|
||||||
section_memory,
|
|
||||||
section_global,
|
|
||||||
section_export,
|
|
||||||
section_start,
|
|
||||||
section_element,
|
|
||||||
section_code,
|
|
||||||
section_data,
|
|
||||||
section_data_count,
|
|
||||||
section_name,
|
|
||||||
num_sections,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum name_subsection_tag {
|
|
||||||
name_subsection_module,
|
|
||||||
name_subsection_funcs,
|
|
||||||
name_subsection_locals,
|
|
||||||
num_name_subsections,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum reftype {
|
|
||||||
funcref = 0x70,
|
|
||||||
externref = 0x6F,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct resulttype {
|
|
||||||
unsigned char *valtypes; /* enum valtype */
|
|
||||||
u32 num_valtypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct functype {
|
|
||||||
struct resulttype params;
|
|
||||||
struct resulttype result;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct table {
|
|
||||||
enum reftype reftype;
|
|
||||||
struct limits limits;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tablesec {
|
|
||||||
struct table *tables;
|
|
||||||
u32 num_tables;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum elem_mode {
|
|
||||||
elem_mode_passive,
|
|
||||||
elem_mode_active,
|
|
||||||
elem_mode_declarative,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct expr {
|
|
||||||
u8 *code;
|
|
||||||
u32 code_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct refval {
|
|
||||||
u32 addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct table_inst {
|
|
||||||
struct refval *refs;
|
|
||||||
enum reftype reftype;
|
|
||||||
u32 num_refs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct numval {
|
|
||||||
union {
|
|
||||||
int i32;
|
|
||||||
u32 u32;
|
|
||||||
int64_t i64;
|
|
||||||
uint64_t u64;
|
|
||||||
float f32;
|
|
||||||
double f64;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct val {
|
|
||||||
enum valtype type;
|
|
||||||
union {
|
|
||||||
struct numval num;
|
|
||||||
struct refval ref;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct elem_inst {
|
|
||||||
struct val val;
|
|
||||||
u16 elem;
|
|
||||||
u16 init;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct elem {
|
|
||||||
struct expr offset;
|
|
||||||
u32 tableidx;
|
|
||||||
struct expr *inits;
|
|
||||||
u32 num_inits;
|
|
||||||
enum elem_mode mode;
|
|
||||||
enum reftype reftype;
|
|
||||||
struct val val;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct customsec {
|
|
||||||
const char *name;
|
|
||||||
unsigned char *data;
|
|
||||||
u32 data_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct elemsec {
|
|
||||||
struct elem *elements;
|
|
||||||
u32 num_elements;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct memsec {
|
|
||||||
struct limits *mems; /* memtype */
|
|
||||||
u32 num_mems;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct funcsec {
|
|
||||||
u32 *type_indices;
|
|
||||||
u32 num_indices;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mut {
|
|
||||||
mut_const,
|
|
||||||
mut_var,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct globaltype {
|
|
||||||
enum valtype valtype;
|
|
||||||
enum mut mut;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct globalsec {
|
|
||||||
struct global *globals;
|
|
||||||
u32 num_globals;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct typesec {
|
|
||||||
struct functype *functypes;
|
|
||||||
u32 num_functypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum import_type {
|
|
||||||
import_func,
|
|
||||||
import_table,
|
|
||||||
import_mem,
|
|
||||||
import_global,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct importdesc {
|
|
||||||
enum import_type type;
|
|
||||||
union {
|
|
||||||
u32 typeidx;
|
|
||||||
struct limits tabletype;
|
|
||||||
struct limits memtype;
|
|
||||||
struct globaltype globaltype;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct import {
|
|
||||||
const char *module_name;
|
|
||||||
const char *name;
|
|
||||||
struct importdesc desc;
|
|
||||||
int resolved_builtin;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct importsec {
|
|
||||||
struct import *imports;
|
|
||||||
u32 num_imports;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct global {
|
|
||||||
struct globaltype type;
|
|
||||||
struct expr init;
|
|
||||||
struct val val;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct local_def {
|
|
||||||
u32 num_types;
|
|
||||||
enum valtype type;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* "code" */
|
|
||||||
struct wasm_func {
|
|
||||||
struct expr code;
|
|
||||||
struct local_def *local_defs;
|
|
||||||
u32 num_local_defs;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum func_type {
|
|
||||||
func_type_wasm,
|
|
||||||
func_type_builtin,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct func {
|
|
||||||
union {
|
|
||||||
struct wasm_func *wasm_func;
|
|
||||||
struct builtin *builtin;
|
|
||||||
};
|
|
||||||
u32 num_locals;
|
|
||||||
struct functype *functype;
|
|
||||||
enum func_type type;
|
|
||||||
const char *name;
|
|
||||||
u32 idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct codesec {
|
|
||||||
struct wasm_func *funcs;
|
|
||||||
u32 num_funcs;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum exportdesc {
|
|
||||||
export_func,
|
|
||||||
export_table,
|
|
||||||
export_mem,
|
|
||||||
export_global,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wexport {
|
|
||||||
const char *name;
|
|
||||||
u32 index;
|
|
||||||
enum exportdesc desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct exportsec {
|
|
||||||
struct wexport *exports;
|
|
||||||
u32 num_exports;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nameassoc {
|
|
||||||
u32 index;
|
|
||||||
const char *name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct namemap {
|
|
||||||
struct nameassoc *names;
|
|
||||||
u32 num_names;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct namesec {
|
|
||||||
const char *module_name;
|
|
||||||
struct namemap func_names;
|
|
||||||
int parsed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wsection {
|
|
||||||
enum section_tag tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum bulk_tag {
|
|
||||||
i_memory_copy = 10,
|
|
||||||
i_memory_fill = 11,
|
|
||||||
i_table_init = 12,
|
|
||||||
i_elem_drop = 13,
|
|
||||||
i_table_copy = 14,
|
|
||||||
i_table_grow = 15,
|
|
||||||
i_table_size = 16,
|
|
||||||
i_table_fill = 17,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum instr_tag {
|
|
||||||
/* control instructions */
|
|
||||||
i_unreachable = 0x00,
|
|
||||||
i_nop = 0x01,
|
|
||||||
i_block = 0x02,
|
|
||||||
i_loop = 0x03,
|
|
||||||
i_if = 0x04,
|
|
||||||
i_else = 0x05,
|
|
||||||
i_end = 0x0B,
|
|
||||||
i_br = 0x0C,
|
|
||||||
i_br_if = 0x0D,
|
|
||||||
i_br_table = 0x0E,
|
|
||||||
i_return = 0x0F,
|
|
||||||
i_call = 0x10,
|
|
||||||
i_call_indirect = 0x11,
|
|
||||||
|
|
||||||
/* parametric instructions */
|
|
||||||
i_drop = 0x1A,
|
|
||||||
i_select = 0x1B,
|
|
||||||
i_selects = 0x1C,
|
|
||||||
|
|
||||||
/* variable instructions */
|
|
||||||
i_local_get = 0x20,
|
|
||||||
i_local_set = 0x21,
|
|
||||||
i_local_tee = 0x22,
|
|
||||||
i_global_get = 0x23,
|
|
||||||
i_global_set = 0x24,
|
|
||||||
i_table_get = 0x25,
|
|
||||||
i_table_set = 0x26,
|
|
||||||
|
|
||||||
/* memory instructions */
|
|
||||||
i_i32_load = 0x28,
|
|
||||||
i_i64_load = 0x29,
|
|
||||||
i_f32_load = 0x2A,
|
|
||||||
i_f64_load = 0x2B,
|
|
||||||
i_i32_load8_s = 0x2C,
|
|
||||||
i_i32_load8_u = 0x2D,
|
|
||||||
i_i32_load16_s = 0x2E,
|
|
||||||
i_i32_load16_u = 0x2F,
|
|
||||||
i_i64_load8_s = 0x30,
|
|
||||||
i_i64_load8_u = 0x31,
|
|
||||||
i_i64_load16_s = 0x32,
|
|
||||||
i_i64_load16_u = 0x33,
|
|
||||||
i_i64_load32_s = 0x34,
|
|
||||||
i_i64_load32_u = 0x35,
|
|
||||||
i_i32_store = 0x36,
|
|
||||||
i_i64_store = 0x37,
|
|
||||||
i_f32_store = 0x38,
|
|
||||||
i_f64_store = 0x39,
|
|
||||||
i_i32_store8 = 0x3A,
|
|
||||||
i_i32_store16 = 0x3B,
|
|
||||||
i_i64_store8 = 0x3C,
|
|
||||||
i_i64_store16 = 0x3D,
|
|
||||||
i_i64_store32 = 0x3E,
|
|
||||||
i_memory_size = 0x3F,
|
|
||||||
i_memory_grow = 0x40,
|
|
||||||
|
|
||||||
/* numeric instructions */
|
|
||||||
i_i32_const = 0x41,
|
|
||||||
i_i64_const = 0x42,
|
|
||||||
i_f32_const = 0x43,
|
|
||||||
i_f64_const = 0x44,
|
|
||||||
|
|
||||||
i_i32_eqz = 0x45,
|
|
||||||
i_i32_eq = 0x46,
|
|
||||||
i_i32_ne = 0x47,
|
|
||||||
i_i32_lt_s = 0x48,
|
|
||||||
i_i32_lt_u = 0x49,
|
|
||||||
i_i32_gt_s = 0x4A,
|
|
||||||
i_i32_gt_u = 0x4B,
|
|
||||||
i_i32_le_s = 0x4C,
|
|
||||||
i_i32_le_u = 0x4D,
|
|
||||||
i_i32_ge_s = 0x4E,
|
|
||||||
i_i32_ge_u = 0x4F,
|
|
||||||
|
|
||||||
i_i64_eqz = 0x50,
|
|
||||||
i_i64_eq = 0x51,
|
|
||||||
i_i64_ne = 0x52,
|
|
||||||
i_i64_lt_s = 0x53,
|
|
||||||
i_i64_lt_u = 0x54,
|
|
||||||
i_i64_gt_s = 0x55,
|
|
||||||
i_i64_gt_u = 0x56,
|
|
||||||
i_i64_le_s = 0x57,
|
|
||||||
i_i64_le_u = 0x58,
|
|
||||||
i_i64_ge_s = 0x59,
|
|
||||||
i_i64_ge_u = 0x5A,
|
|
||||||
|
|
||||||
i_f32_eq = 0x5B,
|
|
||||||
i_f32_ne = 0x5C,
|
|
||||||
i_f32_lt = 0x5D,
|
|
||||||
i_f32_gt = 0x5E,
|
|
||||||
i_f32_le = 0x5F,
|
|
||||||
i_f32_ge = 0x60,
|
|
||||||
|
|
||||||
i_f64_eq = 0x61,
|
|
||||||
i_f64_ne = 0x62,
|
|
||||||
i_f64_lt = 0x63,
|
|
||||||
i_f64_gt = 0x64,
|
|
||||||
i_f64_le = 0x65,
|
|
||||||
i_f64_ge = 0x66,
|
|
||||||
|
|
||||||
i_i32_clz = 0x67,
|
|
||||||
i_i32_ctz = 0x68,
|
|
||||||
i_i32_popcnt = 0x69,
|
|
||||||
|
|
||||||
i_i32_add = 0x6A,
|
|
||||||
i_i32_sub = 0x6B,
|
|
||||||
i_i32_mul = 0x6C,
|
|
||||||
i_i32_div_s = 0x6D,
|
|
||||||
i_i32_div_u = 0x6E,
|
|
||||||
i_i32_rem_s = 0x6F,
|
|
||||||
i_i32_rem_u = 0x70,
|
|
||||||
i_i32_and = 0x71,
|
|
||||||
i_i32_or = 0x72,
|
|
||||||
i_i32_xor = 0x73,
|
|
||||||
i_i32_shl = 0x74,
|
|
||||||
i_i32_shr_s = 0x75,
|
|
||||||
i_i32_shr_u = 0x76,
|
|
||||||
i_i32_rotl = 0x77,
|
|
||||||
i_i32_rotr = 0x78,
|
|
||||||
|
|
||||||
i_i64_clz = 0x79,
|
|
||||||
i_i64_ctz = 0x7A,
|
|
||||||
i_i64_popcnt = 0x7B,
|
|
||||||
i_i64_add = 0x7C,
|
|
||||||
i_i64_sub = 0x7D,
|
|
||||||
i_i64_mul = 0x7E,
|
|
||||||
i_i64_div_s = 0x7F,
|
|
||||||
i_i64_div_u = 0x80,
|
|
||||||
i_i64_rem_s = 0x81,
|
|
||||||
i_i64_rem_u = 0x82,
|
|
||||||
i_i64_and = 0x83,
|
|
||||||
i_i64_or = 0x84,
|
|
||||||
i_i64_xor = 0x85,
|
|
||||||
i_i64_shl = 0x86,
|
|
||||||
i_i64_shr_s = 0x87,
|
|
||||||
i_i64_shr_u = 0x88,
|
|
||||||
i_i64_rotl = 0x89,
|
|
||||||
i_i64_rotr = 0x8A,
|
|
||||||
|
|
||||||
i_f32_abs = 0x8b,
|
|
||||||
i_f32_neg = 0x8c,
|
|
||||||
i_f32_ceil = 0x8d,
|
|
||||||
i_f32_floor = 0x8e,
|
|
||||||
i_f32_trunc = 0x8f,
|
|
||||||
i_f32_nearest = 0x90,
|
|
||||||
i_f32_sqrt = 0x91,
|
|
||||||
i_f32_add = 0x92,
|
|
||||||
i_f32_sub = 0x93,
|
|
||||||
i_f32_mul = 0x94,
|
|
||||||
i_f32_div = 0x95,
|
|
||||||
i_f32_min = 0x96,
|
|
||||||
i_f32_max = 0x97,
|
|
||||||
i_f32_copysign = 0x98,
|
|
||||||
|
|
||||||
i_f64_abs = 0x99,
|
|
||||||
i_f64_neg = 0x9a,
|
|
||||||
i_f64_ceil = 0x9b,
|
|
||||||
i_f64_floor = 0x9c,
|
|
||||||
i_f64_trunc = 0x9d,
|
|
||||||
i_f64_nearest = 0x9e,
|
|
||||||
i_f64_sqrt = 0x9f,
|
|
||||||
i_f64_add = 0xa0,
|
|
||||||
i_f64_sub = 0xa1,
|
|
||||||
i_f64_mul = 0xa2,
|
|
||||||
i_f64_div = 0xa3,
|
|
||||||
i_f64_min = 0xa4,
|
|
||||||
i_f64_max = 0xa5,
|
|
||||||
i_f64_copysign = 0xa6,
|
|
||||||
|
|
||||||
i_i32_wrap_i64 = 0xa7,
|
|
||||||
i_i32_trunc_f32_s = 0xa8,
|
|
||||||
i_i32_trunc_f32_u = 0xa9,
|
|
||||||
i_i32_trunc_f64_s = 0xaa,
|
|
||||||
i_i32_trunc_f64_u = 0xab,
|
|
||||||
i_i64_extend_i32_s = 0xac,
|
|
||||||
i_i64_extend_i32_u = 0xad,
|
|
||||||
i_i64_trunc_f32_s = 0xae,
|
|
||||||
i_i64_trunc_f32_u = 0xaf,
|
|
||||||
i_i64_trunc_f64_s = 0xb0,
|
|
||||||
i_i64_trunc_f64_u = 0xb1,
|
|
||||||
i_f32_convert_i32_s = 0xb2,
|
|
||||||
i_f32_convert_i32_u = 0xb3,
|
|
||||||
i_f32_convert_i64_s = 0xb4,
|
|
||||||
i_f32_convert_i64_u = 0xb5,
|
|
||||||
i_f32_demote_f64 = 0xb6,
|
|
||||||
i_f64_convert_i32_s = 0xb7,
|
|
||||||
i_f64_convert_i32_u = 0xb8,
|
|
||||||
i_f64_convert_i64_s = 0xb9,
|
|
||||||
i_f64_convert_i64_u = 0xba,
|
|
||||||
i_f64_promote_f32 = 0xbb,
|
|
||||||
|
|
||||||
i_i32_reinterpret_f32 = 0xbc,
|
|
||||||
i_i64_reinterpret_f64 = 0xbd,
|
|
||||||
i_f32_reinterpret_i32 = 0xbe,
|
|
||||||
i_f64_reinterpret_i64 = 0xbf,
|
|
||||||
|
|
||||||
i_i32_extend8_s = 0xc0,
|
|
||||||
i_i32_extend16_s = 0xc1,
|
|
||||||
i_i64_extend8_s = 0xc2,
|
|
||||||
i_i64_extend16_s = 0xc3,
|
|
||||||
i_i64_extend32_s = 0xc4,
|
|
||||||
|
|
||||||
i_ref_null = 0xD0,
|
|
||||||
i_ref_is_null = 0xD1,
|
|
||||||
i_ref_func = 0xD2,
|
|
||||||
|
|
||||||
i_bulk_op = 0xFC,
|
|
||||||
/* TODO: more instrs */
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
enum blocktype_tag {
|
|
||||||
blocktype_empty,
|
|
||||||
blocktype_valtype,
|
|
||||||
blocktype_index,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct blocktype {
|
|
||||||
enum blocktype_tag tag;
|
|
||||||
union {
|
|
||||||
enum valtype valtype;
|
|
||||||
int type_index;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct instrs {
|
|
||||||
unsigned char *data;
|
|
||||||
u32 len;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct block {
|
|
||||||
struct blocktype type;
|
|
||||||
struct expr instrs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct memarg {
|
|
||||||
u32 offset;
|
|
||||||
u32 align;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct br_table {
|
|
||||||
u32 num_label_indices;
|
|
||||||
u32 label_indices[512];
|
|
||||||
u32 default_label;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct call_indirect {
|
|
||||||
u32 tableidx;
|
|
||||||
u32 typeidx;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct table_init {
|
|
||||||
u32 tableidx;
|
|
||||||
u32 elemidx;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct table_copy {
|
|
||||||
u32 from;
|
|
||||||
u32 to;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bulk_op {
|
|
||||||
enum bulk_tag tag;
|
|
||||||
union {
|
|
||||||
struct table_init table_init;
|
|
||||||
struct table_copy table_copy;
|
|
||||||
u32 idx;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct select_instr {
|
|
||||||
u8 *valtypes;
|
|
||||||
u32 num_valtypes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct instr {
|
|
||||||
enum instr_tag tag;
|
|
||||||
int pos;
|
|
||||||
union {
|
|
||||||
struct br_table br_table;
|
|
||||||
struct bulk_op bulk_op;
|
|
||||||
struct call_indirect call_indirect;
|
|
||||||
struct memarg memarg;
|
|
||||||
struct select_instr select;
|
|
||||||
struct block block;
|
|
||||||
struct expr else_block;
|
|
||||||
double f64;
|
|
||||||
float f32;
|
|
||||||
int i32;
|
|
||||||
u32 u32;
|
|
||||||
int64_t i64;
|
|
||||||
u64 u64;
|
|
||||||
unsigned char memidx;
|
|
||||||
enum reftype reftype;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
enum datamode {
|
|
||||||
datamode_active,
|
|
||||||
datamode_passive,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wdata_active {
|
|
||||||
u32 mem_index;
|
|
||||||
struct expr offset_expr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wdata {
|
|
||||||
struct wdata_active active;
|
|
||||||
u8 *bytes;
|
|
||||||
u32 bytes_len;
|
|
||||||
enum datamode mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct datasec {
|
|
||||||
struct wdata *datas;
|
|
||||||
u32 num_datas;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct startsec {
|
|
||||||
u32 start_fn;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct module {
|
|
||||||
unsigned int parsed;
|
|
||||||
unsigned int custom_sections;
|
|
||||||
|
|
||||||
struct func *funcs;
|
|
||||||
|
|
||||||
u32 num_funcs;
|
|
||||||
|
|
||||||
struct customsec custom_section[MAX_CUSTOM_SECTIONS];
|
|
||||||
struct typesec type_section;
|
|
||||||
struct funcsec func_section;
|
|
||||||
struct importsec import_section;
|
|
||||||
struct exportsec export_section;
|
|
||||||
struct codesec code_section;
|
|
||||||
struct tablesec table_section;
|
|
||||||
struct memsec memory_section;
|
|
||||||
struct globalsec global_section;
|
|
||||||
struct startsec start_section;
|
|
||||||
struct elemsec element_section;
|
|
||||||
struct datasec data_section;
|
|
||||||
struct namesec name_section;
|
|
||||||
};
|
|
||||||
|
|
||||||
// make sure the struct is packed so that
|
|
||||||
struct label {
|
|
||||||
u32 instr_pos; // resolved status is stored in HOB of pos
|
|
||||||
u32 jump;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct callframe {
|
|
||||||
struct cursor code;
|
|
||||||
struct val *locals;
|
|
||||||
struct func *func;
|
|
||||||
u16 prev_stack_items;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct resolver {
|
|
||||||
u16 label;
|
|
||||||
u8 end_tag;
|
|
||||||
u8 start_tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct global_inst {
|
|
||||||
struct val val;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct module_inst {
|
|
||||||
struct table_inst *tables;
|
|
||||||
struct global_inst *globals;
|
|
||||||
struct elem_inst *elements;
|
|
||||||
|
|
||||||
u32 num_tables;
|
|
||||||
u32 num_globals;
|
|
||||||
u32 num_elements;
|
|
||||||
|
|
||||||
int start_fn;
|
|
||||||
unsigned char *globals_init;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wasi {
|
|
||||||
int argc;
|
|
||||||
const char **argv;
|
|
||||||
|
|
||||||
int environc;
|
|
||||||
const char **environ;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wasm_interp;
|
|
||||||
|
|
||||||
struct builtin {
|
|
||||||
const char *name;
|
|
||||||
int (*fn)(struct wasm_interp *);
|
|
||||||
int (*prepare_args)(struct wasm_interp *);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wasm_interp {
|
|
||||||
struct module *module;
|
|
||||||
struct module_inst module_inst;
|
|
||||||
struct wasi wasi;
|
|
||||||
void *context;
|
|
||||||
|
|
||||||
struct builtin builtins[MAX_BUILTINS];
|
|
||||||
int num_builtins;
|
|
||||||
|
|
||||||
int prev_resolvers, quitting;
|
|
||||||
|
|
||||||
struct errors errors; /* struct error */
|
|
||||||
size_t ops;
|
|
||||||
|
|
||||||
struct cursor callframes; /* struct callframe */
|
|
||||||
struct cursor stack; /* struct val */
|
|
||||||
struct cursor mem; /* u8/mixed */
|
|
||||||
|
|
||||||
struct cursor memory; /* memory pages (65536 blocks) */
|
|
||||||
|
|
||||||
struct cursor locals; /* struct val */
|
|
||||||
struct cursor labels; /* struct labels */
|
|
||||||
struct cursor num_labels;
|
|
||||||
|
|
||||||
// resolve stack for the current function. every time a control
|
|
||||||
// instruction is encountered, the label index is pushed. When an
|
|
||||||
// instruction is popped, we can resolve the label
|
|
||||||
struct cursor resolver_stack; /* struct resolver */
|
|
||||||
struct cursor resolver_offsets; /* int */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wasm_parser {
|
|
||||||
struct module module;
|
|
||||||
struct builtin *builtins;
|
|
||||||
u32 num_builtins;
|
|
||||||
struct cursor cur;
|
|
||||||
struct cursor mem;
|
|
||||||
struct errors errs;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
int run_wasm(unsigned char *wasm, unsigned long len, int argc, const char **argv, char **env, int *retval);
|
|
||||||
int parse_wasm(struct wasm_parser *p);
|
|
||||||
int wasm_interp_init(struct wasm_interp *interp, struct module *module);
|
|
||||||
void wasm_parser_free(struct wasm_parser *parser);
|
|
||||||
void wasm_parser_init(struct wasm_parser *p, u8 *wasm, size_t wasm_len, size_t arena_size, struct builtin *, int num_builtins);
|
|
||||||
void wasm_interp_free(struct wasm_interp *interp);
|
|
||||||
int interp_wasm_module(struct wasm_interp *interp, int *retval);
|
|
||||||
int interp_wasm_module_resume(struct wasm_interp *interp, int *retval);
|
|
||||||
void print_error_backtrace(struct errors *errors);
|
|
||||||
void setup_wasi(struct wasm_interp *interp, int argc, const char **argv, char **env);
|
|
||||||
void print_callstack(struct wasm_interp *interp);
|
|
||||||
|
|
||||||
// builtin helpers
|
|
||||||
int get_params(struct wasm_interp *interp, struct val** vals, u32 num_vals);
|
|
||||||
int get_var_params(struct wasm_interp *interp, struct val** vals, u32 *num_vals);
|
|
||||||
u8 *interp_mem_ptr(struct wasm_interp *interp, u32 ptr, int size);
|
|
||||||
|
|
||||||
static INLINE struct callframe *top_callframe(struct cursor *cur)
|
|
||||||
{
|
|
||||||
return (struct callframe*)cursor_top(cur, sizeof(struct callframe));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static INLINE struct cursor *interp_codeptr(struct wasm_interp *interp)
|
|
||||||
{
|
|
||||||
struct callframe *frame;
|
|
||||||
if (unlikely(!(frame = top_callframe(&interp->callframes))))
|
|
||||||
return 0;
|
|
||||||
return &frame->code;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static INLINE int mem_ptr_str(struct wasm_interp *interp, u32 ptr,
|
|
||||||
const char **str)
|
|
||||||
{
|
|
||||||
// still technically unsafe if the string runs over the end of memory...
|
|
||||||
if (!(*str = (const char*)interp_mem_ptr(interp, ptr, 1))) {
|
|
||||||
return interp_error(interp, "int memptr");
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE int mem_ptr_i32(struct wasm_interp *interp, u32 ptr, int **i)
|
|
||||||
{
|
|
||||||
if (!(*i = (int*)interp_mem_ptr(interp, ptr, sizeof(int))))
|
|
||||||
return interp_error(interp, "int memptr");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE int cursor_pushval(struct cursor *cur, struct val *val)
|
|
||||||
{
|
|
||||||
return cursor_push(cur, (u8*)val, sizeof(*val));
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE int cursor_push_i32(struct cursor *stack, int i)
|
|
||||||
{
|
|
||||||
struct val val;
|
|
||||||
val.type = val_i32;
|
|
||||||
val.num.i32 = i;
|
|
||||||
|
|
||||||
return cursor_pushval(stack, &val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE int stack_push_i32(struct wasm_interp *interp, int i)
|
|
||||||
{
|
|
||||||
return cursor_push_i32(&interp->stack, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE struct callframe *top_callframes(struct cursor *cur, int top)
|
|
||||||
{
|
|
||||||
return (struct callframe*)cursor_topn(cur, sizeof(struct callframe), top);
|
|
||||||
}
|
|
||||||
|
|
||||||
static INLINE int was_section_parsed(struct module *module,
|
|
||||||
enum section_tag section)
|
|
||||||
{
|
|
||||||
if (section == section_custom)
|
|
||||||
return module->custom_sections > 0;
|
|
||||||
|
|
||||||
return module->parsed & (1 << section);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* PROTOVERSE_WASM_H */
|
|
||||||
+35
-226
@@ -13,30 +13,26 @@
|
|||||||
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||||
3A23838E2A297DD200E5AA2E /* ZapButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A23838D2A297DD200E5AA2E /* ZapButtonModel.swift */; };
|
3A23838E2A297DD200E5AA2E /* ZapButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A23838D2A297DD200E5AA2E /* ZapButtonModel.swift */; };
|
||||||
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */; };
|
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */; };
|
||||||
|
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040EE29A8FEE9008A0F29 /* EventDetailBarTests.swift */; };
|
||||||
3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */; };
|
3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */; };
|
||||||
3A3040F329A91366008A0F29 /* ProfileViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040F229A91366008A0F29 /* ProfileViewTests.swift */; };
|
3A3040F329A91366008A0F29 /* ProfileViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040F229A91366008A0F29 /* ProfileViewTests.swift */; };
|
||||||
3A30410129AB12AA008A0F29 /* EventGroupViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A30410029AB12AA008A0F29 /* EventGroupViewTests.swift */; };
|
3A30410129AB12AA008A0F29 /* EventGroupViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A30410029AB12AA008A0F29 /* EventGroupViewTests.swift */; };
|
||||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
||||||
3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4647CE2A413ADC00386AD8 /* CondensedProfilePicturesView.swift */; };
|
3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4647CE2A413ADC00386AD8 /* CondensedProfilePicturesView.swift */; };
|
||||||
3A48E7B029DFBE9D006E787E /* MutedThreadsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */; };
|
3A48E7B029DFBE9D006E787E /* MutedThreadsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */; };
|
||||||
3A5E47C52A4A6CF400C0D090 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A5E47C42A4A6CF400C0D090 /* Trie.swift */; };
|
|
||||||
3A5E47C72A4A76C800C0D090 /* TrieTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A5E47C62A4A76C800C0D090 /* TrieTests.swift */; };
|
|
||||||
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */; };
|
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */; };
|
||||||
3A90B1812A4EA3AF00000D94 /* UserSearchCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A90B1802A4EA3AF00000D94 /* UserSearchCache.swift */; };
|
|
||||||
3A90B1832A4EA3C600000D94 /* UserSearchCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A90B1822A4EA3C600000D94 /* UserSearchCacheTests.swift */; };
|
|
||||||
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */; };
|
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */; };
|
||||||
3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FE297E3D900090C62D /* RepostsView.swift */; };
|
3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FE297E3D900090C62D /* RepostsView.swift */; };
|
||||||
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA24801297E3DC20090C62D /* RepostView.swift */; };
|
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA24801297E3DC20090C62D /* RepostView.swift */; };
|
||||||
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA59D1C2999B0400061C48E /* DraftsModel.swift */; };
|
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA59D1C2999B0400061C48E /* DraftsModel.swift */; };
|
||||||
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */; };
|
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */; };
|
||||||
3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */; };
|
3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */; };
|
||||||
3AAC7A022A60FE72002B50DF /* LocalizationUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAC7A012A60FE72002B50DF /* LocalizationUtilTests.swift */; };
|
|
||||||
3AAC7A042A626A75002B50DF /* CarouselDotsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAC7A032A626A75002B50DF /* CarouselDotsView.swift */; };
|
|
||||||
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB72AB8298ECF30004BB58C /* Translator.swift */; };
|
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB72AB8298ECF30004BB58C /* Translator.swift */; };
|
||||||
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; };
|
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; };
|
||||||
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
||||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
||||||
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
|
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
|
||||||
|
3AFBF3FD29FDA7CC00E79C7C /* CustomZapViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AFBF3FC29FDA7CC00E79C7C /* CustomZapViewTests.swift */; };
|
||||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
||||||
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
|
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
|
||||||
@@ -44,17 +40,11 @@
|
|||||||
4C06670E28FDEAA000038D2A /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670D28FDEAA000038D2A /* utf8.c */; };
|
4C06670E28FDEAA000038D2A /* utf8.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670D28FDEAA000038D2A /* utf8.c */; };
|
||||||
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
|
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
|
||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
||||||
4C0C03992A61E27B0098B3B8 /* primal.wasm in Resources */ = {isa = PBXBuildFile; fileRef = 4C0C03972A61E27B0098B3B8 /* primal.wasm */; };
|
|
||||||
4C0C039A2A61E27B0098B3B8 /* bool_setting.wasm in Resources */ = {isa = PBXBuildFile; fileRef = 4C0C03982A61E27B0098B3B8 /* bool_setting.wasm */; };
|
|
||||||
4C190F202A535FC200027FD5 /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */; };
|
|
||||||
4C190F252A547D2000027FD5 /* LoadScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C190F242A547D2000027FD5 /* LoadScript.swift */; };
|
|
||||||
4C198DEF29F88C6B004C165C /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DEB29F88C6B004C165C /* BlurHashEncode.swift */; };
|
4C198DEF29F88C6B004C165C /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DEB29F88C6B004C165C /* BlurHashEncode.swift */; };
|
||||||
4C198DF029F88C6B004C165C /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = 4C198DEC29F88C6B004C165C /* Readme.md */; };
|
4C198DF029F88C6B004C165C /* Readme.md in Resources */ = {isa = PBXBuildFile; fileRef = 4C198DEC29F88C6B004C165C /* Readme.md */; };
|
||||||
4C198DF129F88C6B004C165C /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4C198DED29F88C6B004C165C /* License.txt */; };
|
4C198DF129F88C6B004C165C /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4C198DED29F88C6B004C165C /* License.txt */; };
|
||||||
4C198DF229F88C6B004C165C /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DEE29F88C6B004C165C /* BlurHashDecode.swift */; };
|
4C198DF229F88C6B004C165C /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DEE29F88C6B004C165C /* BlurHashDecode.swift */; };
|
||||||
4C198DF529F88D2E004C165C /* ImageMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DF429F88D2E004C165C /* ImageMetadata.swift */; };
|
4C198DF529F88D2E004C165C /* ImageMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C198DF429F88D2E004C165C /* ImageMetadata.swift */; };
|
||||||
4C19AE512A5CEF7C00C90DB7 /* NostrScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C19AE4C2A5CEF7C00C90DB7 /* NostrScript.swift */; };
|
|
||||||
4C19AE552A5D977400C90DB7 /* HashtagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C19AE542A5D977400C90DB7 /* HashtagTests.swift */; };
|
|
||||||
4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */; };
|
4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */; };
|
||||||
4C1A9A1D29DDCF9B00516EAC /* NotificationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.swift */; };
|
4C1A9A1D29DDCF9B00516EAC /* NotificationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.swift */; };
|
||||||
4C1A9A1F29DDD24B00516EAC /* AppearanceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1E29DDD24B00516EAC /* AppearanceSettingsView.swift */; };
|
4C1A9A1F29DDD24B00516EAC /* AppearanceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A1E29DDD24B00516EAC /* AppearanceSettingsView.swift */; };
|
||||||
@@ -129,7 +119,6 @@
|
|||||||
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */; };
|
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */; };
|
||||||
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */; };
|
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */; };
|
||||||
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C42812B298C848200DBF26F /* TranslateView.swift */; };
|
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C42812B298C848200DBF26F /* TranslateView.swift */; };
|
||||||
4C4F14A72A2A61A30045A0B9 /* NostrScriptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4F14A62A2A61A30045A0B9 /* NostrScriptTests.swift */; };
|
|
||||||
4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0629A540BA003E4487 /* NotificationsModel.swift */; };
|
4C54AA0729A540BA003E4487 /* NotificationsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0629A540BA003E4487 /* NotificationsModel.swift */; };
|
||||||
4C54AA0A29A55429003E4487 /* EventGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0929A55429003E4487 /* EventGroup.swift */; };
|
4C54AA0A29A55429003E4487 /* EventGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0929A55429003E4487 /* EventGroup.swift */; };
|
||||||
4C54AA0C29A5543C003E4487 /* ZapGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0B29A5543C003E4487 /* ZapGroup.swift */; };
|
4C54AA0C29A5543C003E4487 /* ZapGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C54AA0B29A5543C003E4487 /* ZapGroup.swift */; };
|
||||||
@@ -143,9 +132,6 @@
|
|||||||
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */; };
|
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */; };
|
||||||
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */; };
|
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */; };
|
||||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */ = {isa = PBXBuildFile; productRef = 4C649880286E0EE300EAE2B3 /* secp256k1 */; };
|
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */ = {isa = PBXBuildFile; productRef = 4C649880286E0EE300EAE2B3 /* secp256k1 */; };
|
||||||
4C687C212A5F7ED00092C550 /* DamusBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C202A5F7ED00092C550 /* DamusBackground.swift */; };
|
|
||||||
4C687C242A5FA86D0092C550 /* SearchHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */; };
|
|
||||||
4C687C272A6039500092C550 /* TestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C262A6039500092C550 /* TestData.swift */; };
|
|
||||||
4C73C5142A4437C10062CAC0 /* ZapUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C5132A4437C10062CAC0 /* ZapUserView.swift */; };
|
4C73C5142A4437C10062CAC0 /* ZapUserView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C5132A4437C10062CAC0 /* ZapUserView.swift */; };
|
||||||
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
|
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
|
||||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA527FF87A20006080F /* Nostr.swift */; };
|
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA527FF87A20006080F /* Nostr.swift */; };
|
||||||
@@ -184,10 +170,8 @@
|
|||||||
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD17283A9EE5008EE7EF /* LoginView.swift */; };
|
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD17283A9EE5008EE7EF /* LoginView.swift */; };
|
||||||
4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD19283AA67F008EE7EF /* Bech32.swift */; };
|
4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD19283AA67F008EE7EF /* Bech32.swift */; };
|
||||||
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; };
|
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */; };
|
||||||
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9276E2A2A5D110098A105 /* wasm.c */; };
|
|
||||||
4C9146FE2A2A87C200DDEA40 /* nostrscript.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C4F14A92A2A71AB0045A0B9 /* nostrscript.c */; };
|
|
||||||
4C9147002A2A891E00DDEA40 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C9146FF2A2A891E00DDEA40 /* error.c */; };
|
|
||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
|
||||||
|
4C9AA1482A44442E003F49FD /* CustomizeZapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */; };
|
||||||
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */; };
|
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */; };
|
||||||
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; };
|
4C9BB83129C0ED4F00FC4E37 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; };
|
||||||
4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; };
|
4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */; };
|
||||||
@@ -196,14 +180,6 @@
|
|||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
||||||
4CA3FA1029F593D000FDB3C3 /* ZapTypePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */; };
|
4CA3FA1029F593D000FDB3C3 /* ZapTypePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */; };
|
||||||
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5588229F33F5B00DC6A45 /* StringCodable.swift */; };
|
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA5588229F33F5B00DC6A45 /* StringCodable.swift */; };
|
||||||
4CA9275D2A28FF630098A105 /* LongformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9275C2A28FF630098A105 /* LongformView.swift */; };
|
|
||||||
4CA9275F2A2902B20098A105 /* LongformPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9275E2A2902B20098A105 /* LongformPreview.swift */; };
|
|
||||||
4CA927612A290E340098A105 /* EventShell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927602A290E340098A105 /* EventShell.swift */; };
|
|
||||||
4CA927632A290EB10098A105 /* EventTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927622A290EB10098A105 /* EventTop.swift */; };
|
|
||||||
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927642A290F1A0098A105 /* TimeDot.swift */; };
|
|
||||||
4CA927672A290F8B0098A105 /* RelativeTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927662A290F8B0098A105 /* RelativeTime.swift */; };
|
|
||||||
4CA9276A2A290FC00098A105 /* ContextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927692A290FC00098A105 /* ContextButton.swift */; };
|
|
||||||
4CA9276C2A2910D10098A105 /* ReplyPart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9276B2A2910D10098A105 /* ReplyPart.swift */; };
|
|
||||||
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
|
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
|
||||||
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; };
|
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; };
|
||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
||||||
@@ -246,6 +222,7 @@
|
|||||||
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128929E9D10C0006FA5A /* SignalView.swift */; };
|
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128929E9D10C0006FA5A /* SignalView.swift */; };
|
||||||
4CDA128C29EB19C40006FA5A /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128B29EB19C40006FA5A /* LocalNotification.swift */; };
|
4CDA128C29EB19C40006FA5A /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128B29EB19C40006FA5A /* LocalNotification.swift */; };
|
||||||
4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */; };
|
4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */; };
|
||||||
|
4CE0E2B229A3DF6900DB4CA2 /* LoadMoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2B129A3DF6900DB4CA2 /* LoadMoreButton.swift */; };
|
||||||
4CE0E2B629A3ED5500DB4CA2 /* InnerTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */; };
|
4CE0E2B629A3ED5500DB4CA2 /* InnerTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */; };
|
||||||
4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE1398F29F0661A00AC6A0B /* RepostAction.swift */; };
|
4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE1398F29F0661A00AC6A0B /* RepostAction.swift */; };
|
||||||
4CE1399229F0666100AC6A0B /* ShareActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE1399129F0666100AC6A0B /* ShareActionButton.swift */; };
|
4CE1399229F0666100AC6A0B /* ShareActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE1399129F0666100AC6A0B /* ShareActionButton.swift */; };
|
||||||
@@ -265,6 +242,7 @@
|
|||||||
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
||||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
||||||
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
|
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
|
||||||
|
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */; };
|
||||||
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794D2996B16A00F758CC /* RelayToggle.swift */; };
|
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794D2996B16A00F758CC /* RelayToggle.swift */; };
|
||||||
4CE879502996B2BD00F758CC /* RelayStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794F2996B2BD00F758CC /* RelayStatusView.swift */; };
|
4CE879502996B2BD00F758CC /* RelayStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794F2996B2BD00F758CC /* RelayStatusView.swift */; };
|
||||||
4CE879522996B68900F758CC /* RelayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879512996B68900F758CC /* RelayType.swift */; };
|
4CE879522996B68900F758CC /* RelayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879512996B68900F758CC /* RelayType.swift */; };
|
||||||
@@ -290,7 +268,6 @@
|
|||||||
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
||||||
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
||||||
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
||||||
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFD502E2A2DA45800A229DB /* MediaView.swift */; };
|
|
||||||
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
4CFF8F6329CC9AD7008DB934 /* ImageContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */; };
|
||||||
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
|
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6629CC9E3A008DB934 /* ImageView.swift */; };
|
||||||
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
4CFF8F6929CC9ED1008DB934 /* ImageContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */; };
|
||||||
@@ -304,11 +281,7 @@
|
|||||||
501F8C5A29FF70F5001AFC1D /* ProfileDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C5929FF70F5001AFC1D /* ProfileDatabase.swift */; };
|
501F8C5A29FF70F5001AFC1D /* ProfileDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C5929FF70F5001AFC1D /* ProfileDatabase.swift */; };
|
||||||
501F8C802A0220E1001AFC1D /* KeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */; };
|
501F8C802A0220E1001AFC1D /* KeychainStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */; };
|
||||||
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */; };
|
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */; };
|
||||||
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A62A34915F006AE6DC /* RelayModel.swift */; };
|
|
||||||
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504323A82A3495B6006AE6DC /* RelayModelCache.swift */; };
|
|
||||||
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
|
|
||||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A60D132A28BEEE00186190 /* RelayLog.swift */; };
|
|
||||||
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
|
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
|
||||||
50DA11262A16A23F00236234 /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50DA11252A16A23F00236234 /* Launch.storyboard */; };
|
50DA11262A16A23F00236234 /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50DA11252A16A23F00236234 /* Launch.storyboard */; };
|
||||||
5C0707D12A1ECB38004E7B51 /* DamusLogoGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */; };
|
5C0707D12A1ECB38004E7B51 /* DamusLogoGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */; };
|
||||||
@@ -331,7 +304,6 @@
|
|||||||
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||||
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; };
|
|
||||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
||||||
E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; };
|
E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; };
|
||||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||||
@@ -341,7 +313,7 @@
|
|||||||
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BA12E29A18EF500E10810 /* BookmarksView.swift */; };
|
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BA12E29A18EF500E10810 /* BookmarksView.swift */; };
|
||||||
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E91298B0F0700AB113A /* RelayDetailView.swift */; };
|
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E91298B0F0700AB113A /* RelayDetailView.swift */; };
|
||||||
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */; };
|
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */; };
|
||||||
F79C7FAD29D5E9620000F946 /* EditPictureControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79C7FAC29D5E9620000F946 /* EditPictureControl.swift */; };
|
F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */; };
|
||||||
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
|
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
|
||||||
F7F0BA272978E54D009531F3 /* ParticipantsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA262978E54D009531F3 /* ParticipantsView.swift */; };
|
F7F0BA272978E54D009531F3 /* ParticipantsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA262978E54D009531F3 /* ParticipantsView.swift */; };
|
||||||
F944F56E29EA9CCC0067B3BF /* DamusParseContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F944F56D29EA9CCC0067B3BF /* DamusParseContentTests.swift */; };
|
F944F56E29EA9CCC0067B3BF /* DamusParseContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F944F56D29EA9CCC0067B3BF /* DamusParseContentTests.swift */; };
|
||||||
@@ -378,6 +350,7 @@
|
|||||||
3A25EF152992DA5D008ABE69 /* el-GR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "el-GR"; path = "el-GR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A25EF152992DA5D008ABE69 /* el-GR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "el-GR"; path = "el-GR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
3A2B8B0A296A8982009CC16D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "en-US"; path = "en-US.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A2B8B0A296A8982009CC16D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "en-US"; path = "en-US.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyDescriptionTests.swift; sourceTree = "<group>"; };
|
3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyDescriptionTests.swift; sourceTree = "<group>"; };
|
||||||
|
3A3040EE29A8FEE9008A0F29 /* EventDetailBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailBarTests.swift; sourceTree = "<group>"; };
|
||||||
3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationUtil.swift; sourceTree = "<group>"; };
|
3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationUtil.swift; sourceTree = "<group>"; };
|
||||||
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewTests.swift; sourceTree = "<group>"; };
|
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewTests.swift; sourceTree = "<group>"; };
|
||||||
3A3040F929A91ED6008A0F29 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A3040F929A91ED6008A0F29 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
@@ -403,8 +376,6 @@
|
|||||||
3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3A5CAE1E298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3A5CAE1E298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3A5CAE1F298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-CN"; path = "zh-CN.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A5CAE1F298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-CN"; path = "zh-CN.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
3A5E47C42A4A6CF400C0D090 /* Trie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trie.swift; sourceTree = "<group>"; };
|
|
||||||
3A5E47C62A4A76C800C0D090 /* TrieTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrieTests.swift; sourceTree = "<group>"; };
|
|
||||||
3A66D927299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
3A66D927299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
3A66D928299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
3A66D928299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
3A66D929299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
3A66D929299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
@@ -418,8 +389,6 @@
|
|||||||
3A8624DA299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
3A8624DA299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
3A8624DB299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
3A8624DB299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtil.swift; sourceTree = "<group>"; };
|
3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringUtil.swift; sourceTree = "<group>"; };
|
||||||
3A90B1802A4EA3AF00000D94 /* UserSearchCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserSearchCache.swift; sourceTree = "<group>"; };
|
|
||||||
3A90B1822A4EA3C600000D94 /* UserSearchCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearchCacheTests.swift; sourceTree = "<group>"; };
|
|
||||||
3A929C20297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A929C20297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3A929C21297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3A929C21297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3A929C22297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "it-IT"; path = "it-IT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A929C22297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "it-IT"; path = "it-IT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
@@ -441,15 +410,10 @@
|
|||||||
3AA5E70729B9E84A002701ED /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
3AA5E70729B9E84A002701ED /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3AAA95C9298DF87B00F3D526 /* TranslationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationService.swift; sourceTree = "<group>"; };
|
3AAA95C9298DF87B00F3D526 /* TranslationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationService.swift; sourceTree = "<group>"; };
|
||||||
3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLPlan.swift; sourceTree = "<group>"; };
|
3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLPlan.swift; sourceTree = "<group>"; };
|
||||||
3AAC7A012A60FE72002B50DF /* LocalizationUtilTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationUtilTests.swift; sourceTree = "<group>"; };
|
|
||||||
3AAC7A032A626A75002B50DF /* CarouselDotsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselDotsView.swift; sourceTree = "<group>"; };
|
|
||||||
3AB5B86A2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
3AB5B86A2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
3AB5B86B2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
3AB5B86B2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
3AB5B86C2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
3AB5B86C2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3AB72AB8298ECF30004BB58C /* Translator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Translator.swift; sourceTree = "<group>"; };
|
3AB72AB8298ECF30004BB58C /* Translator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Translator.swift; sourceTree = "<group>"; };
|
||||||
3ABACEBF2A5B3ED10037A847 /* sw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sw; path = sw.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
|
||||||
3ABACEC02A5B3ED10037A847 /* sw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sw; path = sw.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
|
||||||
3ABACEC12A5B3ED10037A847 /* sw */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sw; path = sw.lproj/Localizable.strings; sourceTree = "<group>"; };
|
|
||||||
3AC524EE298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
3AC524EE298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
3AC524EF298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
3AC524EF298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
3AC524F0298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
3AC524F0298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
@@ -478,6 +442,7 @@
|
|||||||
3AF6336829884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3AF6336829884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3AF6336929884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3AF6336929884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3AF6336A29884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3AF6336A29884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3AFBF3FC29FDA7CC00E79C7C /* CustomZapViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomZapViewTests.swift; sourceTree = "<group>"; };
|
||||||
4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; };
|
4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; };
|
||||||
4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; };
|
4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; };
|
||||||
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; };
|
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
@@ -487,19 +452,11 @@
|
|||||||
4C06670D28FDEAA000038D2A /* utf8.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utf8.c; sourceTree = "<group>"; };
|
4C06670D28FDEAA000038D2A /* utf8.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utf8.c; sourceTree = "<group>"; };
|
||||||
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
||||||
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
||||||
4C0C03972A61E27B0098B3B8 /* primal.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = primal.wasm; path = nostrscript/primal.wasm; sourceTree = SOURCE_ROOT; };
|
|
||||||
4C0C03982A61E27B0098B3B8 /* bool_setting.wasm */ = {isa = PBXFileReference; lastKnownFileType = file; name = bool_setting.wasm; path = nostrscript/bool_setting.wasm; sourceTree = SOURCE_ROOT; };
|
|
||||||
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>"; };
|
|
||||||
4C198DEB29F88C6B004C165C /* BlurHashEncode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
|
4C198DEB29F88C6B004C165C /* BlurHashEncode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = "<group>"; };
|
||||||
4C198DEC29F88C6B004C165C /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = "<group>"; };
|
4C198DEC29F88C6B004C165C /* Readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Readme.md; sourceTree = "<group>"; };
|
||||||
4C198DED29F88C6B004C165C /* License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
|
4C198DED29F88C6B004C165C /* License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
|
||||||
4C198DEE29F88C6B004C165C /* BlurHashDecode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
4C198DEE29F88C6B004C165C /* BlurHashDecode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlurHashDecode.swift; sourceTree = "<group>"; };
|
||||||
4C198DF429F88D2E004C165C /* ImageMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageMetadata.swift; sourceTree = "<group>"; };
|
4C198DF429F88D2E004C165C /* ImageMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageMetadata.swift; sourceTree = "<group>"; };
|
||||||
4C19AE4B2A5CEF7C00C90DB7 /* primal.ts */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.typescript; path = primal.ts; sourceTree = "<group>"; };
|
|
||||||
4C19AE4C2A5CEF7C00C90DB7 /* NostrScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NostrScript.swift; sourceTree = "<group>"; };
|
|
||||||
4C19AE502A5CEF7C00C90DB7 /* nostr.ts */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.typescript; path = nostr.ts; sourceTree = "<group>"; };
|
|
||||||
4C19AE542A5D977400C90DB7 /* HashtagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTests.swift; sourceTree = "<group>"; };
|
|
||||||
4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyCounter.swift; sourceTree = "<group>"; };
|
4C1A9A1929DCA17E00516EAC /* ReplyCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyCounter.swift; sourceTree = "<group>"; };
|
||||||
4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsView.swift; sourceTree = "<group>"; };
|
4C1A9A1C29DDCF9B00516EAC /* NotificationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsView.swift; sourceTree = "<group>"; };
|
||||||
4C1A9A1E29DDD24B00516EAC /* AppearanceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceSettingsView.swift; sourceTree = "<group>"; };
|
4C1A9A1E29DDD24B00516EAC /* AppearanceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceSettingsView.swift; sourceTree = "<group>"; };
|
||||||
@@ -604,9 +561,6 @@
|
|||||||
4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceView.swift; sourceTree = "<group>"; };
|
4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceView.swift; sourceTree = "<group>"; };
|
||||||
4C42812B298C848200DBF26F /* TranslateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslateView.swift; sourceTree = "<group>"; };
|
4C42812B298C848200DBF26F /* TranslateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslateView.swift; sourceTree = "<group>"; };
|
||||||
4C4A3A5A288A1B2200453788 /* damus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = damus.entitlements; sourceTree = "<group>"; };
|
4C4A3A5A288A1B2200453788 /* damus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = damus.entitlements; sourceTree = "<group>"; };
|
||||||
4C4F14A62A2A61A30045A0B9 /* NostrScriptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrScriptTests.swift; sourceTree = "<group>"; };
|
|
||||||
4C4F14A82A2A71AB0045A0B9 /* nostrscript.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = nostrscript.h; sourceTree = "<group>"; };
|
|
||||||
4C4F14A92A2A71AB0045A0B9 /* nostrscript.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nostrscript.c; sourceTree = "<group>"; };
|
|
||||||
4C54AA0629A540BA003E4487 /* NotificationsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsModel.swift; sourceTree = "<group>"; };
|
4C54AA0629A540BA003E4487 /* NotificationsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsModel.swift; sourceTree = "<group>"; };
|
||||||
4C54AA0929A55429003E4487 /* EventGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventGroup.swift; sourceTree = "<group>"; };
|
4C54AA0929A55429003E4487 /* EventGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventGroup.swift; sourceTree = "<group>"; };
|
||||||
4C54AA0B29A5543C003E4487 /* ZapGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapGroup.swift; sourceTree = "<group>"; };
|
4C54AA0B29A5543C003E4487 /* ZapGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapGroup.swift; sourceTree = "<group>"; };
|
||||||
@@ -619,9 +573,6 @@
|
|||||||
4C633351283D419F00B1C9C3 /* SignalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalModel.swift; sourceTree = "<group>"; };
|
4C633351283D419F00B1C9C3 /* SignalModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalModel.swift; sourceTree = "<group>"; };
|
||||||
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesView.swift; sourceTree = "<group>"; };
|
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesView.swift; sourceTree = "<group>"; };
|
||||||
4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesModel.swift; sourceTree = "<group>"; };
|
4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectMessagesModel.swift; sourceTree = "<group>"; };
|
||||||
4C687C202A5F7ED00092C550 /* DamusBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusBackground.swift; sourceTree = "<group>"; };
|
|
||||||
4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHeaderView.swift; sourceTree = "<group>"; };
|
|
||||||
4C687C262A6039500092C550 /* TestData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestData.swift; sourceTree = "<group>"; };
|
|
||||||
4C73C5132A4437C10062CAC0 /* ZapUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapUserView.swift; sourceTree = "<group>"; };
|
4C73C5132A4437C10062CAC0 /* ZapUserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapUserView.swift; sourceTree = "<group>"; };
|
||||||
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
|
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
|
||||||
4C75EFA527FF87A20006080F /* Nostr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Nostr.swift; sourceTree = "<group>"; };
|
4C75EFA527FF87A20006080F /* Nostr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Nostr.swift; sourceTree = "<group>"; };
|
||||||
@@ -665,8 +616,8 @@
|
|||||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
4C90BD17283A9EE5008EE7EF /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
||||||
4C90BD19283AA67F008EE7EF /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = "<group>"; };
|
4C90BD19283AA67F008EE7EF /* Bech32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32.swift; sourceTree = "<group>"; };
|
||||||
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; };
|
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Tests.swift; sourceTree = "<group>"; };
|
||||||
4C9146FF2A2A891E00DDEA40 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = "<group>"; };
|
|
||||||
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
|
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
|
||||||
|
4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeZapModel.swift; sourceTree = "<group>"; };
|
||||||
4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationStatusModel.swift; sourceTree = "<group>"; };
|
4C9AA1492A4587A6003F49FD /* NotificationStatusModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationStatusModel.swift; sourceTree = "<group>"; };
|
||||||
4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; };
|
4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayName.swift; sourceTree = "<group>"; };
|
||||||
4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; };
|
4C9BB83329C12D9900FC4E37 /* EventProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfileName.swift; sourceTree = "<group>"; };
|
||||||
@@ -675,21 +626,6 @@
|
|||||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
||||||
4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapTypePicker.swift; sourceTree = "<group>"; };
|
4CA3FA0F29F593D000FDB3C3 /* ZapTypePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapTypePicker.swift; sourceTree = "<group>"; };
|
||||||
4CA5588229F33F5B00DC6A45 /* StringCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringCodable.swift; sourceTree = "<group>"; };
|
4CA5588229F33F5B00DC6A45 /* StringCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringCodable.swift; sourceTree = "<group>"; };
|
||||||
4CA9275C2A28FF630098A105 /* LongformView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformView.swift; sourceTree = "<group>"; };
|
|
||||||
4CA9275E2A2902B20098A105 /* LongformPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformPreview.swift; sourceTree = "<group>"; };
|
|
||||||
4CA927602A290E340098A105 /* EventShell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventShell.swift; sourceTree = "<group>"; };
|
|
||||||
4CA927622A290EB10098A105 /* EventTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTop.swift; sourceTree = "<group>"; };
|
|
||||||
4CA927642A290F1A0098A105 /* TimeDot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeDot.swift; sourceTree = "<group>"; };
|
|
||||||
4CA927662A290F8B0098A105 /* RelativeTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelativeTime.swift; sourceTree = "<group>"; };
|
|
||||||
4CA927692A290FC00098A105 /* ContextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextButton.swift; sourceTree = "<group>"; };
|
|
||||||
4CA9276B2A2910D10098A105 /* ReplyPart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyPart.swift; sourceTree = "<group>"; };
|
|
||||||
4CA9276D2A2A5D110098A105 /* wasm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wasm.h; sourceTree = "<group>"; };
|
|
||||||
4CA9276E2A2A5D110098A105 /* wasm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wasm.c; sourceTree = "<group>"; };
|
|
||||||
4CA9276F2A2A5D470098A105 /* parser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parser.h; sourceTree = "<group>"; };
|
|
||||||
4CA927702A2A5D470098A105 /* debug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = "<group>"; };
|
|
||||||
4CA927712A2A5D480098A105 /* error.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = "<group>"; };
|
|
||||||
4CA927742A2A5E2F0098A105 /* varint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = varint.h; sourceTree = "<group>"; };
|
|
||||||
4CA927752A2A5E2F0098A105 /* typedefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = typedefs.h; sourceTree = "<group>"; };
|
|
||||||
4CAAD8AC298851D000060CEA /* AccountDeletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletion.swift; sourceTree = "<group>"; };
|
4CAAD8AC298851D000060CEA /* AccountDeletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletion.swift; sourceTree = "<group>"; };
|
||||||
4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConfigView.swift; sourceTree = "<group>"; };
|
4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConfigView.swift; sourceTree = "<group>"; };
|
||||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
||||||
@@ -731,6 +667,7 @@
|
|||||||
4CDA128929E9D10C0006FA5A /* SignalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalView.swift; sourceTree = "<group>"; };
|
4CDA128929E9D10C0006FA5A /* SignalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalView.swift; sourceTree = "<group>"; };
|
||||||
4CDA128B29EB19C40006FA5A /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; };
|
4CDA128B29EB19C40006FA5A /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; };
|
||||||
4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventHolder.swift; sourceTree = "<group>"; };
|
4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventHolder.swift; sourceTree = "<group>"; };
|
||||||
|
4CE0E2B129A3DF6900DB4CA2 /* LoadMoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreButton.swift; sourceTree = "<group>"; };
|
||||||
4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerTimelineView.swift; sourceTree = "<group>"; };
|
4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerTimelineView.swift; sourceTree = "<group>"; };
|
||||||
4CE1398F29F0661A00AC6A0B /* RepostAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostAction.swift; sourceTree = "<group>"; };
|
4CE1398F29F0661A00AC6A0B /* RepostAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostAction.swift; sourceTree = "<group>"; };
|
||||||
4CE1399129F0666100AC6A0B /* ShareActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareActionButton.swift; sourceTree = "<group>"; };
|
4CE1399129F0666100AC6A0B /* ShareActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareActionButton.swift; sourceTree = "<group>"; };
|
||||||
@@ -753,6 +690,7 @@
|
|||||||
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||||
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; };
|
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; };
|
||||||
4CE8794729941DA700F758CC /* RelayFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilters.swift; sourceTree = "<group>"; };
|
4CE8794729941DA700F758CC /* RelayFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilters.swift; sourceTree = "<group>"; };
|
||||||
|
4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayMetadatas.swift; sourceTree = "<group>"; };
|
||||||
4CE8794D2996B16A00F758CC /* RelayToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayToggle.swift; sourceTree = "<group>"; };
|
4CE8794D2996B16A00F758CC /* RelayToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayToggle.swift; sourceTree = "<group>"; };
|
||||||
4CE8794F2996B2BD00F758CC /* RelayStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayStatusView.swift; sourceTree = "<group>"; };
|
4CE8794F2996B2BD00F758CC /* RelayStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayStatusView.swift; sourceTree = "<group>"; };
|
||||||
4CE879512996B68900F758CC /* RelayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayType.swift; sourceTree = "<group>"; };
|
4CE879512996B68900F758CC /* RelayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayType.swift; sourceTree = "<group>"; };
|
||||||
@@ -779,7 +717,6 @@
|
|||||||
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
||||||
4CFD502E2A2DA45800A229DB /* MediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaView.swift; sourceTree = "<group>"; };
|
|
||||||
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
4CFF8F6229CC9AD7008DB934 /* ImageContextMenuModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContextMenuModifier.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageView.swift; sourceTree = "<group>"; };
|
||||||
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageContainerView.swift; sourceTree = "<group>"; };
|
||||||
@@ -793,11 +730,7 @@
|
|||||||
501F8C5929FF70F5001AFC1D /* ProfileDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDatabase.swift; sourceTree = "<group>"; };
|
501F8C5929FF70F5001AFC1D /* ProfileDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDatabase.swift; sourceTree = "<group>"; };
|
||||||
501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorage.swift; sourceTree = "<group>"; };
|
501F8C7F2A0220E1001AFC1D /* KeychainStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorage.swift; sourceTree = "<group>"; };
|
||||||
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorageTests.swift; sourceTree = "<group>"; };
|
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorageTests.swift; sourceTree = "<group>"; };
|
||||||
504323A62A34915F006AE6DC /* RelayModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModel.swift; sourceTree = "<group>"; };
|
|
||||||
504323A82A3495B6006AE6DC /* RelayModelCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayModelCache.swift; sourceTree = "<group>"; };
|
|
||||||
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
|
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
50A60D132A28BEEE00186190 /* RelayLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayLog.swift; sourceTree = "<group>"; };
|
|
||||||
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
|
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
|
||||||
50DA11252A16A23F00236234 /* Launch.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = "<group>"; };
|
50DA11252A16A23F00236234 /* Launch.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = "<group>"; };
|
||||||
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusLogoGradient.swift; sourceTree = "<group>"; };
|
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusLogoGradient.swift; sourceTree = "<group>"; };
|
||||||
@@ -820,7 +753,6 @@
|
|||||||
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = "<group>"; };
|
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = "<group>"; };
|
||||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||||
D2277EE92A089BD5006C3807 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
|
||||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
||||||
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
|
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = "<group>"; };
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
||||||
@@ -830,7 +762,7 @@
|
|||||||
F75BA12E29A18EF500E10810 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
|
F75BA12E29A18EF500E10810 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
|
||||||
F7908E91298B0F0700AB113A /* RelayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayDetailView.swift; sourceTree = "<group>"; };
|
F7908E91298B0F0700AB113A /* RelayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayDetailView.swift; sourceTree = "<group>"; };
|
||||||
F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIPURLBuilder.swift; sourceTree = "<group>"; };
|
F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIPURLBuilder.swift; sourceTree = "<group>"; };
|
||||||
F79C7FAC29D5E9620000F946 /* EditPictureControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditPictureControl.swift; sourceTree = "<group>"; };
|
F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfilePictureControl.swift; sourceTree = "<group>"; };
|
||||||
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
|
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
|
||||||
F7F0BA262978E54D009531F3 /* ParticipantsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantsView.swift; sourceTree = "<group>"; };
|
F7F0BA262978E54D009531F3 /* ParticipantsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParticipantsView.swift; sourceTree = "<group>"; };
|
||||||
F944F56D29EA9CCC0067B3BF /* DamusParseContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusParseContentTests.swift; sourceTree = "<group>"; };
|
F944F56D29EA9CCC0067B3BF /* DamusParseContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusParseContentTests.swift; sourceTree = "<group>"; };
|
||||||
@@ -885,16 +817,6 @@
|
|||||||
4C06670728FDE62900038D2A /* damus-c */ = {
|
4C06670728FDE62900038D2A /* damus-c */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4C9146FF2A2A891E00DDEA40 /* error.c */,
|
|
||||||
4CA927752A2A5E2F0098A105 /* typedefs.h */,
|
|
||||||
4CA927742A2A5E2F0098A105 /* varint.h */,
|
|
||||||
4CA927702A2A5D470098A105 /* debug.h */,
|
|
||||||
4CA927712A2A5D480098A105 /* error.h */,
|
|
||||||
4CA9276F2A2A5D470098A105 /* parser.h */,
|
|
||||||
4CA9276E2A2A5D110098A105 /* wasm.c */,
|
|
||||||
4CA9276D2A2A5D110098A105 /* wasm.h */,
|
|
||||||
4C4F14A82A2A71AB0045A0B9 /* nostrscript.h */,
|
|
||||||
4C4F14A92A2A71AB0045A0B9 /* nostrscript.c */,
|
|
||||||
4C06670928FDE64700038D2A /* damus.h */,
|
4C06670928FDE64700038D2A /* damus.h */,
|
||||||
4C06670A28FDE64700038D2A /* damus.c */,
|
4C06670A28FDE64700038D2A /* damus.c */,
|
||||||
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */,
|
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */,
|
||||||
@@ -954,7 +876,7 @@
|
|||||||
4C0A3F8D280F63FF000448DE /* Models */ = {
|
4C0A3F8D280F63FF000448DE /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4C190F1E2A535FC200027FD5 /* Zaps */,
|
4C9AA1462A444422003F49FD /* Zaps */,
|
||||||
4C54AA0829A55416003E4487 /* Notifications */,
|
4C54AA0829A55416003E4487 /* Notifications */,
|
||||||
3AA247FC297E3CFF0090C62D /* RepostsModel.swift */,
|
3AA247FC297E3CFF0090C62D /* RepostsModel.swift */,
|
||||||
4C0A3F8E280F640A000448DE /* ThreadModel.swift */,
|
4C0A3F8E280F640A000448DE /* ThreadModel.swift */,
|
||||||
@@ -997,37 +919,10 @@
|
|||||||
3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */,
|
3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */,
|
||||||
4C7D09772A0B0CC900943473 /* WalletModel.swift */,
|
4C7D09772A0B0CC900943473 /* WalletModel.swift */,
|
||||||
3A23838D2A297DD200E5AA2E /* ZapButtonModel.swift */,
|
3A23838D2A297DD200E5AA2E /* ZapButtonModel.swift */,
|
||||||
3A5E47C42A4A6CF400C0D090 /* Trie.swift */,
|
|
||||||
3A90B1802A4EA3AF00000D94 /* UserSearchCache.swift */,
|
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
4C0C03962A61E2670098B3B8 /* Fixtures */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
4C0C03982A61E27B0098B3B8 /* bool_setting.wasm */,
|
|
||||||
4C0C03972A61E27B0098B3B8 /* primal.wasm */,
|
|
||||||
);
|
|
||||||
name = Fixtures;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4C190F1E2A535FC200027FD5 /* Zaps */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
4C190F1F2A535FC200027FD5 /* CustomizeZapModel.swift */,
|
|
||||||
);
|
|
||||||
path = Zaps;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4C190F232A547D1700027FD5 /* NostrScript */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
4C190F242A547D2000027FD5 /* LoadScript.swift */,
|
|
||||||
);
|
|
||||||
path = NostrScript;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4C198DEA29F88C6B004C165C /* BlurHash */ = {
|
4C198DEA29F88C6B004C165C /* BlurHash */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1047,16 +942,6 @@
|
|||||||
path = Images;
|
path = Images;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
4C19AE4A2A5CEF7C00C90DB7 /* nostrscript */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
4C19AE4B2A5CEF7C00C90DB7 /* primal.ts */,
|
|
||||||
4C19AE4C2A5CEF7C00C90DB7 /* NostrScript.swift */,
|
|
||||||
4C19AE502A5CEF7C00C90DB7 /* nostr.ts */,
|
|
||||||
);
|
|
||||||
path = nostrscript;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4C1A9A1B29DDCF8B00516EAC /* Settings */ = {
|
4C1A9A1B29DDCF8B00516EAC /* Settings */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1066,7 +951,6 @@
|
|||||||
4C1A9A2429DDDF2600516EAC /* ZapSettingsView.swift */,
|
4C1A9A2429DDDF2600516EAC /* ZapSettingsView.swift */,
|
||||||
4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */,
|
4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */,
|
||||||
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */,
|
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */,
|
||||||
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1101,18 +985,9 @@
|
|||||||
path = Notifications;
|
path = Notifications;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
4C687C2A2A6058450092C550 /* Search */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
4C687C232A5FA86D0092C550 /* SearchHeaderView.swift */,
|
|
||||||
);
|
|
||||||
path = Search;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4C75EFA227FA576C0006080F /* Views */ = {
|
4C75EFA227FA576C0006080F /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4C190F232A547D1700027FD5 /* NostrScript */,
|
|
||||||
4C7D09692A0AEA0400943473 /* CodeScanner */,
|
4C7D09692A0AEA0400943473 /* CodeScanner */,
|
||||||
4C7D095A2A098C5C00943473 /* Wallet */,
|
4C7D095A2A098C5C00943473 /* Wallet */,
|
||||||
4C8D1A6D29F31E4100ACDF75 /* Buttons */,
|
4C8D1A6D29F31E4100ACDF75 /* Buttons */,
|
||||||
@@ -1183,7 +1058,6 @@
|
|||||||
children = (
|
children = (
|
||||||
501F8C5329FF5EE2001AFC1D /* CoreData */,
|
501F8C5329FF5EE2001AFC1D /* CoreData */,
|
||||||
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */,
|
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */,
|
||||||
50A60D132A28BEEE00186190 /* RelayLog.swift */,
|
|
||||||
4C75EFA527FF87A20006080F /* Nostr.swift */,
|
4C75EFA527FF87A20006080F /* Nostr.swift */,
|
||||||
4C75EFAE28049D340006080F /* NostrFilter.swift */,
|
4C75EFAE28049D340006080F /* NostrFilter.swift */,
|
||||||
4C75EFB028049D510006080F /* NostrResponse.swift */,
|
4C75EFB028049D510006080F /* NostrResponse.swift */,
|
||||||
@@ -1229,7 +1103,6 @@
|
|||||||
4C2859612A12A7F0004746F7 /* GoldSupportGradient.swift */,
|
4C2859612A12A7F0004746F7 /* GoldSupportGradient.swift */,
|
||||||
5C6E1DAE2A194075008FC15A /* PinkGradient.swift */,
|
5C6E1DAE2A194075008FC15A /* PinkGradient.swift */,
|
||||||
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */,
|
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */,
|
||||||
4C687C202A5F7ED00092C550 /* DamusBackground.swift */,
|
|
||||||
);
|
);
|
||||||
path = Gradients;
|
path = Gradients;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1282,7 +1155,6 @@
|
|||||||
50B5685229F97CB400A23243 /* CredentialHandler.swift */,
|
50B5685229F97CB400A23243 /* CredentialHandler.swift */,
|
||||||
4C7D09582A05BEAD00943473 /* KeyboardVisible.swift */,
|
4C7D09582A05BEAD00943473 /* KeyboardVisible.swift */,
|
||||||
3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */,
|
3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */,
|
||||||
D2277EE92A089BD5006C3807 /* Router.swift */,
|
|
||||||
);
|
);
|
||||||
path = Util;
|
path = Util;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1296,26 +1168,12 @@
|
|||||||
path = Buttons;
|
path = Buttons;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
4CA9275B2A28FF570098A105 /* Longform */ = {
|
4C9AA1462A444422003F49FD /* Zaps */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4CA9275C2A28FF630098A105 /* LongformView.swift */,
|
4C9AA1472A44442E003F49FD /* CustomizeZapModel.swift */,
|
||||||
4CA9275E2A2902B20098A105 /* LongformPreview.swift */,
|
|
||||||
);
|
);
|
||||||
path = Longform;
|
path = Zaps;
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4CA927682A290F8F0098A105 /* Components */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
4CA927642A290F1A0098A105 /* TimeDot.swift */,
|
|
||||||
4CA927622A290EB10098A105 /* EventTop.swift */,
|
|
||||||
4CC7AAF3297F18B400430951 /* ReplyDescription.swift */,
|
|
||||||
4CA927662A290F8B0098A105 /* RelativeTime.swift */,
|
|
||||||
4CA927692A290FC00098A105 /* ContextButton.swift */,
|
|
||||||
4CA9276B2A2910D10098A105 /* ReplyPart.swift */,
|
|
||||||
);
|
|
||||||
path = Components;
|
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
4CAAD8AE29888A9B00060CEA /* Relays */ = {
|
4CAAD8AE29888A9B00060CEA /* Relays */ = {
|
||||||
@@ -1361,8 +1219,8 @@
|
|||||||
4CB8FC222A41ABA500763C51 /* AboutView.swift */,
|
4CB8FC222A41ABA500763C51 /* AboutView.swift */,
|
||||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
||||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
||||||
|
F79C7FAC29D5E9620000F946 /* EditProfilePictureControl.swift */,
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
||||||
F79C7FAC29D5E9620000F946 /* EditPictureControl.swift */,
|
|
||||||
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
||||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||||
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
|
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
|
||||||
@@ -1378,8 +1236,8 @@
|
|||||||
4CC7AAEE297F11B300430951 /* Events */ = {
|
4CC7AAEE297F11B300430951 /* Events */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4CA927682A290F8F0098A105 /* Components */,
|
|
||||||
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */,
|
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */,
|
||||||
|
4CC7AAF3297F18B400430951 /* ReplyDescription.swift */,
|
||||||
4CC7AAF5297F1A6A00430951 /* EventBody.swift */,
|
4CC7AAF5297F1A6A00430951 /* EventBody.swift */,
|
||||||
4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */,
|
4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */,
|
||||||
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */,
|
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */,
|
||||||
@@ -1388,8 +1246,6 @@
|
|||||||
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
|
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
|
||||||
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
|
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
|
||||||
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */,
|
4CFF8F6C29CD022E008DB934 /* WideEventView.swift */,
|
||||||
4CA9275B2A28FF570098A105 /* Longform */,
|
|
||||||
4CA927602A290E340098A105 /* EventShell.swift */,
|
|
||||||
);
|
);
|
||||||
path = Events;
|
path = Events;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1406,6 +1262,7 @@
|
|||||||
4CE0E2B029A3DF4700DB4CA2 /* Timeline */ = {
|
4CE0E2B029A3DF4700DB4CA2 /* Timeline */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4CE0E2B129A3DF6900DB4CA2 /* LoadMoreButton.swift */,
|
||||||
4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */,
|
4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */,
|
||||||
);
|
);
|
||||||
path = Timeline;
|
path = Timeline;
|
||||||
@@ -1414,7 +1271,6 @@
|
|||||||
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4C687C2A2A6058450092C550 /* Search */,
|
|
||||||
4C7D09702A0AEF4C00943473 /* Gradients */,
|
4C7D09702A0AEF4C00943473 /* Gradients */,
|
||||||
31D2E846295218AF006D67F8 /* Shimmer.swift */,
|
31D2E846295218AF006D67F8 /* Shimmer.swift */,
|
||||||
4CD7641A28A1641400B6928F /* EndBlock.swift */,
|
4CD7641A28A1641400B6928F /* EndBlock.swift */,
|
||||||
@@ -1437,7 +1293,6 @@
|
|||||||
4C8D00C929DF80350036AF10 /* TruncatedText.swift */,
|
4C8D00C929DF80350036AF10 /* TruncatedText.swift */,
|
||||||
4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */,
|
4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */,
|
||||||
5C6E1DAC2A193EC2008FC15A /* GradientButtonStyle.swift */,
|
5C6E1DAC2A193EC2008FC15A /* GradientButtonStyle.swift */,
|
||||||
3AAC7A032A626A75002B50DF /* CarouselDotsView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1445,7 +1300,6 @@
|
|||||||
4CE6DEDA27F7A08100C66700 = {
|
4CE6DEDA27F7A08100C66700 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4C19AE4A2A5CEF7C00C90DB7 /* nostrscript */,
|
|
||||||
4C06670728FDE62900038D2A /* damus-c */,
|
4C06670728FDE62900038D2A /* damus-c */,
|
||||||
4CE6DEE527F7A08100C66700 /* damus */,
|
4CE6DEE527F7A08100C66700 /* damus */,
|
||||||
4CE6DEF627F7A08200C66700 /* damusTests */,
|
4CE6DEF627F7A08200C66700 /* damusTests */,
|
||||||
@@ -1453,9 +1307,7 @@
|
|||||||
4CE6DEE427F7A08100C66700 /* Products */,
|
4CE6DEE427F7A08100C66700 /* Products */,
|
||||||
4CEE2AE62804F57B00AB5EEF /* Frameworks */,
|
4CEE2AE62804F57B00AB5EEF /* Frameworks */,
|
||||||
);
|
);
|
||||||
indentWidth = 4;
|
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
tabWidth = 4;
|
|
||||||
};
|
};
|
||||||
4CE6DEE427F7A08100C66700 /* Products */ = {
|
4CE6DEE427F7A08100C66700 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
@@ -1485,7 +1337,6 @@
|
|||||||
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
|
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
|
||||||
4CE6DEEC27F7A08200C66700 /* Preview Content */,
|
4CE6DEEC27F7A08200C66700 /* Preview Content */,
|
||||||
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
|
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
|
||||||
4C687C262A6039500092C550 /* TestData.swift */,
|
|
||||||
);
|
);
|
||||||
path = damus;
|
path = damus;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1501,7 +1352,6 @@
|
|||||||
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4C0C03962A61E2670098B3B8 /* Fixtures */,
|
|
||||||
4C7D097D2A0C58B900943473 /* WalletConnectTests.swift */,
|
4C7D097D2A0C58B900943473 /* WalletConnectTests.swift */,
|
||||||
F944F56C29EA9CB20067B3BF /* Models */,
|
F944F56C29EA9CB20067B3BF /* Models */,
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */,
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */,
|
||||||
@@ -1517,16 +1367,13 @@
|
|||||||
4CB883A9297612FF00DC99E7 /* ZapTests.swift */,
|
4CB883A9297612FF00DC99E7 /* ZapTests.swift */,
|
||||||
4CB883AD2976FA9300DC99E7 /* FormatTests.swift */,
|
4CB883AD2976FA9300DC99E7 /* FormatTests.swift */,
|
||||||
3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */,
|
3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */,
|
||||||
|
3A3040EE29A8FEE9008A0F29 /* EventDetailBarTests.swift */,
|
||||||
5019CADC2A0FB0A9000069E1 /* ProfileDatabaseTests.swift */,
|
5019CADC2A0FB0A9000069E1 /* ProfileDatabaseTests.swift */,
|
||||||
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */,
|
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */,
|
||||||
3A30410029AB12AA008A0F29 /* EventGroupViewTests.swift */,
|
3A30410029AB12AA008A0F29 /* EventGroupViewTests.swift */,
|
||||||
4C8D00D329E3C5D40036AF10 /* NIP19Tests.swift */,
|
4C8D00D329E3C5D40036AF10 /* NIP19Tests.swift */,
|
||||||
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */,
|
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */,
|
||||||
3A5E47C62A4A76C800C0D090 /* TrieTests.swift */,
|
3AFBF3FC29FDA7CC00E79C7C /* CustomZapViewTests.swift */,
|
||||||
3A90B1822A4EA3C600000D94 /* UserSearchCacheTests.swift */,
|
|
||||||
4C4F14A62A2A61A30045A0B9 /* NostrScriptTests.swift */,
|
|
||||||
4C19AE542A5D977400C90DB7 /* HashtagTests.swift */,
|
|
||||||
3AAC7A012A60FE72002B50DF /* LocalizationUtilTests.swift */,
|
|
||||||
);
|
);
|
||||||
path = damusTests;
|
path = damusTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1544,9 +1391,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4CE8794729941DA700F758CC /* RelayFilters.swift */,
|
4CE8794729941DA700F758CC /* RelayFilters.swift */,
|
||||||
504323A82A3495B6006AE6DC /* RelayModelCache.swift */,
|
4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */,
|
||||||
4CC6193929DC777C006A86D1 /* RelayBootstrap.swift */,
|
4CC6193929DC777C006A86D1 /* RelayBootstrap.swift */,
|
||||||
504323A62A34915F006AE6DC /* RelayModel.swift */,
|
|
||||||
);
|
);
|
||||||
path = Relays;
|
path = Relays;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1611,7 +1457,6 @@
|
|||||||
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */,
|
4CFF8F6629CC9E3A008DB934 /* ImageView.swift */,
|
||||||
6439E013296790CF0020672B /* ProfilePicImageView.swift */,
|
6439E013296790CF0020672B /* ProfilePicImageView.swift */,
|
||||||
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
4CFF8F6829CC9ED1008DB934 /* ImageContainerView.swift */,
|
||||||
4CFD502E2A2DA45800A229DB /* MediaView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Images;
|
path = Images;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1719,7 +1564,7 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 1330;
|
LastSwiftUpdateCheck = 1330;
|
||||||
LastUpgradeCheck = 1500;
|
LastUpgradeCheck = 1330;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
4CE6DEE227F7A08100C66700 = {
|
4CE6DEE227F7A08100C66700 = {
|
||||||
CreatedOnToolsVersion = 13.3;
|
CreatedOnToolsVersion = 13.3;
|
||||||
@@ -1763,7 +1608,6 @@
|
|||||||
"pt-PT",
|
"pt-PT",
|
||||||
ru,
|
ru,
|
||||||
"sv-SE",
|
"sv-SE",
|
||||||
sw,
|
|
||||||
"tr-TR",
|
"tr-TR",
|
||||||
uk,
|
uk,
|
||||||
vi,
|
vi,
|
||||||
@@ -1808,8 +1652,6 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
4C0C039A2A61E27B0098B3B8 /* bool_setting.wasm in Resources */,
|
|
||||||
4C0C03992A61E27B0098B3B8 /* primal.wasm in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1836,19 +1678,16 @@
|
|||||||
4C216F34286F5ACD00040376 /* DMView.swift in Sources */,
|
4C216F34286F5ACD00040376 /* DMView.swift in Sources */,
|
||||||
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
|
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
|
||||||
4CCF9AAF2A1FDBDB00E03CFB /* VideoPlayer.swift in Sources */,
|
4CCF9AAF2A1FDBDB00E03CFB /* VideoPlayer.swift in Sources */,
|
||||||
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */,
|
|
||||||
4CA9276A2A290FC00098A105 /* ContextButton.swift in Sources */,
|
|
||||||
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */,
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */,
|
||||||
4C363AA828297703006E126D /* InsertSort.swift in Sources */,
|
4C363AA828297703006E126D /* InsertSort.swift in Sources */,
|
||||||
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
||||||
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
||||||
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */,
|
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */,
|
||||||
4C190F252A547D2000027FD5 /* LoadScript.swift in Sources */,
|
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */,
|
||||||
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
||||||
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */,
|
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */,
|
||||||
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
||||||
4C54AA0C29A5543C003E4487 /* ZapGroup.swift in Sources */,
|
4C54AA0C29A5543C003E4487 /* ZapGroup.swift in Sources */,
|
||||||
4C190F202A535FC200027FD5 /* CustomizeZapModel.swift in Sources */,
|
|
||||||
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
||||||
F757933A29D7AECD007DEAC1 /* ImagePicker.swift in Sources */,
|
F757933A29D7AECD007DEAC1 /* ImagePicker.swift in Sources */,
|
||||||
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */,
|
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */,
|
||||||
@@ -1856,30 +1695,24 @@
|
|||||||
4C3EA67728FF7A9800C48A62 /* talstr.c in Sources */,
|
4C3EA67728FF7A9800C48A62 /* talstr.c in Sources */,
|
||||||
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
||||||
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
||||||
4CA9275D2A28FF630098A105 /* LongformView.swift in Sources */,
|
|
||||||
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
|
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
|
||||||
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
|
|
||||||
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
|
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
|
||||||
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
|
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
|
||||||
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
|
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
|
||||||
4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */,
|
4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */,
|
||||||
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */,
|
|
||||||
4CC6193A29DC777C006A86D1 /* RelayBootstrap.swift in Sources */,
|
4CC6193A29DC777C006A86D1 /* RelayBootstrap.swift in Sources */,
|
||||||
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
|
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
|
||||||
4CFD502F2A2DA45800A229DB /* MediaView.swift in Sources */,
|
|
||||||
4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */,
|
4C9F18E429ABDE6D008C55EC /* MaybeAnonPfpView.swift in Sources */,
|
||||||
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */,
|
4CA5588329F33F5B00DC6A45 /* StringCodable.swift in Sources */,
|
||||||
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
||||||
4C8D00C829DF791C0036AF10 /* CompatibleAttribute.swift in Sources */,
|
4C8D00C829DF791C0036AF10 /* CompatibleAttribute.swift in Sources */,
|
||||||
4C7D09742A0AEF9000943473 /* AlbyGradient.swift in Sources */,
|
4C7D09742A0AEF9000943473 /* AlbyGradient.swift in Sources */,
|
||||||
4C687C272A6039500092C550 /* TestData.swift in Sources */,
|
|
||||||
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */,
|
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */,
|
||||||
4C198DF229F88C6B004C165C /* BlurHashDecode.swift in Sources */,
|
4C198DF229F88C6B004C165C /* BlurHashDecode.swift in Sources */,
|
||||||
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */,
|
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */,
|
||||||
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */,
|
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */,
|
||||||
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
||||||
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
||||||
3AAC7A042A626A75002B50DF /* CarouselDotsView.swift in Sources */,
|
|
||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
||||||
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */,
|
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */,
|
||||||
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,
|
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,
|
||||||
@@ -1888,14 +1721,13 @@
|
|||||||
F7F0BA272978E54D009531F3 /* ParticipantsView.swift in Sources */,
|
F7F0BA272978E54D009531F3 /* ParticipantsView.swift in Sources */,
|
||||||
4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */,
|
4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */,
|
||||||
4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */,
|
4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */,
|
||||||
|
4CE0E2B229A3DF6900DB4CA2 /* LoadMoreButton.swift in Sources */,
|
||||||
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */,
|
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */,
|
||||||
4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */,
|
4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */,
|
||||||
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
|
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
|
||||||
4C8D1A6F29F31E5000ACDF75 /* FriendsButton.swift in Sources */,
|
4C8D1A6F29F31E5000ACDF75 /* FriendsButton.swift in Sources */,
|
||||||
3A5E47C52A4A6CF400C0D090 /* Trie.swift in Sources */,
|
|
||||||
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */,
|
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */,
|
||||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
||||||
4CA927672A290F8B0098A105 /* RelativeTime.swift in Sources */,
|
|
||||||
4CB883A62975F83C00DC99E7 /* LNUrlPayRequest.swift in Sources */,
|
4CB883A62975F83C00DC99E7 /* LNUrlPayRequest.swift in Sources */,
|
||||||
4C7D096D2A0AEA0400943473 /* CodeScanner.swift in Sources */,
|
4C7D096D2A0AEA0400943473 /* CodeScanner.swift in Sources */,
|
||||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
|
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
|
||||||
@@ -1915,7 +1747,6 @@
|
|||||||
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
||||||
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */,
|
4CFF8F6729CC9E3A008DB934 /* ImageView.swift in Sources */,
|
||||||
4CA927632A290EB10098A105 /* EventTop.swift in Sources */,
|
|
||||||
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
||||||
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
||||||
4CA3FA1029F593D000FDB3C3 /* ZapTypePicker.swift in Sources */,
|
4CA3FA1029F593D000FDB3C3 /* ZapTypePicker.swift in Sources */,
|
||||||
@@ -1950,6 +1781,7 @@
|
|||||||
4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */,
|
4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */,
|
||||||
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
||||||
4C7D09682A0AE9B200943473 /* NWCScannerView.swift in Sources */,
|
4C7D09682A0AE9B200943473 /* NWCScannerView.swift in Sources */,
|
||||||
|
4C9AA1482A44442E003F49FD /* CustomizeZapModel.swift in Sources */,
|
||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||||
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */,
|
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */,
|
||||||
4C7D09722A0AEF5E00943473 /* DamusGradient.swift in Sources */,
|
4C7D09722A0AEF5E00943473 /* DamusGradient.swift in Sources */,
|
||||||
@@ -1958,7 +1790,6 @@
|
|||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||||
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */,
|
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */,
|
||||||
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */,
|
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */,
|
||||||
4C687C242A5FA86D0092C550 /* SearchHeaderView.swift in Sources */,
|
|
||||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
||||||
4C1A9A2329DDDB8100516EAC /* IconLabel.swift in Sources */,
|
4C1A9A2329DDDB8100516EAC /* IconLabel.swift in Sources */,
|
||||||
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
||||||
@@ -1991,15 +1822,12 @@
|
|||||||
4C363A8828236948006E126D /* BlocksView.swift in Sources */,
|
4C363A8828236948006E126D /* BlocksView.swift in Sources */,
|
||||||
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
|
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
|
||||||
3A23838E2A297DD200E5AA2E /* ZapButtonModel.swift in Sources */,
|
3A23838E2A297DD200E5AA2E /* ZapButtonModel.swift in Sources */,
|
||||||
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */,
|
F79C7FAD29D5E9620000F946 /* EditProfilePictureControl.swift in Sources */,
|
||||||
F79C7FAD29D5E9620000F946 /* EditPictureControl.swift in Sources */,
|
|
||||||
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */,
|
4C9F18E229AA9B6C008C55EC /* CustomizeZapView.swift in Sources */,
|
||||||
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
||||||
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
|
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
|
||||||
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */,
|
|
||||||
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
||||||
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
||||||
4CA9276C2A2910D10098A105 /* ReplyPart.swift in Sources */,
|
|
||||||
4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */,
|
4CE1399029F0661A00AC6A0B /* RepostAction.swift in Sources */,
|
||||||
4CE1399229F0666100AC6A0B /* ShareActionButton.swift in Sources */,
|
4CE1399229F0666100AC6A0B /* ShareActionButton.swift in Sources */,
|
||||||
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */,
|
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */,
|
||||||
@@ -2016,7 +1844,6 @@
|
|||||||
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */,
|
50088DA129E8271A008A1FDF /* WebSocket.swift in Sources */,
|
||||||
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
||||||
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
|
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
|
||||||
4C19AE512A5CEF7C00C90DB7 /* NostrScript.swift in Sources */,
|
|
||||||
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
||||||
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
|
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
|
||||||
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */,
|
5C42E78C29DB76D90086AAC1 /* EmptyUserSearchView.swift in Sources */,
|
||||||
@@ -2034,8 +1861,6 @@
|
|||||||
5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */,
|
5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */,
|
||||||
4C8EC52529D1FA6C0085D9A8 /* DamusColors.swift in Sources */,
|
4C8EC52529D1FA6C0085D9A8 /* DamusColors.swift in Sources */,
|
||||||
3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */,
|
3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */,
|
||||||
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */,
|
|
||||||
3A90B1812A4EA3AF00000D94 /* UserSearchCache.swift in Sources */,
|
|
||||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||||
4CE1399429F0669900AC6A0B /* BigButton.swift in Sources */,
|
4CE1399429F0669900AC6A0B /* BigButton.swift in Sources */,
|
||||||
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */,
|
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */,
|
||||||
@@ -2044,7 +1869,6 @@
|
|||||||
4CDA128C29EB19C40006FA5A /* LocalNotification.swift in Sources */,
|
4CDA128C29EB19C40006FA5A /* LocalNotification.swift in Sources */,
|
||||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
||||||
4C7D09762A0AF19E00943473 /* FillAndStroke.swift in Sources */,
|
4C7D09762A0AF19E00943473 /* FillAndStroke.swift in Sources */,
|
||||||
4CA927612A290E340098A105 /* EventShell.swift in Sources */,
|
|
||||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
||||||
4C8D00CC29DF92DF0036AF10 /* Hashtags.swift in Sources */,
|
4C8D00CC29DF92DF0036AF10 /* Hashtags.swift in Sources */,
|
||||||
4C7D096F2A0AEA0400943473 /* ScannerViewController.swift in Sources */,
|
4C7D096F2A0AEA0400943473 /* ScannerViewController.swift in Sources */,
|
||||||
@@ -2054,10 +1878,8 @@
|
|||||||
4C8D00CF29E38B950036AF10 /* nostr_bech32.c in Sources */,
|
4C8D00CF29E38B950036AF10 /* nostr_bech32.c in Sources */,
|
||||||
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
|
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
|
||||||
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
|
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
|
||||||
4C687C212A5F7ED00092C550 /* DamusBackground.swift in Sources */,
|
|
||||||
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
||||||
4C363A962827096D006E126D /* PostBlock.swift in Sources */,
|
4C363A962827096D006E126D /* PostBlock.swift in Sources */,
|
||||||
4CA9275F2A2902B20098A105 /* LongformPreview.swift in Sources */,
|
|
||||||
4C5F9116283D855D0052CD1C /* EventsModel.swift in Sources */,
|
4C5F9116283D855D0052CD1C /* EventsModel.swift in Sources */,
|
||||||
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
|
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
|
||||||
4C06670E28FDEAA000038D2A /* utf8.c in Sources */,
|
4C06670E28FDEAA000038D2A /* utf8.c in Sources */,
|
||||||
@@ -2067,7 +1889,6 @@
|
|||||||
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
||||||
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */,
|
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */,
|
||||||
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
||||||
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */,
|
|
||||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
||||||
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
||||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
||||||
@@ -2079,7 +1900,6 @@
|
|||||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
||||||
4CE4F0F229D4FCFA005914DB /* DebouncedOnChange.swift in Sources */,
|
4CE4F0F229D4FCFA005914DB /* DebouncedOnChange.swift in Sources */,
|
||||||
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
|
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
|
||||||
4C9146FE2A2A87C200DDEA40 /* nostrscript.c in Sources */,
|
|
||||||
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
|
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
|
||||||
4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */,
|
4C1A9A1A29DCA17E00516EAC /* ReplyCounter.swift in Sources */,
|
||||||
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */,
|
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */,
|
||||||
@@ -2093,7 +1913,6 @@
|
|||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
||||||
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */,
|
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */,
|
||||||
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
|
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
|
||||||
4C9147002A2A891E00DDEA40 /* error.c in Sources */,
|
|
||||||
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */,
|
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */,
|
||||||
501F8C5529FF5EF6001AFC1D /* PersistedProfile.swift in Sources */,
|
501F8C5529FF5EF6001AFC1D /* PersistedProfile.swift in Sources */,
|
||||||
4CF0ABD42980996B00D66079 /* Report.swift in Sources */,
|
4CF0ABD42980996B00D66079 /* Report.swift in Sources */,
|
||||||
@@ -2133,23 +1952,20 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5019CADD2A0FB0A9000069E1 /* ProfileDatabaseTests.swift in Sources */,
|
5019CADD2A0FB0A9000069E1 /* ProfileDatabaseTests.swift in Sources */,
|
||||||
3A90B1832A4EA3C600000D94 /* UserSearchCacheTests.swift in Sources */,
|
|
||||||
4C19AE552A5D977400C90DB7 /* HashtagTests.swift in Sources */,
|
|
||||||
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */,
|
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */,
|
||||||
3AAC7A022A60FE72002B50DF /* LocalizationUtilTests.swift in Sources */,
|
|
||||||
4C8D00D429E3C5D40036AF10 /* NIP19Tests.swift in Sources */,
|
4C8D00D429E3C5D40036AF10 /* NIP19Tests.swift in Sources */,
|
||||||
3A30410129AB12AA008A0F29 /* EventGroupViewTests.swift in Sources */,
|
3A30410129AB12AA008A0F29 /* EventGroupViewTests.swift in Sources */,
|
||||||
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */,
|
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */,
|
||||||
|
3AFBF3FD29FDA7CC00E79C7C /* CustomZapViewTests.swift in Sources */,
|
||||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
||||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
||||||
4C4F14A72A2A61A30045A0B9 /* NostrScriptTests.swift in Sources */,
|
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */,
|
||||||
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
|
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
|
||||||
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
|
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
|
||||||
4C7D097E2A0C58B900943473 /* WalletConnectTests.swift in Sources */,
|
4C7D097E2A0C58B900943473 /* WalletConnectTests.swift in Sources */,
|
||||||
4CB883AA297612FF00DC99E7 /* ZapTests.swift in Sources */,
|
4CB883AA297612FF00DC99E7 /* ZapTests.swift in Sources */,
|
||||||
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */,
|
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */,
|
||||||
F944F56E29EA9CCC0067B3BF /* DamusParseContentTests.swift in Sources */,
|
F944F56E29EA9CCC0067B3BF /* DamusParseContentTests.swift in Sources */,
|
||||||
3A5E47C72A4A76C800C0D090 /* TrieTests.swift in Sources */,
|
|
||||||
4CB883AE2976FA9300DC99E7 /* FormatTests.swift in Sources */,
|
4CB883AE2976FA9300DC99E7 /* FormatTests.swift in Sources */,
|
||||||
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
||||||
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */,
|
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */,
|
||||||
@@ -2216,7 +2032,6 @@
|
|||||||
3A325AC929C9E0CF002BE7ED /* es-ES */,
|
3A325AC929C9E0CF002BE7ED /* es-ES */,
|
||||||
3AC59CA929CDDB78007E04A6 /* pt-BR */,
|
3AC59CA929CDDB78007E04A6 /* pt-BR */,
|
||||||
3A821C4029E819D500B4BCA7 /* fr */,
|
3A821C4029E819D500B4BCA7 /* fr */,
|
||||||
3ABACEC02A5B3ED10037A847 /* sw */,
|
|
||||||
);
|
);
|
||||||
name = Localizable.stringsdict;
|
name = Localizable.stringsdict;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2251,7 +2066,6 @@
|
|||||||
3A325AC829C9E0CF002BE7ED /* es-ES */,
|
3A325AC829C9E0CF002BE7ED /* es-ES */,
|
||||||
3AC59CA829CDDB78007E04A6 /* pt-BR */,
|
3AC59CA829CDDB78007E04A6 /* pt-BR */,
|
||||||
3A821C3F29E819D500B4BCA7 /* fr */,
|
3A821C3F29E819D500B4BCA7 /* fr */,
|
||||||
3ABACEBF2A5B3ED10037A847 /* sw */,
|
|
||||||
);
|
);
|
||||||
name = InfoPlist.strings;
|
name = InfoPlist.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2287,7 +2101,6 @@
|
|||||||
3A325AC729C9E0CF002BE7ED /* es-ES */,
|
3A325AC729C9E0CF002BE7ED /* es-ES */,
|
||||||
3AC59CA729CDDB78007E04A6 /* pt-BR */,
|
3AC59CA729CDDB78007E04A6 /* pt-BR */,
|
||||||
3A821C3E29E819D500B4BCA7 /* fr */,
|
3A821C3E29E819D500B4BCA7 /* fr */,
|
||||||
3ABACEC12A5B3ED10037A847 /* sw */,
|
|
||||||
);
|
);
|
||||||
name = Localizable.strings;
|
name = Localizable.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2332,7 +2145,6 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
@@ -2396,7 +2208,6 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -2425,7 +2236,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 5;
|
CURRENT_PROJECT_VERSION = 8;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -2443,7 +2254,6 @@
|
|||||||
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -2452,7 +2262,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)",
|
"$(PROJECT_DIR)",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.6;
|
MARKETING_VERSION = 1.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
@@ -2474,7 +2284,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 5;
|
CURRENT_PROJECT_VERSION = 8;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -2492,7 +2302,6 @@
|
|||||||
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -2501,7 +2310,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)",
|
"$(PROJECT_DIR)",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.6;
|
MARKETING_VERSION = 1.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||||
@@ -2523,7 +2332,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -2543,7 +2352,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damusTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1500"
|
LastUpgradeVersion = "1420"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1500"
|
LastUpgradeVersion = "1420"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
//
|
|
||||||
// CarouselDotsView.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Terry Yiu on 7/15/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct CarouselDotsView: View {
|
|
||||||
let maxCount: Int
|
|
||||||
let maxVisibleCount: Int
|
|
||||||
@Binding var selectedIndex: Int
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
if maxCount > 1 {
|
|
||||||
HStack {
|
|
||||||
let visibleRange = visibleRange()
|
|
||||||
ForEach(0 ..< maxCount, id: \.self) { index in
|
|
||||||
if visibleRange.contains(index) {
|
|
||||||
Circle()
|
|
||||||
.fill(index == selectedIndex ? Color("DamusPurple") : Color("DamusLightGrey"))
|
|
||||||
.frame(width: 10, height: 10)
|
|
||||||
.onTapGesture {
|
|
||||||
selectedIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.top, CGFloat(8))
|
|
||||||
.id(UUID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func visibleRange() -> ClosedRange<Int> {
|
|
||||||
let visibleCount = min(maxCount, maxVisibleCount)
|
|
||||||
|
|
||||||
let half = Int(visibleCount / 2)
|
|
||||||
|
|
||||||
// Keep the selected dot in the middle of the visible dots when possible.
|
|
||||||
var minVisibleIndex: Int
|
|
||||||
var maxVisibleIndex: Int
|
|
||||||
|
|
||||||
if visibleCount % 2 == 0 {
|
|
||||||
minVisibleIndex = max(0, selectedIndex - half)
|
|
||||||
maxVisibleIndex = min(maxCount - 1, selectedIndex + half - 1)
|
|
||||||
} else {
|
|
||||||
minVisibleIndex = max(0, selectedIndex - half)
|
|
||||||
maxVisibleIndex = min(maxCount - 1, selectedIndex + half)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust min and max to be within the bounds of what is visibly allowed.
|
|
||||||
if (maxVisibleIndex - minVisibleIndex + 1) < visibleCount {
|
|
||||||
if minVisibleIndex == 0 {
|
|
||||||
maxVisibleIndex = visibleCount - 1
|
|
||||||
} else if maxVisibleIndex == maxCount - 1 {
|
|
||||||
minVisibleIndex = maxVisibleIndex - visibleCount + 1
|
|
||||||
}
|
|
||||||
} else if (maxVisibleIndex - minVisibleIndex + 1) > visibleCount {
|
|
||||||
minVisibleIndex = maxVisibleIndex - maxVisibleCount + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return minVisibleIndex...maxVisibleIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,19 +8,13 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct GradientButtonStyle: ButtonStyle {
|
struct GradientButtonStyle: ButtonStyle {
|
||||||
let padding: CGFloat
|
|
||||||
|
|
||||||
init(padding: CGFloat = 16.0) {
|
|
||||||
self.padding = padding
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBody(configuration: Self.Configuration) -> some View {
|
func makeBody(configuration: Self.Configuration) -> some View {
|
||||||
return configuration.label
|
return configuration.label
|
||||||
.padding(padding)
|
.padding()
|
||||||
.foregroundColor(Color.white)
|
.foregroundColor(Color.white)
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 12)
|
RoundedRectangle(cornerRadius: 12)
|
||||||
.fill(PinkGradient)
|
.fill(PinkGradient.gradient)
|
||||||
}
|
}
|
||||||
.scaleEffect(configuration.isPressed ? 0.8 : 1)
|
.scaleEffect(configuration.isPressed ? 0.8 : 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// DamusBackground.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-07-12.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct DamusBackground: View {
|
|
||||||
let maxHeight: CGFloat
|
|
||||||
|
|
||||||
init(maxHeight: CGFloat = 250.0) {
|
|
||||||
self.maxHeight = maxHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Image("login-header")
|
|
||||||
.resizable()
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: maxHeight, alignment: .center)
|
|
||||||
.ignoresSafeArea()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DamusBackground_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
DamusBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,18 +11,20 @@ fileprivate let damus_grad_c1 = hex_col(r: 0xd3, g: 0x4c, b: 0xd9)
|
|||||||
fileprivate let damus_grad_c2 = hex_col(r: 0xf8, g: 0x69, b: 0xb6)
|
fileprivate let damus_grad_c2 = hex_col(r: 0xf8, g: 0x69, b: 0xb6)
|
||||||
fileprivate let pink_grad = [damus_grad_c1, damus_grad_c2]
|
fileprivate let pink_grad = [damus_grad_c1, damus_grad_c2]
|
||||||
|
|
||||||
let PinkGradient = LinearGradient(colors: pink_grad, startPoint: .topTrailing, endPoint: .bottom)
|
struct PinkGradient: View {
|
||||||
|
|
||||||
struct PinkGradientView: View {
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
PinkGradient
|
PinkGradient.gradient
|
||||||
.edgesIgnoringSafeArea([.top,.bottom])
|
.edgesIgnoringSafeArea([.top,.bottom])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static var gradient: LinearGradient {
|
||||||
struct PinkGradientView_Previews: PreviewProvider {
|
LinearGradient(colors: pink_grad, startPoint: .topTrailing, endPoint: .bottom)
|
||||||
static var previews: some View {
|
}
|
||||||
PinkGradientView()
|
}
|
||||||
|
|
||||||
|
struct PinkGradient_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PinkGradient()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,9 +209,30 @@ struct ImageCarousel: View {
|
|||||||
.onTapGesture { }
|
.onTapGesture { }
|
||||||
|
|
||||||
// This is our custom carousel image indicator
|
// This is our custom carousel image indicator
|
||||||
// A maximum of 18 should be visible. Any more than that and it starts to push the frame of the parent view
|
CarouselDotsView(urls: urls, selectedIndex: $selectedIndex)
|
||||||
// causing adjacent views to disort in dimensions.
|
}
|
||||||
CarouselDotsView(maxCount: urls.count, maxVisibleCount: 18, selectedIndex: $selectedIndex)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Custom Carousel
|
||||||
|
struct CarouselDotsView<T>: View {
|
||||||
|
let urls: [T]
|
||||||
|
@Binding var selectedIndex: Int
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if urls.count > 1 {
|
||||||
|
HStack {
|
||||||
|
ForEach(urls.indices, id: \.self) { index in
|
||||||
|
Circle()
|
||||||
|
.fill(index == selectedIndex ? Color("DamusPurple") : Color("DamusLightGrey"))
|
||||||
|
.frame(width: 10, height: 10)
|
||||||
|
.onTapGesture {
|
||||||
|
selectedIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.top, CGFloat(8))
|
||||||
|
.id(UUID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,12 @@ struct NIP05Badge: View {
|
|||||||
Group {
|
Group {
|
||||||
if nip05_color {
|
if nip05_color {
|
||||||
LINEAR_GRADIENT
|
LINEAR_GRADIENT
|
||||||
.mask(Image("verified.fill")
|
.mask(Image("check-circle.fill")
|
||||||
.resizable()
|
.resizable()
|
||||||
).frame(width: 18, height: 18)
|
).frame(width: 14, height: 14)
|
||||||
} else if show_domain {
|
} else if show_domain {
|
||||||
Image("verified")
|
Image("check-circle.fill")
|
||||||
.resizable()
|
.font(.footnote)
|
||||||
.frame(width: 18, height: 18)
|
|
||||||
.nip05_colorized(gradient: nip05_color)
|
.nip05_colorized(gradient: nip05_color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,11 +84,7 @@ func use_nip05_color(pubkey: String, contacts: Contacts) -> Bool {
|
|||||||
struct NIP05Badge_Previews: PreviewProvider {
|
struct NIP05Badge_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let test_state = test_damus_state()
|
let test_state = test_damus_state()
|
||||||
VStack {
|
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, clickable: false)
|
||||||
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, clickable: false)
|
|
||||||
|
|
||||||
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: Contacts(our_pubkey: "sdkfjsdf"), show_domain: true, clickable: false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
//
|
|
||||||
// SearchIconView.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-07-12.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct SearchHeaderView: View {
|
|
||||||
let state: DamusState
|
|
||||||
let described: DescribedSearch
|
|
||||||
@State var is_following: Bool
|
|
||||||
|
|
||||||
init(state: DamusState, described: DescribedSearch) {
|
|
||||||
self.state = state
|
|
||||||
self.described = described
|
|
||||||
|
|
||||||
let is_following = (described.is_hashtag.map {
|
|
||||||
ht in is_following_hashtag(contacts: state.contacts.event, hashtag: ht)
|
|
||||||
}) ?? false
|
|
||||||
|
|
||||||
self._is_following = State(wrappedValue: is_following)
|
|
||||||
}
|
|
||||||
|
|
||||||
var Icon: some View {
|
|
||||||
ZStack {
|
|
||||||
Circle()
|
|
||||||
.fill(Color(red: 0xF8/255.0, green: 0xE7/255.0, blue: 0xF8/255.0))
|
|
||||||
.frame(width: 54, height: 54)
|
|
||||||
|
|
||||||
switch described {
|
|
||||||
case .hashtag:
|
|
||||||
Text(verbatim: "#")
|
|
||||||
.font(.largeTitle.bold())
|
|
||||||
.foregroundStyle(PinkGradient)
|
|
||||||
.mask(Text(verbatim: "#")
|
|
||||||
.font(.largeTitle.bold()))
|
|
||||||
|
|
||||||
case .unknown:
|
|
||||||
Image(systemName: "magnifyingglass")
|
|
||||||
.font(.title.bold())
|
|
||||||
.foregroundStyle(PinkGradient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var SearchText: Text {
|
|
||||||
Text(described.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unfollow(_ hashtag: String) {
|
|
||||||
is_following = false
|
|
||||||
handle_unfollow(state: state, unfollow: .t(hashtag))
|
|
||||||
}
|
|
||||||
|
|
||||||
func follow(_ hashtag: String) {
|
|
||||||
is_following = true
|
|
||||||
handle_follow(state: state, follow: .t(hashtag))
|
|
||||||
}
|
|
||||||
|
|
||||||
func FollowButton(_ ht: String) -> some View {
|
|
||||||
return Button(action: { follow(ht) }) {
|
|
||||||
Text("Follow hashtag", comment: "Button to follow a given hashtag.")
|
|
||||||
.font(.footnote.bold())
|
|
||||||
}
|
|
||||||
.buttonStyle(GradientButtonStyle(padding: 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnfollowButton(_ ht: String) -> some View {
|
|
||||||
return Button(action: { unfollow(ht) }) {
|
|
||||||
Text("Unfollow hashtag", comment: "Button to unfollow a given hashtag.")
|
|
||||||
.font(.footnote.bold())
|
|
||||||
}
|
|
||||||
.buttonStyle(GradientButtonStyle(padding: 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack(alignment: .center, spacing: 30) {
|
|
||||||
Icon
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 10.0) {
|
|
||||||
SearchText
|
|
||||||
.foregroundStyle(DamusLogoGradient.gradient)
|
|
||||||
.font(.title.bold())
|
|
||||||
|
|
||||||
if state.is_privkey_user, case .hashtag(let ht) = described {
|
|
||||||
if is_following {
|
|
||||||
UnfollowButton(ht)
|
|
||||||
} else {
|
|
||||||
FollowButton(ht)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onReceive(handle_notify(.followed)) { notif in
|
|
||||||
let ref = notif.object as! ReferencedId
|
|
||||||
guard hashtag_matches_search(desc: self.described, ref: ref) else { return }
|
|
||||||
self.is_following = true
|
|
||||||
}
|
|
||||||
.onReceive(handle_notify(.unfollowed)) { notif in
|
|
||||||
let ref = notif.object as! ReferencedId
|
|
||||||
guard hashtag_matches_search(desc: self.described, ref: ref) else { return }
|
|
||||||
self.is_following = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hashtag_matches_search(desc: DescribedSearch, ref: ReferencedId) -> Bool {
|
|
||||||
guard let ht = desc.is_hashtag, ref.key == "t" && ref.ref_id == ht
|
|
||||||
else { return false }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func is_following_hashtag(contacts: NostrEvent?, hashtag: String) -> Bool {
|
|
||||||
guard let contacts else { return false }
|
|
||||||
return is_already_following(contacts: contacts, follow: .t(hashtag))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct SearchHeaderView_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
SearchHeaderView(state: test_damus_state(), described: .hashtag("damus"))
|
|
||||||
|
|
||||||
SearchHeaderView(state: test_damus_state(), described: .unknown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ import NaturalLanguage
|
|||||||
|
|
||||||
|
|
||||||
struct Translated: Equatable {
|
struct Translated: Equatable {
|
||||||
let artifacts: NoteArtifactsSeparated
|
let artifacts: NoteArtifacts
|
||||||
let language: String
|
let language: String
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +42,9 @@ struct TranslateView: View {
|
|||||||
.translate_button_style()
|
.translate_button_style()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TranslatedView(lang: String?, artifacts: NoteArtifactsSeparated) -> some View {
|
func TranslatedView(lang: String?, artifacts: NoteArtifacts) -> some View {
|
||||||
return VStack(alignment: .leading) {
|
return VStack(alignment: .leading) {
|
||||||
let translatedFromLanguageString = String(format: NSLocalizedString("Translated from %@", comment: "Button to indicate that the note has been translated from a different language."), lang ?? "ja")
|
Text(String(format: NSLocalizedString("Translated from %@", comment: "Button to indicate that the note has been translated from a different language."), lang ?? "ja"))
|
||||||
Text(translatedFromLanguageString)
|
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.padding([.top, .bottom], 10)
|
.padding([.top, .bottom], 10)
|
||||||
|
|||||||
@@ -11,10 +11,21 @@ struct UserViewRow: View {
|
|||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
let pubkey: String
|
let pubkey: String
|
||||||
|
|
||||||
|
@State var navigating: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let dest = ProfileView(damus_state: damus_state, pubkey: pubkey)
|
||||||
|
|
||||||
UserView(damus_state: damus_state, pubkey: pubkey)
|
UserView(damus_state: damus_state, pubkey: pubkey)
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.background(.clear)
|
.background(
|
||||||
|
NavigationLink(destination: dest, isActive: $navigating) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.onTapGesture {
|
||||||
|
navigating = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ struct ZapButton: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
if !damus_state.settings.nozaps || zaps.zap_total > 0 {
|
if zaps.zap_total > 0 {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
}, label: {
|
}, label: {
|
||||||
Image(zap_img)
|
Image(zap_img)
|
||||||
@@ -116,9 +116,7 @@ struct ZapButton: View {
|
|||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width:20, height: 20)
|
.frame(width:20, height: 20)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if zaps.zap_total > 0 {
|
|
||||||
Text(verbatim: format_msats_abbrev(zaps.zap_total))
|
Text(verbatim: format_msats_abbrev(zaps.zap_total))
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.foregroundColor(zap_color)
|
.foregroundColor(zap_color)
|
||||||
@@ -126,14 +124,21 @@ struct ZapButton: View {
|
|||||||
}
|
}
|
||||||
.accessibilityLabel(NSLocalizedString("Zap", comment: "Accessibility label for zap button"))
|
.accessibilityLabel(NSLocalizedString("Zap", comment: "Accessibility label for zap button"))
|
||||||
.simultaneousGesture(LongPressGesture().onEnded {_ in
|
.simultaneousGesture(LongPressGesture().onEnded {_ in
|
||||||
guard !damus_state.settings.nozaps else { return }
|
// when we don't have nozaps mode enable, long press shows the zap customizer
|
||||||
|
if !damus_state.settings.nozaps {
|
||||||
|
//present_sheet(.zap(target: target, lnurl: lnurl))
|
||||||
|
}
|
||||||
|
|
||||||
present_sheet(.zap(target: target, lnurl: lnurl))
|
// long press does nothing in nozaps mode
|
||||||
})
|
})
|
||||||
.highPriorityGesture(TapGesture().onEnded {
|
.highPriorityGesture(TapGesture().onEnded {
|
||||||
guard !damus_state.settings.nozaps else { return }
|
// when we have appstore mode on, only show the zap customizer as "user zaps"
|
||||||
|
if damus_state.settings.nozaps {
|
||||||
tap()
|
//present_sheet(.zap(target: target, lnurl: lnurl))
|
||||||
|
} else {
|
||||||
|
// otherwise we restore the original behavior of one-tap zaps
|
||||||
|
tap()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+122
-123
@@ -82,6 +82,14 @@ struct ContentView: View {
|
|||||||
@State var damus_state: DamusState? = nil
|
@State var damus_state: DamusState? = nil
|
||||||
@SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home
|
@SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home
|
||||||
@State var is_deleted_account: Bool = false
|
@State var is_deleted_account: Bool = false
|
||||||
|
@State var active_profile: String? = nil
|
||||||
|
@State var active_search: NostrFilter? = nil
|
||||||
|
@State var active_event: NostrEvent? = nil
|
||||||
|
@State var profile_open: Bool = false
|
||||||
|
@State var thread_open: Bool = false
|
||||||
|
@State var search_open: Bool = false
|
||||||
|
@State var wallet_open: Bool = false
|
||||||
|
@State var active_nwc: WalletConnectURL? = nil
|
||||||
@State var muting: String? = nil
|
@State var muting: String? = nil
|
||||||
@State var confirm_mute: Bool = false
|
@State var confirm_mute: Bool = false
|
||||||
@State var user_muted_confirm: Bool = false
|
@State var user_muted_confirm: Bool = false
|
||||||
@@ -89,7 +97,6 @@ struct ContentView: View {
|
|||||||
@SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies
|
@SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies
|
||||||
@State private var isSideBarOpened = false
|
@State private var isSideBarOpened = false
|
||||||
var home: HomeModel = HomeModel()
|
var home: HomeModel = HomeModel()
|
||||||
@StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator()
|
|
||||||
|
|
||||||
let sub_id = UUID().description
|
let sub_id = UUID().description
|
||||||
|
|
||||||
@@ -121,7 +128,7 @@ struct ContentView: View {
|
|||||||
|
|
||||||
if privkey != nil {
|
if privkey != nil {
|
||||||
PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) {
|
PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) {
|
||||||
self.active_sheet = .post(.posting(.none))
|
self.active_sheet = .post(.posting)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,13 +149,16 @@ struct ContentView: View {
|
|||||||
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let damus = self.damus_state {
|
if let damus = self.damus_state {
|
||||||
TimelineView<AnyView>(events: home.events, loading: .constant(false), damus: damus, show_friend_icon: false, filter: filter)
|
TimelineView(events: home.events, loading: .constant(false), damus: damus, show_friend_icon: false, filter: filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func popToRoot() {
|
func popToRoot() {
|
||||||
navigationCoordinator.popToRoot()
|
profile_open = false
|
||||||
|
thread_open = false
|
||||||
|
search_open = false
|
||||||
|
wallet_open = false
|
||||||
isSideBarOpened = false
|
isSideBarOpened = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +169,21 @@ struct ContentView: View {
|
|||||||
|
|
||||||
func MainContent(damus: DamusState) -> some View {
|
func MainContent(damus: DamusState) -> some View {
|
||||||
VStack {
|
VStack {
|
||||||
|
NavigationLink(destination: WalletView(damus_state: damus, model: damus_state!.wallet), isActive: $wallet_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
if let active_event {
|
||||||
|
let thread = ThreadModel(event: active_event, damus_state: damus_state!)
|
||||||
|
NavigationLink(destination: ThreadView(state: damus_state!, thread: thread), isActive: $thread_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NavigationLink(destination: MaybeSearchView, isActive: $search_open) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
switch selected_timeline {
|
switch selected_timeline {
|
||||||
case .search:
|
case .search:
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
@@ -200,6 +225,28 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MaybeSearchView: some View {
|
||||||
|
Group {
|
||||||
|
if let search = self.active_search {
|
||||||
|
SearchView(appstate: damus_state!, search: SearchModel(state: damus_state!, search: search))
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var MaybeProfileView: some View {
|
||||||
|
Group {
|
||||||
|
if let pk = self.active_profile {
|
||||||
|
let profile_model = ProfileModel(pubkey: pk, damus: damus_state!)
|
||||||
|
let followers = FollowersModel(damus_state: damus_state!, target: pk)
|
||||||
|
ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers)
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func MaybeReportView(target: ReportTarget) -> some View {
|
func MaybeReportView(target: ReportTarget) -> some View {
|
||||||
Group {
|
Group {
|
||||||
if let damus_state {
|
if let damus_state {
|
||||||
@@ -215,36 +262,32 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func open_event(ev: NostrEvent) {
|
func open_event(ev: NostrEvent) {
|
||||||
let thread = ThreadModel(event: ev, damus_state: damus_state!)
|
popToRoot()
|
||||||
navigationCoordinator.push(route: Route.Thread(thread: thread))
|
self.active_event = ev
|
||||||
|
self.thread_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func open_wallet(nwc: WalletConnectURL) {
|
func open_wallet(nwc: WalletConnectURL) {
|
||||||
self.damus_state!.wallet.new(nwc)
|
self.damus_state!.wallet.new(nwc)
|
||||||
navigationCoordinator.push(route: Route.Wallet(wallet: damus_state!.wallet))
|
self.wallet_open = true
|
||||||
}
|
|
||||||
|
|
||||||
func open_script(_ script: [UInt8]) {
|
|
||||||
print("pushing script nav")
|
|
||||||
let model = ScriptModel(data: script, state: .not_loaded)
|
|
||||||
navigationCoordinator.push(route: Route.Script(script: model))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func open_profile(id: String) {
|
func open_profile(id: String) {
|
||||||
let profile_model = ProfileModel(pubkey: id, damus: damus_state!)
|
popToRoot()
|
||||||
let followers = FollowersModel(damus_state: damus_state!, target: id)
|
self.active_profile = id
|
||||||
navigationCoordinator.push(route: Route.Profile(profile: profile_model, followers: followers))
|
self.profile_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func open_search(filt: NostrFilter) {
|
func open_search(filt: NostrFilter) {
|
||||||
let search = SearchModel(state: damus_state!, search: filt)
|
popToRoot()
|
||||||
navigationCoordinator.push(route: Route.Search(search: search))
|
self.active_search = filt
|
||||||
|
self.search_open = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
if let damus = self.damus_state {
|
if let damus = self.damus_state {
|
||||||
NavigationStack(path: $navigationCoordinator.path) {
|
NavigationView {
|
||||||
TabView { // Prevents navbar appearance change on scroll
|
TabView { // Prevents navbar appearance change on scroll
|
||||||
MainContent(damus: damus)
|
MainContent(damus: damus)
|
||||||
.toolbar() {
|
.toolbar() {
|
||||||
@@ -284,12 +327,6 @@ struct ContentView: View {
|
|||||||
.overlay(
|
.overlay(
|
||||||
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation())
|
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation())
|
||||||
)
|
)
|
||||||
.navigationDestination(for: Route.self) { route in
|
|
||||||
route.view(navigationCordinator: navigationCoordinator, damusState: damus_state!)
|
|
||||||
}
|
|
||||||
.onReceive(handle_notify(.switched_timeline)) { _ in
|
|
||||||
navigationCoordinator.popToRoot()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationViewStyle(.stack)
|
.navigationViewStyle(.stack)
|
||||||
|
|
||||||
@@ -337,9 +374,7 @@ struct ContentView: View {
|
|||||||
case .filter(let filt): self.open_search(filt: filt)
|
case .filter(let filt): self.open_search(filt: filt)
|
||||||
case .profile(let id): self.open_profile(id: id)
|
case .profile(let id): self.open_profile(id: id)
|
||||||
case .event(let ev): self.open_event(ev: ev)
|
case .event(let ev): self.open_event(ev: ev)
|
||||||
case .wallet_connect(let nwc): self.open_wallet(nwc: nwc)
|
case .wallet_connect(let nwc): self.open_wallet(nwc: nwc)}
|
||||||
case .script(let data): self.open_script(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.compose)) { notif in
|
.onReceive(handle_notify(.compose)) { notif in
|
||||||
@@ -395,19 +430,16 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.unfollow)) { notif in
|
.onReceive(handle_notify(.unfollow)) { notif in
|
||||||
guard let state = self.damus_state else { return }
|
guard let state = self.damus_state else {
|
||||||
_ = handle_unfollow_notif(state: state, notif: notif)
|
return
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.unfollowed)) { notif in
|
handle_unfollow(state: state, notif: notif)
|
||||||
let unfollow = notif.object as! ReferencedId
|
|
||||||
home.resubscribe(.unfollowing(unfollow))
|
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.follow)) { notif in
|
.onReceive(handle_notify(.follow)) { notif in
|
||||||
guard let state = self.damus_state else { return }
|
guard let state = self.damus_state else {
|
||||||
guard handle_follow_notif(state: state, notif: notif) else { return }
|
return
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.followed)) { notif in
|
handle_follow(state: state, notif: notif)
|
||||||
home.resubscribe(.following)
|
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.post)) { notif in
|
.onReceive(handle_notify(.post)) { notif in
|
||||||
guard let state = self.damus_state,
|
guard let state = self.damus_state,
|
||||||
@@ -488,8 +520,8 @@ struct ContentView: View {
|
|||||||
switch local.type {
|
switch local.type {
|
||||||
case .dm:
|
case .dm:
|
||||||
selected_timeline = .dms
|
selected_timeline = .dms
|
||||||
damus_state.dms.set_active_dm(target.pubkey)
|
damus_state.dms.open_dm_by_pk(target.pubkey)
|
||||||
navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model))
|
|
||||||
case .like: fallthrough
|
case .like: fallthrough
|
||||||
case .zap: fallthrough
|
case .zap: fallthrough
|
||||||
case .mention: fallthrough
|
case .mention: fallthrough
|
||||||
@@ -612,44 +644,43 @@ struct ContentView: View {
|
|||||||
|
|
||||||
func connect() {
|
func connect() {
|
||||||
let pool = RelayPool()
|
let pool = RelayPool()
|
||||||
let model_cache = RelayModelCache()
|
let metadatas = RelayMetadatas()
|
||||||
let relay_filters = RelayFilters(our_pubkey: pubkey)
|
let relay_filters = RelayFilters(our_pubkey: pubkey)
|
||||||
let bootstrap_relays = load_bootstrap_relays(pubkey: pubkey)
|
let bootstrap_relays = load_bootstrap_relays(pubkey: pubkey)
|
||||||
|
|
||||||
|
let new_relay_filters = load_relay_filters(pubkey) == nil
|
||||||
|
for relay in bootstrap_relays {
|
||||||
|
if let url = RelayURL(relay) {
|
||||||
|
let descriptor = RelayDescriptor(url: url, info: .rw)
|
||||||
|
add_new_relay(relay_filters: relay_filters, metadatas: metadatas, pool: pool, descriptor: descriptor, new_relay_filters: new_relay_filters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.register_handler(sub_id: sub_id, handler: home.handle_event)
|
||||||
|
|
||||||
// dumb stuff needed for property wrappers
|
// dumb stuff needed for property wrappers
|
||||||
UserSettingsStore.pubkey = pubkey
|
UserSettingsStore.pubkey = pubkey
|
||||||
let settings = UserSettingsStore()
|
let settings = UserSettingsStore()
|
||||||
UserSettingsStore.shared = settings
|
UserSettingsStore.shared = settings
|
||||||
|
|
||||||
let new_relay_filters = load_relay_filters(pubkey) == nil
|
|
||||||
for relay in bootstrap_relays {
|
|
||||||
if let url = RelayURL(relay) {
|
|
||||||
let descriptor = RelayDescriptor(url: url, info: .rw)
|
|
||||||
add_new_relay(model_cache: model_cache, relay_filters: relay_filters, pool: pool, descriptor: descriptor, new_relay_filters: new_relay_filters, logging_enabled: settings.developer_mode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.register_handler(sub_id: sub_id, handler: home.handle_event)
|
|
||||||
|
|
||||||
if let nwc_str = settings.nostr_wallet_connect,
|
if let nwc_str = settings.nostr_wallet_connect,
|
||||||
let nwc = WalletConnectURL(str: nwc_str) {
|
let nwc = WalletConnectURL(str: nwc_str) {
|
||||||
try? pool.add_relay(.nwc(url: nwc.relay))
|
try? pool.add_relay(.nwc(url: nwc.relay))
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_search_cache = UserSearchCache()
|
|
||||||
self.damus_state = DamusState(pool: pool,
|
self.damus_state = DamusState(pool: pool,
|
||||||
keypair: keypair,
|
keypair: keypair,
|
||||||
likes: EventCounter(our_pubkey: pubkey),
|
likes: EventCounter(our_pubkey: pubkey),
|
||||||
boosts: EventCounter(our_pubkey: pubkey),
|
boosts: EventCounter(our_pubkey: pubkey),
|
||||||
contacts: Contacts(our_pubkey: pubkey),
|
contacts: Contacts(our_pubkey: pubkey),
|
||||||
profiles: Profiles(user_search_cache: user_search_cache),
|
profiles: Profiles(),
|
||||||
dms: home.dms,
|
dms: home.dms,
|
||||||
previews: PreviewCache(),
|
previews: PreviewCache(),
|
||||||
zaps: Zaps(our_pubkey: pubkey),
|
zaps: Zaps(our_pubkey: pubkey),
|
||||||
lnurls: LNUrls(),
|
lnurls: LNUrls(),
|
||||||
settings: settings,
|
settings: settings,
|
||||||
relay_filters: relay_filters,
|
relay_filters: relay_filters,
|
||||||
relay_model_cache: model_cache,
|
relay_metadata: metadatas,
|
||||||
drafts: Drafts(),
|
drafts: Drafts(),
|
||||||
events: EventCache(),
|
events: EventCache(),
|
||||||
bookmarks: BookmarksManager(pubkey: pubkey),
|
bookmarks: BookmarksManager(pubkey: pubkey),
|
||||||
@@ -657,9 +688,7 @@ struct ContentView: View {
|
|||||||
bootstrap_relays: bootstrap_relays,
|
bootstrap_relays: bootstrap_relays,
|
||||||
replies: ReplyCounter(our_pubkey: pubkey),
|
replies: ReplyCounter(our_pubkey: pubkey),
|
||||||
muted_threads: MutedThreadsManager(keypair: keypair),
|
muted_threads: MutedThreadsManager(keypair: keypair),
|
||||||
wallet: WalletModel(settings: settings),
|
wallet: WalletModel(settings: settings)
|
||||||
nav: self.navigationCoordinator,
|
|
||||||
user_search_cache: user_search_cache
|
|
||||||
)
|
)
|
||||||
home.damus_state = self.damus_state!
|
home.damus_state = self.damus_state!
|
||||||
|
|
||||||
@@ -882,75 +911,50 @@ func timeline_name(_ timeline: Timeline?) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
func handle_unfollow(state: DamusState, notif: Notification) {
|
||||||
func handle_unfollow(state: DamusState, unfollow: ReferencedId) -> Bool {
|
guard let privkey = state.keypair.privkey else {
|
||||||
guard let keypair = state.keypair.to_full() else {
|
return
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let old_contacts = state.contacts.event
|
|
||||||
|
|
||||||
guard let ev = unfollow_reference(postbox: state.postbox, our_contacts: old_contacts, keypair: keypair, unfollow: unfollow)
|
|
||||||
else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
notify(.unfollowed, unfollow)
|
|
||||||
|
|
||||||
state.contacts.event = ev
|
|
||||||
|
|
||||||
if unfollow.key == "p" {
|
|
||||||
state.contacts.remove_friend(unfollow.ref_id)
|
|
||||||
state.user_search_cache.updateOwnContactsPetnames(id: state.pubkey, oldEvent: old_contacts, newEvent: ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle_unfollow_notif(state: DamusState, notif: Notification) -> ReferencedId? {
|
|
||||||
let target = notif.object as! FollowTarget
|
let target = notif.object as! FollowTarget
|
||||||
let pk = target.pubkey
|
let pk = target.pubkey
|
||||||
|
|
||||||
let ref = ReferencedId.p(pk)
|
if let ev = unfollow_user(postbox: state.postbox,
|
||||||
if handle_unfollow(state: state, unfollow: ref) {
|
our_contacts: state.contacts.event,
|
||||||
return ref
|
pubkey: state.pubkey,
|
||||||
|
privkey: privkey,
|
||||||
|
unfollow: pk) {
|
||||||
|
notify(.unfollowed, pk)
|
||||||
|
|
||||||
|
state.contacts.event = ev
|
||||||
|
state.contacts.remove_friend(pk)
|
||||||
|
//friend_events = friend_events.filter { $0.pubkey != pk }
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult
|
func handle_follow(state: DamusState, notif: Notification) {
|
||||||
func handle_follow(state: DamusState, follow: ReferencedId) -> Bool {
|
guard let privkey = state.keypair.privkey else {
|
||||||
guard let keypair = state.keypair.to_full() else {
|
return
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let ev = follow_reference(box: state.postbox, our_contacts: state.contacts.event, keypair: keypair, follow: follow)
|
|
||||||
else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
notify(.followed, follow)
|
|
||||||
|
|
||||||
state.contacts.event = ev
|
|
||||||
if follow.key == "p" {
|
|
||||||
state.contacts.add_friend_pubkey(follow.ref_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
func handle_follow_notif(state: DamusState, notif: Notification) -> Bool {
|
|
||||||
let fnotify = notif.object as! FollowTarget
|
let fnotify = notif.object as! FollowTarget
|
||||||
switch fnotify {
|
|
||||||
case .pubkey(let pk):
|
|
||||||
state.contacts.add_friend_pubkey(pk)
|
|
||||||
case .contact(let ev):
|
|
||||||
state.contacts.add_friend_contact(ev)
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle_follow(state: state, follow: .p(fnotify.pubkey))
|
if let ev = follow_user(pool: state.pool,
|
||||||
|
our_contacts: state.contacts.event,
|
||||||
|
pubkey: state.pubkey,
|
||||||
|
privkey: privkey,
|
||||||
|
follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) {
|
||||||
|
notify(.followed, fnotify.pubkey)
|
||||||
|
|
||||||
|
state.contacts.event = ev
|
||||||
|
|
||||||
|
switch fnotify {
|
||||||
|
case .pubkey(let pk):
|
||||||
|
state.contacts.add_friend_pubkey(pk)
|
||||||
|
case .contact(let ev):
|
||||||
|
state.contacts.add_friend_contact(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_post_notification(keypair: FullKeypair, postbox: PostBox, events: EventCache, notif: Notification) -> Bool {
|
func handle_post_notification(keypair: FullKeypair, postbox: PostBox, events: EventCache, notif: Notification) -> Bool {
|
||||||
@@ -981,7 +985,6 @@ enum OpenResult {
|
|||||||
case filter(NostrFilter)
|
case filter(NostrFilter)
|
||||||
case event(NostrEvent)
|
case event(NostrEvent)
|
||||||
case wallet_connect(WalletConnectURL)
|
case wallet_connect(WalletConnectURL)
|
||||||
case script([UInt8])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func on_open_url(state: DamusState, url: URL, result: @escaping (OpenResult?) -> Void) {
|
func on_open_url(state: DamusState, url: URL, result: @escaping (OpenResult?) -> Void) {
|
||||||
@@ -1009,9 +1012,5 @@ func on_open_url(state: DamusState, url: URL, result: @escaping (OpenResult?) ->
|
|||||||
result(.filter(filt))
|
result(.filter(filt))
|
||||||
break
|
break
|
||||||
// TODO: handle filter searches?
|
// TODO: handle filter searches?
|
||||||
case .script(let script):
|
|
||||||
result(.script(script))
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-30
@@ -66,19 +66,10 @@ class Contacts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_friend_list() -> Set<String> {
|
func get_friend_list() -> [String] {
|
||||||
return friends
|
return Array(friends)
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_followed_hashtags() -> Set<String> {
|
|
||||||
guard let ev = self.event else { return Set() }
|
|
||||||
return ev.tags.reduce(into: Set<String>(), { htags, tag in
|
|
||||||
if tag.count >= 2 && tag[0] == "t" && tag[1] != "" {
|
|
||||||
htags.insert(tag[1])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func add_friend_pubkey(_ pubkey: String) {
|
func add_friend_pubkey(_ pubkey: String) {
|
||||||
friends.insert(pubkey)
|
friends.insert(pubkey)
|
||||||
}
|
}
|
||||||
@@ -127,36 +118,37 @@ class Contacts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func follow_reference(box: PostBox, our_contacts: NostrEvent?, keypair: FullKeypair, follow: ReferencedId) -> NostrEvent? {
|
func follow_user(pool: RelayPool, our_contacts: NostrEvent?, pubkey: String, privkey: String, follow: ReferencedId) -> NostrEvent? {
|
||||||
guard let ev = follow_user_event(our_contacts: our_contacts, our_pubkey: keypair.pubkey, follow: follow) else {
|
guard let ev = follow_user_event(our_contacts: our_contacts, our_pubkey: pubkey, follow: follow) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ev.calculate_id()
|
ev.calculate_id()
|
||||||
ev.sign(privkey: keypair.privkey)
|
ev.sign(privkey: privkey)
|
||||||
|
|
||||||
box.send(ev)
|
|
||||||
|
pool.send(.event(ev))
|
||||||
|
|
||||||
return ev
|
return ev
|
||||||
}
|
}
|
||||||
|
|
||||||
func unfollow_reference(postbox: PostBox, our_contacts: NostrEvent?, keypair: FullKeypair, unfollow: ReferencedId) -> NostrEvent? {
|
func unfollow_user(postbox: PostBox, our_contacts: NostrEvent?, pubkey: String, privkey: String, unfollow: String) -> NostrEvent? {
|
||||||
guard let cs = our_contacts else {
|
guard let cs = our_contacts else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let ev = unfollow_reference_event(our_contacts: cs, our_pubkey: keypair.pubkey, unfollow: unfollow)
|
let ev = unfollow_user_event(our_contacts: cs, our_pubkey: pubkey, unfollow: unfollow)
|
||||||
ev.calculate_id()
|
ev.calculate_id()
|
||||||
ev.sign(privkey: keypair.privkey)
|
ev.sign(privkey: privkey)
|
||||||
|
|
||||||
postbox.send(ev)
|
postbox.send(ev)
|
||||||
|
|
||||||
return ev
|
return ev
|
||||||
}
|
}
|
||||||
|
|
||||||
func unfollow_reference_event(our_contacts: NostrEvent, our_pubkey: String, unfollow: ReferencedId) -> NostrEvent {
|
func unfollow_user_event(our_contacts: NostrEvent, our_pubkey: String, unfollow: String) -> NostrEvent {
|
||||||
let tags = our_contacts.tags.filter { tag in
|
let tags = our_contacts.tags.filter { tag in
|
||||||
if tag.count >= 2 && tag[0] == unfollow.key && tag[1] == unfollow.ref_id {
|
if tag.count >= 2 && tag[0] == "p" && tag[1] == unfollow {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -228,16 +220,12 @@ func ensure_relay_info(relays: [RelayDescriptor], content: String) -> [String: R
|
|||||||
return relay_info
|
return relay_info
|
||||||
}
|
}
|
||||||
|
|
||||||
func is_already_following(contacts: NostrEvent, follow: ReferencedId) -> Bool {
|
|
||||||
return contacts.references(id: follow.ref_id, key: follow.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func follow_with_existing_contacts(our_pubkey: String, our_contacts: NostrEvent, follow: ReferencedId) -> NostrEvent? {
|
func follow_with_existing_contacts(our_pubkey: String, our_contacts: NostrEvent, follow: ReferencedId) -> NostrEvent? {
|
||||||
// don't update if we're already following
|
// don't update if we're already following
|
||||||
if is_already_following(contacts: our_contacts, follow: follow) {
|
if our_contacts.references(id: follow.ref_id, key: "p") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let kind = NostrKind.contacts.rawValue
|
let kind = NostrKind.contacts.rawValue
|
||||||
var tags = our_contacts.tags
|
var tags = our_contacts.tags
|
||||||
tags.append(refid_to_tag(follow))
|
tags.append(refid_to_tag(follow))
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ class CreateAccountModel: ObservableObject {
|
|||||||
@Published var about: String = ""
|
@Published var about: String = ""
|
||||||
@Published var pubkey: String = ""
|
@Published var pubkey: String = ""
|
||||||
@Published var privkey: String = ""
|
@Published var privkey: String = ""
|
||||||
@Published var profile_image: URL? = nil
|
@Published var profile_image: String? = nil
|
||||||
|
|
||||||
var pubkey_bech32: String {
|
var pubkey_bech32: String {
|
||||||
return bech32_pubkey(self.pubkey) ?? ""
|
return bech32_pubkey(self.pubkey) ?? ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ struct DamusState {
|
|||||||
let lnurls: LNUrls
|
let lnurls: LNUrls
|
||||||
let settings: UserSettingsStore
|
let settings: UserSettingsStore
|
||||||
let relay_filters: RelayFilters
|
let relay_filters: RelayFilters
|
||||||
let relay_model_cache: RelayModelCache
|
let relay_metadata: RelayMetadatas
|
||||||
let drafts: Drafts
|
let drafts: Drafts
|
||||||
let events: EventCache
|
let events: EventCache
|
||||||
let bookmarks: BookmarksManager
|
let bookmarks: BookmarksManager
|
||||||
@@ -30,8 +30,6 @@ struct DamusState {
|
|||||||
let replies: ReplyCounter
|
let replies: ReplyCounter
|
||||||
let muted_threads: MutedThreadsManager
|
let muted_threads: MutedThreadsManager
|
||||||
let wallet: WalletModel
|
let wallet: WalletModel
|
||||||
let nav: NavigationCoordinator
|
|
||||||
let user_search_cache: UserSearchCache
|
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
func add_zap(zap: Zapping) -> Bool {
|
func add_zap(zap: Zapping) -> Bool {
|
||||||
@@ -59,6 +57,5 @@ struct DamusState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static var empty: DamusState {
|
static var empty: DamusState {
|
||||||
let user_search_cache = UserSearchCache()
|
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""), postbox: PostBox(pool: RelayPool()), bootstrap_relays: [], replies: ReplyCounter(our_pubkey: ""), muted_threads: MutedThreadsManager(keypair: Keypair(pubkey: "", privkey: nil)), wallet: WalletModel(settings: UserSettingsStore())) }
|
||||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), profiles: Profiles(user_search_cache: user_search_cache), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_model_cache: RelayModelCache(), drafts: Drafts(), events: EventCache(), bookmarks: BookmarksManager(pubkey: ""), postbox: PostBox(pool: RelayPool()), bootstrap_relays: [], replies: ReplyCounter(our_pubkey: ""), muted_threads: MutedThreadsManager(keypair: Keypair(pubkey: "", privkey: nil)), wallet: WalletModel(settings: UserSettingsStore()), nav: NavigationCoordinator(), user_search_cache: user_search_cache) }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ class DirectMessagesModel: ObservableObject {
|
|||||||
self.active_model = model
|
self.active_model = model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func open_dm_by_pk(_ pubkey: String) {
|
||||||
|
self.set_active_dm(pubkey)
|
||||||
|
self.open_dm = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func open_dm_by_model(_ model: DirectMessageModel) {
|
||||||
|
self.set_active_dm_model(model)
|
||||||
|
self.open_dm = true
|
||||||
|
}
|
||||||
|
|
||||||
func set_active_dm(_ pubkey: String) {
|
func set_active_dm(_ pubkey: String) {
|
||||||
for model in self.dms where model.pubkey == pubkey {
|
for model in self.dms where model.pubkey == pubkey {
|
||||||
self.set_active_dm_model(model)
|
self.set_active_dm_model(model)
|
||||||
|
|||||||
+70
-220
@@ -23,40 +23,6 @@ struct NewEventsBits: OptionSet {
|
|||||||
static let notifications: NewEventsBits = [.zaps, .likes, .reposts, .mentions]
|
static let notifications: NewEventsBits = [.zaps, .likes, .reposts, .mentions]
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Resubscribe {
|
|
||||||
case following
|
|
||||||
case unfollowing(ReferencedId)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum HomeResubFilter {
|
|
||||||
case pubkey(String)
|
|
||||||
case hashtag(String)
|
|
||||||
|
|
||||||
init?(from: ReferencedId) {
|
|
||||||
if from.key == "p" {
|
|
||||||
self = .pubkey(from.ref_id)
|
|
||||||
return
|
|
||||||
} else if from.key == "t" {
|
|
||||||
self = .hashtag(from.ref_id)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func filter(contacts: Contacts, ev: NostrEvent) -> Bool {
|
|
||||||
switch self {
|
|
||||||
case .pubkey(let pk):
|
|
||||||
return ev.pubkey == pk
|
|
||||||
case .hashtag(let ht):
|
|
||||||
if contacts.is_friend(ev.pubkey) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return ev.references(id: ht, key: "t")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HomeModel {
|
class HomeModel {
|
||||||
// Don't trigger a user notification for events older than a certain age
|
// Don't trigger a user notification for events older than a certain age
|
||||||
static let event_max_age_for_notification: TimeInterval = 12 * 60 * 60
|
static let event_max_age_for_notification: TimeInterval = 12 * 60 * 60
|
||||||
@@ -70,7 +36,6 @@ class HomeModel {
|
|||||||
var done_init: Bool = false
|
var done_init: Bool = false
|
||||||
var incoming_dms: [NostrEvent] = []
|
var incoming_dms: [NostrEvent] = []
|
||||||
let dm_debouncer = Debouncer(interval: 0.5)
|
let dm_debouncer = Debouncer(interval: 0.5)
|
||||||
let resub_debouncer = Debouncer(interval: 3.0)
|
|
||||||
var should_debounce_dms = true
|
var should_debounce_dms = true
|
||||||
|
|
||||||
let home_subid = UUID().description
|
let home_subid = UUID().description
|
||||||
@@ -125,31 +90,6 @@ class HomeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resubscribe(_ resubbing: Resubscribe) {
|
|
||||||
if self.should_debounce_dms {
|
|
||||||
// don't resub on initial load
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
print("hit resub debouncer")
|
|
||||||
|
|
||||||
resub_debouncer.debounce {
|
|
||||||
print("resub")
|
|
||||||
self.unsubscribe_to_home_filters()
|
|
||||||
|
|
||||||
switch resubbing {
|
|
||||||
case .following:
|
|
||||||
break
|
|
||||||
case .unfollowing(let r):
|
|
||||||
if let filter = HomeResubFilter(from: r) {
|
|
||||||
self.events.filter { ev in !filter.filter(contacts: self.damus_state.contacts, ev: ev) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.subscribe_to_home_filters()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func process_event(sub_id: String, relay_id: String, ev: NostrEvent) {
|
func process_event(sub_id: String, relay_id: String, ev: NostrEvent) {
|
||||||
if has_sub_id_event(sub_id: sub_id, ev_id: ev.id) {
|
if has_sub_id_event(sub_id: sub_id, ev_id: ev.id) {
|
||||||
return
|
return
|
||||||
@@ -166,7 +106,6 @@ class HomeModel {
|
|||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case .chat: fallthrough
|
case .chat: fallthrough
|
||||||
case .longform: fallthrough
|
|
||||||
case .text:
|
case .text:
|
||||||
handle_text_event(sub_id: sub_id, ev)
|
handle_text_event(sub_id: sub_id, ev)
|
||||||
case .contacts:
|
case .contacts:
|
||||||
@@ -215,14 +154,14 @@ class HomeModel {
|
|||||||
print("nwc: \(resp.req_id) not found in the postbox, nothing to remove [\(relay)]")
|
print("nwc: \(resp.req_id) not found in the postbox, nothing to remove [\(relay)]")
|
||||||
}
|
}
|
||||||
|
|
||||||
guard resp.response.error == nil else {
|
guard let err = resp.response.error else {
|
||||||
print("nwc error: \(resp.response)")
|
print("nwc success: \(resp.response.result.debugDescription) [\(relay)]")
|
||||||
nwc_error(zapcache: self.damus_state.zaps, evcache: self.damus_state.events, resp: resp)
|
nwc_success(state: self.damus_state, resp: resp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print("nwc success: \(resp.response.result.debugDescription) [\(relay)]")
|
print("nwc error: \(resp.response)")
|
||||||
nwc_success(state: self.damus_state, resp: resp)
|
nwc_error(zapcache: self.damus_state.zaps, evcache: self.damus_state.events, resp: resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,7 +381,8 @@ class HomeModel {
|
|||||||
// TODO: since times should be based on events from a specific relay
|
// TODO: since times should be based on events from a specific relay
|
||||||
// perhaps we could mark this in the relay pool somehow
|
// perhaps we could mark this in the relay pool somehow
|
||||||
|
|
||||||
let friends = get_friends()
|
var friends = damus_state.contacts.get_friend_list()
|
||||||
|
friends.append(damus_state.pubkey)
|
||||||
|
|
||||||
var contacts_filter = NostrFilter(kinds: [.metadata])
|
var contacts_filter = NostrFilter(kinds: [.metadata])
|
||||||
contacts_filter.authors = friends
|
contacts_filter.authors = friends
|
||||||
@@ -464,6 +404,19 @@ class HomeModel {
|
|||||||
dms_filter.pubkeys = [ damus_state.pubkey ]
|
dms_filter.pubkeys = [ damus_state.pubkey ]
|
||||||
our_dms_filter.authors = [ damus_state.pubkey ]
|
our_dms_filter.authors = [ damus_state.pubkey ]
|
||||||
|
|
||||||
|
// TODO: separate likes?
|
||||||
|
var home_filter_kinds: [NostrKind] = [
|
||||||
|
.text,
|
||||||
|
.boost
|
||||||
|
]
|
||||||
|
if !damus_state.settings.onlyzaps_mode {
|
||||||
|
home_filter_kinds.append(.like)
|
||||||
|
}
|
||||||
|
var home_filter = NostrFilter(kinds: home_filter_kinds)
|
||||||
|
// include our pubkey as well even if we're not technically a friend
|
||||||
|
home_filter.authors = friends
|
||||||
|
home_filter.limit = 500
|
||||||
|
|
||||||
var notifications_filter_kinds: [NostrKind] = [
|
var notifications_filter_kinds: [NostrKind] = [
|
||||||
.text,
|
.text,
|
||||||
.boost,
|
.boost,
|
||||||
@@ -476,71 +429,33 @@ class HomeModel {
|
|||||||
notifications_filter.pubkeys = [damus_state.pubkey]
|
notifications_filter.pubkeys = [damus_state.pubkey]
|
||||||
notifications_filter.limit = 500
|
notifications_filter.limit = 500
|
||||||
|
|
||||||
|
var home_filters = [home_filter]
|
||||||
var notifications_filters = [notifications_filter]
|
var notifications_filters = [notifications_filter]
|
||||||
var contacts_filters = [contacts_filter, our_contacts_filter, our_blocklist_filter]
|
var contacts_filters = [contacts_filter, our_contacts_filter, our_blocklist_filter]
|
||||||
var dms_filters = [dms_filter, our_dms_filter]
|
var dms_filters = [dms_filter, our_dms_filter]
|
||||||
let last_of_kind = get_last_of_kind(relay_id: relay_id)
|
|
||||||
|
|
||||||
|
let last_of_kind = relay_id.flatMap { last_event_of_kind[$0] } ?? [:]
|
||||||
|
|
||||||
|
home_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: home_filters)
|
||||||
contacts_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: contacts_filters)
|
contacts_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: contacts_filters)
|
||||||
notifications_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: notifications_filters)
|
notifications_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: notifications_filters)
|
||||||
dms_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: dms_filters)
|
dms_filters = update_filters_with_since(last_of_kind: last_of_kind, filters: dms_filters)
|
||||||
|
|
||||||
//print_filters(relay_id: relay_id, filters: [home_filters, contacts_filters, notifications_filters, dms_filters])
|
//print_filters(relay_id: relay_id, filters: [home_filters, contacts_filters, notifications_filters, dms_filters])
|
||||||
|
|
||||||
subscribe_to_home_filters(relay_id: relay_id)
|
if let relay_id {
|
||||||
|
pool.send(.subscribe(.init(filters: home_filters, sub_id: home_subid)), to: [relay_id])
|
||||||
let relay_ids = relay_id.map { [$0] }
|
pool.send(.subscribe(.init(filters: contacts_filters, sub_id: contacts_subid)), to: [relay_id])
|
||||||
|
pool.send(.subscribe(.init(filters: notifications_filters, sub_id: notifications_subid)), to: [relay_id])
|
||||||
pool.send(.subscribe(.init(filters: contacts_filters, sub_id: contacts_subid)), to: relay_ids)
|
pool.send(.subscribe(.init(filters: dms_filters, sub_id: dms_subid)), to: [relay_id])
|
||||||
pool.send(.subscribe(.init(filters: notifications_filters, sub_id: notifications_subid)), to: relay_ids)
|
} else {
|
||||||
pool.send(.subscribe(.init(filters: dms_filters, sub_id: dms_subid)), to: relay_ids)
|
pool.send(.subscribe(.init(filters: home_filters, sub_id: home_subid)))
|
||||||
}
|
pool.send(.subscribe(.init(filters: contacts_filters, sub_id: contacts_subid)))
|
||||||
|
pool.send(.subscribe(.init(filters: notifications_filters, sub_id: notifications_subid)))
|
||||||
func get_last_of_kind(relay_id: String?) -> [Int: NostrEvent] {
|
pool.send(.subscribe(.init(filters: dms_filters, sub_id: dms_subid)))
|
||||||
return relay_id.flatMap { last_event_of_kind[$0] } ?? [:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsubscribe_to_home_filters() {
|
|
||||||
pool.send(.unsubscribe(home_subid))
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_friends() -> [String] {
|
|
||||||
var friends = damus_state.contacts.get_friend_list()
|
|
||||||
friends.insert(damus_state.pubkey)
|
|
||||||
return Array(friends)
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribe_to_home_filters(friends fs: [String]? = nil, relay_id: String? = nil) {
|
|
||||||
// TODO: separate likes?
|
|
||||||
var home_filter_kinds: [NostrKind] = [
|
|
||||||
.text, .longform, .boost
|
|
||||||
]
|
|
||||||
if !damus_state.settings.onlyzaps_mode {
|
|
||||||
home_filter_kinds.append(.like)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let friends = fs ?? get_friends()
|
|
||||||
var home_filter = NostrFilter(kinds: home_filter_kinds)
|
|
||||||
// include our pubkey as well even if we're not technically a friend
|
|
||||||
home_filter.authors = friends
|
|
||||||
home_filter.limit = 500
|
|
||||||
|
|
||||||
var home_filters = [home_filter]
|
|
||||||
|
|
||||||
let followed_hashtags = Array(damus_state.contacts.get_followed_hashtags())
|
|
||||||
if followed_hashtags.count != 0 {
|
|
||||||
var hashtag_filter = NostrFilter.filter_hashtag(followed_hashtags)
|
|
||||||
hashtag_filter.limit = 100
|
|
||||||
home_filters.append(hashtag_filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
let relay_ids = relay_id.map { [$0] }
|
|
||||||
home_filters = update_filters_with_since(last_of_kind: get_last_of_kind(relay_id: relay_id), filters: home_filters)
|
|
||||||
let sub = NostrSubscribe(filters: home_filters, sub_id: home_subid)
|
|
||||||
|
|
||||||
pool.send(.subscribe(sub), to: relay_ids)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_list_event(_ ev: NostrEvent) {
|
func handle_list_event(_ ev: NostrEvent) {
|
||||||
// we only care about our lists
|
// we only care about our lists
|
||||||
guard ev.pubkey == damus_state.pubkey else {
|
guard ev.pubkey == damus_state.pubkey else {
|
||||||
@@ -697,40 +612,35 @@ func add_contact_if_friend(contacts: Contacts, ev: NostrEvent) {
|
|||||||
contacts.add_friend_contact(ev)
|
contacts.add_friend_contact(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_our_contacts(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
func load_our_contacts(contacts: Contacts, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
||||||
let contacts = state.contacts
|
var new_pks = Set<String>()
|
||||||
var new_refs = Set<ReferencedId>()
|
|
||||||
// our contacts
|
// our contacts
|
||||||
for tag in ev.tags {
|
for tag in ev.tags {
|
||||||
guard let ref = tag_to_refid(tag) else { continue }
|
if tag.count >= 2 && tag[0] == "p" {
|
||||||
new_refs.insert(ref)
|
new_pks.insert(tag[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var old_refs = Set<ReferencedId>()
|
var old_pks = Set<String>()
|
||||||
// find removed contacts
|
// find removed contacts
|
||||||
if let old_ev = m_old_ev {
|
if let old_ev = m_old_ev {
|
||||||
for tag in old_ev.tags {
|
for tag in old_ev.tags {
|
||||||
guard let ref = tag_to_refid(tag) else { continue }
|
if tag.count >= 2 && tag[0] == "p" {
|
||||||
old_refs.insert(ref)
|
old_pks.insert(tag[1])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let diff = new_refs.symmetricDifference(old_refs)
|
let diff = new_pks.symmetricDifference(old_pks)
|
||||||
for ref in diff {
|
for pk in diff {
|
||||||
if new_refs.contains(ref) {
|
if new_pks.contains(pk) {
|
||||||
notify(.followed, ref)
|
notify(.followed, pk)
|
||||||
if ref.key == "p" {
|
contacts.add_friend_pubkey(pk)
|
||||||
contacts.add_friend_pubkey(ref.ref_id)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
notify(.unfollowed, ref)
|
notify(.unfollowed, pk)
|
||||||
if ref.key == "p" {
|
contacts.remove_friend(pk)
|
||||||
contacts.remove_friend(ref.ref_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.user_search_cache.updateOwnContactsPetnames(id: contacts.our_pubkey, oldEvent: m_old_ev, newEvent: ev)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -791,9 +701,7 @@ func process_metadata_profile(our_pubkey: String, profiles: Profiles, profile: P
|
|||||||
}
|
}
|
||||||
|
|
||||||
var old_nip05: String? = nil
|
var old_nip05: String? = nil
|
||||||
let mprof = profiles.lookup_with_timestamp(id: ev.pubkey)
|
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||||
|
|
||||||
if let mprof {
|
|
||||||
old_nip05 = mprof.profile.nip05
|
old_nip05 = mprof.profile.nip05
|
||||||
if mprof.event.created_at > ev.created_at {
|
if mprof.event.created_at > ev.created_at {
|
||||||
// skip if we already have an newer profile
|
// skip if we already have an newer profile
|
||||||
@@ -813,7 +721,7 @@ func process_metadata_profile(our_pubkey: String, profiles: Profiles, profile: P
|
|||||||
}
|
}
|
||||||
|
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
profiles.set_validated(ev.pubkey, nip05: validated)
|
profiles.validated[ev.pubkey] = validated
|
||||||
profiles.nip05_pubkey[nip05] = ev.pubkey
|
profiles.nip05_pubkey[nip05] = ev.pubkey
|
||||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||||
}
|
}
|
||||||
@@ -840,14 +748,11 @@ func process_metadata_profile(our_pubkey: String, profiles: Profiles, profile: P
|
|||||||
}
|
}
|
||||||
|
|
||||||
func guard_valid_event(events: EventCache, ev: NostrEvent, callback: @escaping () -> Void) {
|
func guard_valid_event(events: EventCache, ev: NostrEvent, callback: @escaping () -> Void) {
|
||||||
guard ev.id==calculate_event_id(ev: ev) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let validated = events.is_event_valid(ev.id)
|
let validated = events.is_event_valid(ev.id)
|
||||||
|
|
||||||
switch validated {
|
switch validated {
|
||||||
case .unknown:
|
case .unknown:
|
||||||
Task.detached(priority: .medium) {
|
Task {
|
||||||
let result = validate_event(ev: ev)
|
let result = validate_event(ev: ev)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@@ -905,7 +810,7 @@ func load_our_stuff(state: DamusState, ev: NostrEvent) {
|
|||||||
let m_old_ev = state.contacts.event
|
let m_old_ev = state.contacts.event
|
||||||
state.contacts.event = ev
|
state.contacts.event = ev
|
||||||
|
|
||||||
load_our_contacts(state: state, m_old_ev: m_old_ev, ev: ev)
|
load_our_contacts(contacts: state.contacts, m_old_ev: m_old_ev, ev: ev)
|
||||||
load_our_relays(state: state, m_old_ev: m_old_ev, ev: ev)
|
load_our_relays(state: state, m_old_ev: m_old_ev, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,7 +849,7 @@ func load_our_relays(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
|||||||
if new.contains(d) {
|
if new.contains(d) {
|
||||||
if let url = RelayURL(d) {
|
if let url = RelayURL(d) {
|
||||||
let descriptor = RelayDescriptor(url: url, info: decoded[d] ?? .rw)
|
let descriptor = RelayDescriptor(url: url, info: decoded[d] ?? .rw)
|
||||||
add_new_relay(model_cache: state.relay_model_cache, relay_filters: state.relay_filters, pool: state.pool, descriptor: descriptor, new_relay_filters: new_relay_filters, logging_enabled: state.settings.developer_mode)
|
add_new_relay(relay_filters: state.relay_filters, metadatas: state.relay_metadata, pool: state.pool, descriptor: descriptor, new_relay_filters: new_relay_filters)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state.pool.remove_relay(d)
|
state.pool.remove_relay(d)
|
||||||
@@ -953,17 +858,16 @@ func load_our_relays(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
|||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
save_bootstrap_relays(pubkey: state.pubkey, relays: Array(new))
|
save_bootstrap_relays(pubkey: state.pubkey, relays: Array(new))
|
||||||
state.pool.connect()
|
|
||||||
notify(.relays_changed, ())
|
notify(.relays_changed, ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func add_new_relay(model_cache: RelayModelCache, relay_filters: RelayFilters, pool: RelayPool, descriptor: RelayDescriptor, new_relay_filters: Bool, logging_enabled: Bool) {
|
func add_new_relay(relay_filters: RelayFilters, metadatas: RelayMetadatas, pool: RelayPool, descriptor: RelayDescriptor, new_relay_filters: Bool) {
|
||||||
try? pool.add_relay(descriptor)
|
try? pool.add_relay(descriptor)
|
||||||
let url = descriptor.url
|
let url = descriptor.url
|
||||||
|
|
||||||
let relay_id = url.id
|
let relay_id = url.id
|
||||||
guard model_cache.model(withURL: url) == nil else {
|
guard metadatas.lookup(relay_id: relay_id) == nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,13 +876,8 @@ func add_new_relay(model_cache: RelayModelCache, relay_filters: RelayFilters, po
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await MainActor.run {
|
DispatchQueue.main.async {
|
||||||
let model = RelayModel(url, metadata: meta)
|
metadatas.insert(relay_id: relay_id, metadata: meta)
|
||||||
model_cache.insert(model: model)
|
|
||||||
|
|
||||||
if logging_enabled {
|
|
||||||
pool.setLog(model.log, for: relay_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is the first time adding filters, we should filter non-paid relays
|
// if this is the first time adding filters, we should filter non-paid relays
|
||||||
if new_relay_filters && !meta.is_paid {
|
if new_relay_filters && !meta.is_paid {
|
||||||
@@ -1234,27 +1133,6 @@ func create_in_app_event_zap_notification(profiles: Profiles, zap: Zap, locale:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func render_notification_content_preview(cache: EventCache, ev: NostrEvent, profiles: Profiles, privkey: String?) -> String {
|
|
||||||
|
|
||||||
let prefix_len = 50
|
|
||||||
let artifacts = cache.get_cache_data(ev.id).artifacts.artifacts ?? render_note_content(ev: ev, profiles: profiles, privkey: privkey)
|
|
||||||
|
|
||||||
// special case for longform events
|
|
||||||
if ev.known_kind == .longform {
|
|
||||||
let longform = LongformEvent(event: ev)
|
|
||||||
return longform.title ?? longform.summary ?? "Longform Event"
|
|
||||||
}
|
|
||||||
|
|
||||||
switch artifacts {
|
|
||||||
case .parts:
|
|
||||||
// we should never hit this until we have more note types built out of parts
|
|
||||||
// since we handle this case above in known_kind == .longform
|
|
||||||
return String(ev.content.prefix(prefix_len))
|
|
||||||
|
|
||||||
case .separated(let artifacts):
|
|
||||||
return String(NSAttributedString(artifacts.content.attributed).string.prefix(prefix_len))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
||||||
guard let type = ev.known_kind else {
|
guard let type = ev.known_kind else {
|
||||||
@@ -1278,22 +1156,23 @@ func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if type == .text && damus_state.settings.mention_notification {
|
if type == .text && damus_state.settings.mention_notification {
|
||||||
let blocks = ev.blocks(damus_state.keypair.privkey).blocks
|
let blocks = ev.blocks(damus_state.keypair.privkey)
|
||||||
for case .mention(let mention) in blocks where mention.ref.ref_id == damus_state.keypair.pubkey {
|
for case .mention(let mention) in blocks where mention.ref.ref_id == damus_state.keypair.pubkey {
|
||||||
let content_preview = render_notification_content_preview(cache: damus_state.events, ev: ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
let content = NSAttributedString(render_note_content(ev: ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
||||||
let notify = LocalNotification(type: .mention, event: ev, target: ev, content: content_preview)
|
|
||||||
|
let notify = LocalNotification(type: .mention, event: ev, target: ev, content: content)
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify )
|
create_local_notification(profiles: damus_state.profiles, notify: notify )
|
||||||
}
|
}
|
||||||
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
|
||||||
let content_preview = render_notification_content_preview(cache: damus_state.events, ev: inner_ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
let content = NSAttributedString(render_note_content(ev: inner_ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
||||||
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: content_preview)
|
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: content)
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||||
} else if type == .like && damus_state.settings.like_notification,
|
} else if type == .like && damus_state.settings.like_notification,
|
||||||
let evid = ev.referenced_ids.last?.ref_id,
|
let evid = ev.referenced_ids.last?.ref_id,
|
||||||
let liked_event = damus_state.events.lookup(evid)
|
let liked_event = damus_state.events.lookup(evid)
|
||||||
{
|
{
|
||||||
let content_preview = render_notification_content_preview(cache: damus_state.events, ev: liked_event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
let content = NSAttributedString(render_note_content(ev: liked_event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
|
||||||
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: content_preview)
|
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: content)
|
||||||
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
create_local_notification(profiles: damus_state.profiles, notify: notify)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1348,42 +1227,13 @@ enum ProcessZapResult {
|
|||||||
case failed
|
case failed
|
||||||
}
|
}
|
||||||
|
|
||||||
// securely get the zap target's pubkey. this can be faked so we need to be
|
|
||||||
// careful
|
|
||||||
func get_zap_target_pubkey(ev: NostrEvent, events: EventCache) -> String? {
|
|
||||||
let etags = ev.referenced_ids
|
|
||||||
|
|
||||||
guard let etag = etags.first else {
|
|
||||||
// no etags, ptag-only case
|
|
||||||
|
|
||||||
let ptags = ev.referenced_pubkeys
|
|
||||||
|
|
||||||
// ensure that there is only 1 ptag to stop fake profile zap attacks
|
|
||||||
guard ptags.count == 1 else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ptags.first?.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have an e-tag
|
|
||||||
|
|
||||||
// ensure that there is only 1 etag to stop fake note zap attacks
|
|
||||||
guard etags.count == 1 else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// we can't trust the p tag on note zaps because they can be faked
|
|
||||||
return events.lookup(etag.id)?.pubkey
|
|
||||||
}
|
|
||||||
|
|
||||||
func process_zap_event(damus_state: DamusState, ev: NostrEvent, completion: @escaping (ProcessZapResult) -> Void) {
|
func process_zap_event(damus_state: DamusState, ev: NostrEvent, completion: @escaping (ProcessZapResult) -> Void) {
|
||||||
// These are zap notifications
|
// These are zap notifications
|
||||||
guard let ptag = get_zap_target_pubkey(ev: ev, events: damus_state.events) else {
|
guard let ptag = event_tag(ev, name: "p") else {
|
||||||
completion(.failed)
|
completion(.failed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// just return the zap if we already have it
|
// just return the zap if we already have it
|
||||||
if let zap = damus_state.zaps.zaps[ev.id], case .zap(let z) = zap {
|
if let zap = damus_state.zaps.zaps[ev.id], case .zap(let z) = zap {
|
||||||
completion(.already_processed(z))
|
completion(.already_processed(z))
|
||||||
|
|||||||
+99
-44
@@ -25,14 +25,6 @@ struct Mention: Equatable {
|
|||||||
let index: Int?
|
let index: Int?
|
||||||
let type: MentionType
|
let type: MentionType
|
||||||
let ref: ReferencedId
|
let ref: ReferencedId
|
||||||
|
|
||||||
static func note(_ id: String) -> Mention {
|
|
||||||
return Mention(index: nil, type: .event, ref: .e(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
static func pubkey(_ pubkey: String) -> Mention {
|
|
||||||
return Mention(index: nil, type: .pubkey, ref: .p(pubkey))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias Invoice = LightningInvoice<Amount>
|
typealias Invoice = LightningInvoice<Amount>
|
||||||
@@ -122,12 +114,12 @@ enum Block: Equatable {
|
|||||||
|
|
||||||
return mention.type == .event
|
return mention.type == .event
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_mention: Mention? {
|
var is_mention: Bool {
|
||||||
if case .mention(let m) = self {
|
if case .mention = self {
|
||||||
return m
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,15 +150,10 @@ func render_blocks(blocks: [Block]) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Blocks {
|
func parse_mentions(content: String, tags: [[String]]) -> [Block] {
|
||||||
let words: Int
|
|
||||||
let blocks: [Block]
|
|
||||||
}
|
|
||||||
|
|
||||||
func parse_mentions(content: String, tags: [[String]]) -> Blocks {
|
|
||||||
var out: [Block] = []
|
var out: [Block] = []
|
||||||
|
|
||||||
var bs = note_blocks()
|
var bs = blocks()
|
||||||
bs.num_blocks = 0;
|
bs.num_blocks = 0;
|
||||||
|
|
||||||
blocks_init(&bs)
|
blocks_init(&bs)
|
||||||
@@ -187,10 +174,9 @@ func parse_mentions(content: String, tags: [[String]]) -> Blocks {
|
|||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
let words = Int(bs.words)
|
|
||||||
blocks_free(&bs)
|
blocks_free(&bs)
|
||||||
|
|
||||||
return Blocks(words: words, blocks: out)
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func strblock_to_string(_ s: str_block_t) -> String? {
|
func strblock_to_string(_ s: str_block_t) -> String? {
|
||||||
@@ -340,13 +326,7 @@ func convert_mention_bech32_block(_ b: mention_bech32_block) -> Block?
|
|||||||
let pubkey = hex_encode(Data(bytes: npub.pubkey, count: 32))
|
let pubkey = hex_encode(Data(bytes: npub.pubkey, count: 32))
|
||||||
let pubkey_ref = ReferencedId(ref_id: pubkey, relay_id: nil, key: "p")
|
let pubkey_ref = ReferencedId(ref_id: pubkey, relay_id: nil, key: "p")
|
||||||
return .mention(Mention(index: nil, type: .pubkey, ref: pubkey_ref))
|
return .mention(Mention(index: nil, type: .pubkey, ref: pubkey_ref))
|
||||||
|
|
||||||
case NOSTR_BECH32_NSEC:
|
|
||||||
let nsec = b.bech32.data.nsec
|
|
||||||
let nsec_bytes = Data(bytes: nsec.nsec, count: 32)
|
|
||||||
let pubkey = privkey_to_pubkey_raw(sec: nsec_bytes.bytes) ?? hex_encode(nsec_bytes)
|
|
||||||
return .mention(.pubkey(pubkey))
|
|
||||||
|
|
||||||
case NOSTR_BECH32_NPROFILE:
|
case NOSTR_BECH32_NPROFILE:
|
||||||
let nprofile = b.bech32.data.nprofile
|
let nprofile = b.bech32.data.nprofile
|
||||||
let pubkey = hex_encode(Data(bytes: nprofile.pubkey, count: 32))
|
let pubkey = hex_encode(Data(bytes: nprofile.pubkey, count: 32))
|
||||||
@@ -408,6 +388,65 @@ func convert_mention_index_block(ind: Int32, tags: [[String]]) -> Block?
|
|||||||
return .mention(Mention(index: ind, type: mention_type, ref: ref))
|
return .mention(Mention(index: ind, type: mention_type, ref: ref))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parse_while(_ p: Parser, match: (Character) -> Bool) -> String? {
|
||||||
|
var i: Int = 0
|
||||||
|
let sub = substring(p.str, start: p.pos, end: p.str.count)
|
||||||
|
let start = p.pos
|
||||||
|
for c in sub {
|
||||||
|
if match(c) {
|
||||||
|
p.pos += 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = start + i
|
||||||
|
if start == end {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return String(substring(p.str, start: start, end: end))
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_hashtag_char(_ c: Character) -> Bool {
|
||||||
|
return c.isLetter || c.isNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func prev_char(_ p: Parser, n: Int) -> Character? {
|
||||||
|
if p.pos - n < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos - n)
|
||||||
|
return p.str[ind]
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_punctuation(_ c: Character) -> Bool {
|
||||||
|
return c.isWhitespace || c.isPunctuation
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_hashtag(_ p: Parser) -> String? {
|
||||||
|
let start = p.pos
|
||||||
|
|
||||||
|
if !parse_char(p, "#") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let prev = prev_char(p, n: 2) {
|
||||||
|
// we don't allow adjacent hashtags
|
||||||
|
if !is_punctuation(prev) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let str = parse_while(p, match: is_hashtag_char) else {
|
||||||
|
p.pos = start
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {
|
func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {
|
||||||
var i: Int = 0
|
var i: Int = 0
|
||||||
for tag in tags {
|
for tag in tags {
|
||||||
@@ -438,31 +477,47 @@ func parse_mention_type(_ c: String) -> MentionType? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert
|
/// Convert
|
||||||
func make_post_tags(post_blocks: [Block], tags: [[String]], silent_mentions: Bool) -> PostTags {
|
func make_post_tags(post_blocks: [PostBlock], tags: [[String]], silent_mentions: Bool) -> PostTags {
|
||||||
var new_tags = tags
|
var new_tags = tags
|
||||||
|
var blocks: [Block] = []
|
||||||
|
|
||||||
for post_block in post_blocks {
|
for post_block in post_blocks {
|
||||||
switch post_block {
|
switch post_block {
|
||||||
case .mention(let mention):
|
case .ref(let ref):
|
||||||
let mention_type = mention.type
|
guard let mention_type = parse_mention_type(ref.key) else {
|
||||||
|
|
||||||
if silent_mentions || mention_type == .event {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
new_tags.append(refid_to_tag(mention.ref))
|
if silent_mentions || mention_type == .event {
|
||||||
|
let mention = Mention(index: nil, type: mention_type, ref: ref)
|
||||||
|
let block = Block.mention(mention)
|
||||||
|
blocks.append(block)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if find_tag_ref(type: ref.key, id: ref.ref_id, tags: tags) != nil {
|
||||||
|
// Mention index is nil because indexed mentions from NIP-08 is deprecated.
|
||||||
|
// It has been replaced with NIP-27 text note references with nostr: prefixed URIs.
|
||||||
|
let mention = Mention(index: nil, type: mention_type, ref: ref)
|
||||||
|
let block = Block.mention(mention)
|
||||||
|
blocks.append(block)
|
||||||
|
} else {
|
||||||
|
new_tags.append(refid_to_tag(ref))
|
||||||
|
// Mention index is nil because indexed mentions from NIP-08 is deprecated.
|
||||||
|
// It has been replaced with NIP-27 text note references with nostr: prefixed URIs.
|
||||||
|
let mention = Mention(index: nil, type: mention_type, ref: ref)
|
||||||
|
let block = Block.mention(mention)
|
||||||
|
blocks.append(block)
|
||||||
|
}
|
||||||
case .hashtag(let hashtag):
|
case .hashtag(let hashtag):
|
||||||
new_tags.append(["t", hashtag.lowercased()])
|
new_tags.append(["t", hashtag.lowercased()])
|
||||||
case .text: break
|
blocks.append(.hashtag(hashtag))
|
||||||
case .invoice: break
|
case .text(let txt):
|
||||||
case .relay: break
|
blocks.append(Block.text(txt))
|
||||||
case .url(let url):
|
|
||||||
new_tags.append(["r", url.absoluteString])
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return PostTags(blocks: post_blocks, tags: new_tags)
|
return PostTags(blocks: blocks, tags: new_tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func post_to_event(post: NostrPost, privkey: String, pubkey: String) -> NostrEvent {
|
func post_to_event(post: NostrPost, privkey: String, pubkey: String) -> NostrEvent {
|
||||||
|
|||||||
+27
-3
@@ -64,7 +64,6 @@ func parse_post_mention(_ p: Parser, mention_type: MentionType) -> ReferencedId?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace this with our C parser
|
|
||||||
func parse_post_bech32_mention(_ p: Parser) -> ReferencedId? {
|
func parse_post_bech32_mention(_ p: Parser) -> ReferencedId? {
|
||||||
let start = p.pos
|
let start = p.pos
|
||||||
if parse_str(p, "note") {
|
if parse_str(p, "note") {
|
||||||
@@ -110,7 +109,32 @@ func parse_post_bech32_mention(_ p: Parser) -> ReferencedId? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return a list of tags
|
/// Return a list of tags
|
||||||
func parse_post_blocks(content: String) -> [Block] {
|
func parse_post_blocks(content: String) -> [PostBlock] {
|
||||||
return parse_mentions(content: content, tags: []).blocks
|
let p = Parser(pos: 0, str: content)
|
||||||
|
var blocks: [PostBlock] = []
|
||||||
|
var starting_from: Int = 0
|
||||||
|
|
||||||
|
if content.count == 0 {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
while p.pos < content.count {
|
||||||
|
let pre_mention = p.pos
|
||||||
|
if let reference = parse_post_reference(p) {
|
||||||
|
blocks.append(parse_post_textblock(str: p.str, from: starting_from, to: pre_mention))
|
||||||
|
blocks.append(.ref(reference))
|
||||||
|
starting_from = p.pos
|
||||||
|
} else if let hashtag = parse_hashtag(p) {
|
||||||
|
blocks.append(parse_post_textblock(str: p.str, from: starting_from, to: pre_mention))
|
||||||
|
blocks.append(.hashtag(hashtag))
|
||||||
|
starting_from = p.pos
|
||||||
|
} else {
|
||||||
|
p.pos += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.append(parse_post_textblock(str: content, from: starting_from, to: content.count))
|
||||||
|
|
||||||
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,13 +69,14 @@ class ProfileModel: ObservableObject, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
var text_filter = NostrFilter(kinds: [.text, .longform])
|
var text_filter = NostrFilter(kinds: [.text, .chat])
|
||||||
|
|
||||||
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
|
var profile_filter = NostrFilter(kinds: [.contacts, .metadata, .boost])
|
||||||
|
|
||||||
profile_filter.authors = [pubkey]
|
profile_filter.authors = [pubkey]
|
||||||
|
|
||||||
text_filter.authors = [pubkey]
|
text_filter.authors = [pubkey]
|
||||||
text_filter.limit = 500
|
text_filter.limit = 50
|
||||||
|
|
||||||
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
|
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
|
||||||
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
|
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
|
||||||
|
|||||||
@@ -7,27 +7,11 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ReportType: String, CustomStringConvertible, CaseIterable {
|
enum ReportType: String {
|
||||||
case spam
|
case explicit
|
||||||
case nudity
|
|
||||||
case profanity
|
|
||||||
case illegal
|
case illegal
|
||||||
|
case spam
|
||||||
case impersonation
|
case impersonation
|
||||||
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .spam:
|
|
||||||
return NSLocalizedString("Spam", comment: "Description of report type for spam.")
|
|
||||||
case .nudity:
|
|
||||||
return NSLocalizedString("Nudity", comment: "Description of report type for nudity.")
|
|
||||||
case .profanity:
|
|
||||||
return NSLocalizedString("Profanity", comment: "Description of report type for profanity.")
|
|
||||||
case .illegal:
|
|
||||||
return NSLocalizedString("Illegal Content", comment: "Description of report type for illegal content.")
|
|
||||||
case .impersonation:
|
|
||||||
return NSLocalizedString("Impersonation", comment: "Description of report type for impersonation.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReportNoteTarget {
|
struct ReportNoteTarget {
|
||||||
@@ -47,12 +31,16 @@ struct Report {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func create_report_tags(target: ReportTarget, type: ReportType) -> [[String]] {
|
func create_report_tags(target: ReportTarget, type: ReportType) -> [[String]] {
|
||||||
|
var tags: [[String]]
|
||||||
switch target {
|
switch target {
|
||||||
case .user(let pubkey):
|
case .user(let pubkey):
|
||||||
return [["p", pubkey, type.rawValue]]
|
tags = [["p", pubkey]]
|
||||||
case .note(let notet):
|
case .note(let notet):
|
||||||
return [["e", notet.note_id, type.rawValue], ["p", notet.pubkey]]
|
tags = [["e", notet.note_id], ["p", notet.pubkey]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tags.append(["report", type.rawValue])
|
||||||
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func create_report_event(privkey: String, report: Report) -> NostrEvent? {
|
func create_report_event(privkey: String, report: Report) -> NostrEvent? {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class SearchModel: ObservableObject {
|
|||||||
func subscribe() {
|
func subscribe() {
|
||||||
// since 1 month
|
// since 1 month
|
||||||
search.limit = self.limit
|
search.limit = self.limit
|
||||||
search.kinds = [.text, .like, .longform]
|
search.kinds = [.text, .like]
|
||||||
|
|
||||||
//likes_filter.ids = ref_events.referenced_ids!
|
//likes_filter.ids = ref_events.referenced_ids!
|
||||||
|
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
//
|
|
||||||
// Trie.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Terry Yiu on 6/26/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Tree data structure of all the substring permutations of a collection of strings optimized for searching for values of type V.
|
|
||||||
///
|
|
||||||
/// Each node in the tree can have child nodes.
|
|
||||||
/// Each node represents a single character in substrings, and each of its child nodes represent the subsequent character in those substrings.
|
|
||||||
///
|
|
||||||
/// A node that has no children mean that there are no substrings with any additional characters beyond the branch of letters leading up to that node.
|
|
||||||
///
|
|
||||||
/// A node that has values mean that there are strings that end in the character represented by the node and contain the substring represented by the branch of letters leading up to that node.
|
|
||||||
///
|
|
||||||
/// https://en.wikipedia.org/wiki/Trie
|
|
||||||
class Trie<V: Hashable> {
|
|
||||||
private var children: [Character : Trie] = [:]
|
|
||||||
|
|
||||||
/// Separate exact matches from strict substrings so that exact matches appear first in returned results.
|
|
||||||
private var exactMatchValues = Set<V>()
|
|
||||||
private var substringMatchValues = Set<V>()
|
|
||||||
|
|
||||||
private var parent: Trie? = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Trie {
|
|
||||||
var hasChildren: Bool {
|
|
||||||
return !self.children.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasValues: Bool {
|
|
||||||
return !self.exactMatchValues.isEmpty || !self.substringMatchValues.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the branch that matches the specified key and returns the values from all of its descendant nodes.
|
|
||||||
func find(key: String) -> [V] {
|
|
||||||
var currentNode = self
|
|
||||||
|
|
||||||
// Find branch with matching prefix.
|
|
||||||
for char in key {
|
|
||||||
if let child = currentNode.children[char] {
|
|
||||||
currentNode = child
|
|
||||||
} else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform breadth-first search from matching branch and collect values from all descendants.
|
|
||||||
var substringMatches = Set<V>(currentNode.substringMatchValues)
|
|
||||||
var queue = Array(currentNode.children.values)
|
|
||||||
|
|
||||||
while !queue.isEmpty {
|
|
||||||
let node = queue.removeFirst()
|
|
||||||
substringMatches.formUnion(node.exactMatchValues)
|
|
||||||
substringMatches.formUnion(node.substringMatchValues)
|
|
||||||
queue.append(contentsOf: node.children.values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prioritize exact matches to be returned first, and then remove exact matches from the set of partial substring matches that are appended afterward.
|
|
||||||
return Array(currentNode.exactMatchValues) + (substringMatches.subtracting(currentNode.exactMatchValues))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts value of type V into this trie for the specified key. This function stores all substring endings of the key, not only the key itself.
|
|
||||||
/// Runtime performance is O(n^2) and storage cost is O(n), where n is the number of characters in the key.
|
|
||||||
func insert(key: String, value: V) {
|
|
||||||
// Create root branches for each character of the key to enable substring searches instead of only just prefix searches.
|
|
||||||
// Hence the nested loop.
|
|
||||||
for i in 0..<key.count {
|
|
||||||
var currentNode = self
|
|
||||||
|
|
||||||
// Find branch with matching prefix.
|
|
||||||
for char in key[key.index(key.startIndex, offsetBy: i)...] {
|
|
||||||
if let child = currentNode.children[char] {
|
|
||||||
currentNode = child
|
|
||||||
} else {
|
|
||||||
let child = Trie()
|
|
||||||
child.parent = currentNode
|
|
||||||
currentNode.children[char] = child
|
|
||||||
currentNode = child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == 0 {
|
|
||||||
currentNode.exactMatchValues.insert(value)
|
|
||||||
} else {
|
|
||||||
currentNode.substringMatchValues.insert(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes value of type V from this trie for the specified key.
|
|
||||||
func remove(key: String, value: V) {
|
|
||||||
for i in 0..<key.count {
|
|
||||||
var currentNode = self
|
|
||||||
|
|
||||||
var foundLeafNode = true
|
|
||||||
|
|
||||||
// Find branch with matching prefix.
|
|
||||||
for j in i..<key.count {
|
|
||||||
let char = key[key.index(key.startIndex, offsetBy: j)]
|
|
||||||
|
|
||||||
if let child = currentNode.children[char] {
|
|
||||||
currentNode = child
|
|
||||||
} else {
|
|
||||||
foundLeafNode = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if foundLeafNode {
|
|
||||||
currentNode.exactMatchValues.remove(value)
|
|
||||||
currentNode.substringMatchValues.remove(value)
|
|
||||||
|
|
||||||
// Clean up the tree if this leaf node no longer holds values or children.
|
|
||||||
for j in (i..<key.count).reversed() {
|
|
||||||
if let parent = currentNode.parent, !currentNode.hasValues && !currentNode.hasChildren {
|
|
||||||
currentNode = parent
|
|
||||||
let char = key[key.index(key.startIndex, offsetBy: j)]
|
|
||||||
currentNode.children.removeValue(forKey: char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// UserSearchCache.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Terry Yiu on 6/27/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Cache of searchable users by name, display_name, NIP-05 identifier, or own contact list petname.
|
|
||||||
/// Optimized for fast searches of substrings by using a Trie.
|
|
||||||
/// Optimal for performing user searches that could be initiated by typing quickly on a keyboard into a text input field.
|
|
||||||
class UserSearchCache {
|
|
||||||
private let trie = Trie<String>()
|
|
||||||
|
|
||||||
func search(key: String) -> [String] {
|
|
||||||
let results = trie.find(key: key)
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the differences between an old profile, if it exists, and a new profile, and updates the user search cache accordingly.
|
|
||||||
func updateProfile(id: String, profiles: Profiles, oldProfile: Profile?, newProfile: Profile) {
|
|
||||||
// Remove searchable keys tied to the old profile if they differ from the new profile
|
|
||||||
// to keep the trie clean without empty nodes while avoiding excessive graph searching.
|
|
||||||
if let oldProfile {
|
|
||||||
if let oldName = oldProfile.name, newProfile.name?.caseInsensitiveCompare(oldName) != .orderedSame {
|
|
||||||
trie.remove(key: oldName.lowercased(), value: id)
|
|
||||||
}
|
|
||||||
if let oldDisplayName = oldProfile.display_name, newProfile.display_name?.caseInsensitiveCompare(oldDisplayName) != .orderedSame {
|
|
||||||
trie.remove(key: oldDisplayName.lowercased(), value: id)
|
|
||||||
}
|
|
||||||
if let oldNip05 = oldProfile.nip05, newProfile.nip05?.caseInsensitiveCompare(oldNip05) != .orderedSame {
|
|
||||||
trie.remove(key: oldNip05.lowercased(), value: id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addProfile(id: id, profiles: profiles, profile: newProfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a profile to the user search cache.
|
|
||||||
private func addProfile(id: String, profiles: Profiles, profile: Profile) {
|
|
||||||
// Searchable by name.
|
|
||||||
if let name = profile.name {
|
|
||||||
trie.insert(key: name.lowercased(), value: id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Searchable by display name.
|
|
||||||
if let displayName = profile.display_name {
|
|
||||||
trie.insert(key: displayName.lowercased(), value: id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Searchable by NIP-05 identifier.
|
|
||||||
if let nip05 = profiles.is_validated(id) {
|
|
||||||
trie.insert(key: "\(nip05.username.lowercased())@\(nip05.host.lowercased())", value: id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the diffences between an old contacts event and a new contacts event for our own user, and updates the search cache accordingly.
|
|
||||||
func updateOwnContactsPetnames(id: String, oldEvent: NostrEvent?, newEvent: NostrEvent) {
|
|
||||||
guard newEvent.known_kind == .contacts && newEvent.pubkey == id else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var petnames: [String: String] = [:]
|
|
||||||
|
|
||||||
// Gets all petnames from our new contacts list.
|
|
||||||
newEvent.tags.forEach { tag in
|
|
||||||
guard tag.count >= 4 && tag[0] == "p" else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let pubkey = tag[1]
|
|
||||||
let petname = tag[3]
|
|
||||||
|
|
||||||
petnames[pubkey] = petname
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the diff with the old contacts list, if it exists,
|
|
||||||
// mark the ones that are the same to not be removed from the user search cache,
|
|
||||||
// and remove the old ones that are different from the user search cache.
|
|
||||||
if let oldEvent, oldEvent.known_kind == .contacts && oldEvent.pubkey == id {
|
|
||||||
oldEvent.tags.forEach { tag in
|
|
||||||
guard tag.count >= 4 && tag[0] == "p" else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let pubkey = tag[1]
|
|
||||||
let oldPetname = tag[3]
|
|
||||||
|
|
||||||
if let newPetname = petnames[pubkey] {
|
|
||||||
if newPetname.caseInsensitiveCompare(oldPetname) == .orderedSame {
|
|
||||||
petnames.removeValue(forKey: pubkey)
|
|
||||||
} else {
|
|
||||||
trie.remove(key: oldPetname, value: pubkey)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trie.remove(key: oldPetname, value: pubkey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the new petnames to the user search cache.
|
|
||||||
for (pubkey, petname) in petnames {
|
|
||||||
trie.insert(key: petname, value: pubkey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,9 +15,6 @@ let fallback_zap_amount = 1000
|
|||||||
private var value: T
|
private var value: T
|
||||||
|
|
||||||
init(key: String, default_value: T) {
|
init(key: String, default_value: T) {
|
||||||
if T.self == Bool.self {
|
|
||||||
UserSettingsStore.bool_options.insert(key)
|
|
||||||
}
|
|
||||||
self.key = pk_setting_key(UserSettingsStore.pubkey ?? "", key: key)
|
self.key = pk_setting_key(UserSettingsStore.pubkey ?? "", key: key)
|
||||||
if let loaded = UserDefaults.standard.object(forKey: self.key) as? T {
|
if let loaded = UserDefaults.standard.object(forKey: self.key) as? T {
|
||||||
self.value = loaded
|
self.value = loaded
|
||||||
@@ -80,7 +77,6 @@ let fallback_zap_amount = 1000
|
|||||||
class UserSettingsStore: ObservableObject {
|
class UserSettingsStore: ObservableObject {
|
||||||
static var pubkey: String? = nil
|
static var pubkey: String? = nil
|
||||||
static var shared: UserSettingsStore? = nil
|
static var shared: UserSettingsStore? = nil
|
||||||
static var bool_options = Set<String>()
|
|
||||||
|
|
||||||
@StringSetting(key: "default_wallet", default_value: .system_default_wallet)
|
@StringSetting(key: "default_wallet", default_value: .system_default_wallet)
|
||||||
var default_wallet: Wallet
|
var default_wallet: Wallet
|
||||||
@@ -157,9 +153,6 @@ class UserSettingsStore: ObservableObject {
|
|||||||
|
|
||||||
@Setting(key: "donation_percent", default_value: 0)
|
@Setting(key: "donation_percent", default_value: 0)
|
||||||
var donation_percent: Int
|
var donation_percent: Int
|
||||||
|
|
||||||
@Setting(key: "developer_mode", default_value: false)
|
|
||||||
var developer_mode: Bool
|
|
||||||
|
|
||||||
// Helper for inverse of disable_animation.
|
// Helper for inverse of disable_animation.
|
||||||
// disable_animation was introduced as a setting first, but it's more natural for the settings UI to show the inverse.
|
// disable_animation was introduced as a setting first, but it's more natural for the settings UI to show the inverse.
|
||||||
|
|||||||
@@ -36,14 +36,6 @@ struct ReferencedId: Identifiable, Hashable, Equatable {
|
|||||||
static func e(_ id: String, relay_id: String? = nil) -> ReferencedId {
|
static func e(_ id: String, relay_id: String? = nil) -> ReferencedId {
|
||||||
return ReferencedId(ref_id: id, relay_id: relay_id, key: "e")
|
return ReferencedId(ref_id: id, relay_id: relay_id, key: "e")
|
||||||
}
|
}
|
||||||
|
|
||||||
static func p(_ pk: String, relay_id: String? = nil) -> ReferencedId {
|
|
||||||
return ReferencedId(ref_id: pk, relay_id: relay_id, key: "p")
|
|
||||||
}
|
|
||||||
|
|
||||||
static func t(_ hashtag: String, relay_id: String? = nil) -> ReferencedId {
|
|
||||||
return ReferencedId(ref_id: hashtag, relay_id: relay_id, key: "t")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Hashable, Comparable {
|
class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Hashable, Comparable {
|
||||||
@@ -76,11 +68,11 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
let content: String
|
let content: String
|
||||||
|
|
||||||
var is_textlike: Bool {
|
var is_textlike: Bool {
|
||||||
return kind == 1 || kind == 42 || kind == 30023
|
return kind == 1 || kind == 42
|
||||||
}
|
}
|
||||||
|
|
||||||
var too_big: Bool {
|
var too_big: Bool {
|
||||||
return known_kind != .longform && self.content.utf8.count > 16000
|
return self.content.utf8.count > 16000
|
||||||
}
|
}
|
||||||
|
|
||||||
var should_show_event: Bool {
|
var should_show_event: Bool {
|
||||||
@@ -91,8 +83,8 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
return calculate_event_id(ev: self) == self.id
|
return calculate_event_id(ev: self) == self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _blocks: Blocks? = nil
|
private var _blocks: [Block]? = nil
|
||||||
func blocks(_ privkey: String?) -> Blocks {
|
func blocks(_ privkey: String?) -> [Block] {
|
||||||
if let bs = _blocks {
|
if let bs = _blocks {
|
||||||
return bs
|
return bs
|
||||||
}
|
}
|
||||||
@@ -101,7 +93,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_blocks(content: String) -> Blocks {
|
func get_blocks(content: String) -> [Block] {
|
||||||
return parse_mentions(content: content, tags: self.tags)
|
return parse_mentions(content: content, tags: self.tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +118,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
if let rs = _event_refs {
|
if let rs = _event_refs {
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
let refs = interpret_event_refs(blocks: self.blocks(privkey).blocks, tags: self.tags)
|
let refs = interpret_event_refs(blocks: self.blocks(privkey), tags: self.tags)
|
||||||
self._event_refs = refs
|
self._event_refs = refs
|
||||||
return refs
|
return refs
|
||||||
}
|
}
|
||||||
@@ -240,7 +232,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
func note_language(_ privkey: String?) -> String? {
|
func note_language(_ privkey: String?) -> String? {
|
||||||
// Rely on Apple's NLLanguageRecognizer to tell us which language it thinks the note is in
|
// 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.
|
// and filter on only the text portions of the content as URLs and hashtags confuse the language recognizer.
|
||||||
let originalBlocks = blocks(privkey).blocks
|
let originalBlocks = blocks(privkey)
|
||||||
let originalOnlyText = originalBlocks.compactMap { $0.is_text }.joined(separator: " ")
|
let originalOnlyText = originalBlocks.compactMap { $0.is_text }.joined(separator: " ")
|
||||||
|
|
||||||
// Only accept language recognition hypothesis if there's at least a 50% probability that it's accurate.
|
// Only accept language recognition hypothesis if there's at least a 50% probability that it's accurate.
|
||||||
@@ -461,8 +453,10 @@ func make_first_contact_event(keypair: Keypair) -> NostrEvent? {
|
|||||||
|
|
||||||
let relay_json = encode_json(relays)!
|
let relay_json = encode_json(relays)!
|
||||||
let damus_pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
let damus_pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||||
|
let jb55_pubkey = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" // lol
|
||||||
let tags = [
|
let tags = [
|
||||||
["p", damus_pubkey],
|
["p", damus_pubkey],
|
||||||
|
["p", jb55_pubkey],
|
||||||
["p", keypair.pubkey] // you're a friend of yourself!
|
["p", keypair.pubkey] // you're a friend of yourself!
|
||||||
]
|
]
|
||||||
let ev = NostrEvent(content: relay_json,
|
let ev = NostrEvent(content: relay_json,
|
||||||
@@ -948,7 +942,7 @@ func last_etag(tags: [[String]]) -> String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func first_eref_mention(ev: NostrEvent, privkey: String?) -> Mention? {
|
func first_eref_mention(ev: NostrEvent, privkey: String?) -> Mention? {
|
||||||
let blocks = ev.blocks(privkey).blocks.filter { block in
|
let blocks = ev.blocks(privkey).filter { block in
|
||||||
guard case .mention(let mention) = block else {
|
guard case .mention(let mention) = block else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ enum NostrKind: Int, Codable {
|
|||||||
case channel_meta = 41
|
case channel_meta = 41
|
||||||
case chat = 42
|
case chat = 42
|
||||||
case list = 30000
|
case list = 30000
|
||||||
case longform = 30023
|
|
||||||
case zap = 9735
|
case zap = 9735
|
||||||
case zap_request = 9734
|
case zap_request = 9734
|
||||||
case nwc_request = 23194
|
case nwc_request = 23194
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import Foundation
|
|||||||
enum NostrLink: Equatable {
|
enum NostrLink: Equatable {
|
||||||
case ref(ReferencedId)
|
case ref(ReferencedId)
|
||||||
case filter(NostrFilter)
|
case filter(NostrFilter)
|
||||||
case script([UInt8])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encode_pubkey_uri(_ ref: ReferencedId) -> String {
|
func encode_pubkey_uri(_ ref: ReferencedId) -> String {
|
||||||
@@ -106,8 +105,6 @@ func decode_nostr_bech32_uri(_ s: String) -> NostrLink? {
|
|||||||
return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p"))
|
return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p"))
|
||||||
case .note(let id):
|
case .note(let id):
|
||||||
return .ref(ReferencedId(ref_id: id, relay_id: nil, key: "e"))
|
return .ref(ReferencedId(ref_id: id, relay_id: nil, key: "e"))
|
||||||
case .nscript(let data):
|
|
||||||
return .script(data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +113,8 @@ func decode_nostr_uri(_ s: String) -> NostrLink? {
|
|||||||
return decode_universal_link(s)
|
return decode_universal_link(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
var uri = s
|
var uri = s.replacingOccurrences(of: "nostr://", with: "")
|
||||||
uri = uri.replacingOccurrences(of: "nostr://", with: "")
|
|
||||||
uri = uri.replacingOccurrences(of: "nostr:", with: "")
|
uri = uri.replacingOccurrences(of: "nostr:", with: "")
|
||||||
|
|
||||||
// Fix for non-latin characters resulting in second colon being encoded
|
|
||||||
uri = uri.replacingOccurrences(of: "damus:t%3A", with: "t:")
|
|
||||||
|
|
||||||
uri = uri.replacingOccurrences(of: "damus://", with: "")
|
uri = uri.replacingOccurrences(of: "damus://", with: "")
|
||||||
uri = uri.replacingOccurrences(of: "damus:", with: "")
|
uri = uri.replacingOccurrences(of: "damus:", with: "")
|
||||||
|
|||||||
@@ -12,28 +12,6 @@ struct NostrSubscribe {
|
|||||||
let sub_id: String
|
let sub_id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum NostrRequestType {
|
|
||||||
case typical(NostrRequest)
|
|
||||||
case custom(String)
|
|
||||||
|
|
||||||
var is_write: Bool {
|
|
||||||
guard case .typical(let req) = self else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.is_write
|
|
||||||
}
|
|
||||||
|
|
||||||
var is_read: Bool {
|
|
||||||
guard case .typical(let req) = self else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.is_read
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum NostrRequest {
|
enum NostrRequest {
|
||||||
case subscribe(NostrSubscribe)
|
case subscribe(NostrSubscribe)
|
||||||
case unsubscribe(String)
|
case unsubscribe(String)
|
||||||
@@ -53,5 +31,4 @@ enum NostrRequest {
|
|||||||
var is_read: Bool {
|
var is_read: Bool {
|
||||||
return !is_write
|
return !is_write
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,41 +13,23 @@ class Profiles {
|
|||||||
|
|
||||||
/// This queue is used to synchronize access to the profiles dictionary, which
|
/// This queue is used to synchronize access to the profiles dictionary, which
|
||||||
/// prevents data races from crashing the app.
|
/// prevents data races from crashing the app.
|
||||||
private var profiles_queue = DispatchQueue(label: "io.damus.profiles",
|
private var queue = DispatchQueue(label: "io.damus.profiles",
|
||||||
qos: .userInteractive,
|
qos: .userInteractive,
|
||||||
attributes: .concurrent)
|
attributes: .concurrent)
|
||||||
|
|
||||||
private var validated_queue = DispatchQueue(label: "io.damus.profiles.validated",
|
|
||||||
qos: .userInteractive,
|
|
||||||
attributes: .concurrent)
|
|
||||||
|
|
||||||
private var profiles: [String: TimestampedProfile] = [:]
|
private var profiles: [String: TimestampedProfile] = [:]
|
||||||
private var validated: [String: NIP05] = [:]
|
var validated: [String: NIP05] = [:]
|
||||||
var nip05_pubkey: [String: String] = [:]
|
var nip05_pubkey: [String: String] = [:]
|
||||||
var zappers: [String: String] = [:]
|
var zappers: [String: String] = [:]
|
||||||
|
|
||||||
private let database = ProfileDatabase()
|
private let database = ProfileDatabase()
|
||||||
|
|
||||||
let user_search_cache: UserSearchCache
|
|
||||||
|
|
||||||
init(user_search_cache: UserSearchCache) {
|
|
||||||
self.user_search_cache = user_search_cache
|
|
||||||
}
|
|
||||||
|
|
||||||
func is_validated(_ pk: String) -> NIP05? {
|
func is_validated(_ pk: String) -> NIP05? {
|
||||||
validated_queue.sync {
|
validated[pk]
|
||||||
validated[pk]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func set_validated(_ pk: String, nip05: NIP05?) {
|
|
||||||
validated_queue.async(flags: .barrier) {
|
|
||||||
self.validated[pk] = nip05
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func enumerated() -> EnumeratedSequence<[String: TimestampedProfile]> {
|
func enumerated() -> EnumeratedSequence<[String: TimestampedProfile]> {
|
||||||
return profiles_queue.sync {
|
return queue.sync {
|
||||||
return profiles.enumerated()
|
return profiles.enumerated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,10 +39,8 @@ class Profiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func add(id: String, profile: TimestampedProfile) {
|
func add(id: String, profile: TimestampedProfile) {
|
||||||
profiles_queue.async(flags: .barrier) {
|
queue.async(flags: .barrier) {
|
||||||
let old_timestamped_profile = self.profiles[id]
|
|
||||||
self.profiles[id] = profile
|
self.profiles[id] = profile
|
||||||
self.user_search_cache.updateProfile(id: id, profiles: self, oldProfile: old_timestamped_profile?.profile, newProfile: profile.profile)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
@@ -74,14 +54,14 @@ class Profiles {
|
|||||||
|
|
||||||
func lookup(id: String) -> Profile? {
|
func lookup(id: String) -> Profile? {
|
||||||
var profile: Profile?
|
var profile: Profile?
|
||||||
profiles_queue.sync {
|
queue.sync {
|
||||||
profile = profiles[id]?.profile
|
profile = profiles[id]?.profile
|
||||||
}
|
}
|
||||||
return profile ?? database.get(id: id)
|
return profile ?? database.get(id: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookup_with_timestamp(id: String) -> TimestampedProfile? {
|
func lookup_with_timestamp(id: String) -> TimestampedProfile? {
|
||||||
profiles_queue.sync {
|
queue.sync {
|
||||||
return profiles[id]
|
return profiles[id]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +69,7 @@ class Profiles {
|
|||||||
func has_fresh_profile(id: String) -> Bool {
|
func has_fresh_profile(id: String) -> Bool {
|
||||||
// check memory first
|
// check memory first
|
||||||
var profile: Profile?
|
var profile: Profile?
|
||||||
profiles_queue.sync {
|
queue.sync {
|
||||||
profile = profiles[id]?.profile
|
profile = profiles[id]?.profile
|
||||||
}
|
}
|
||||||
if profile != nil {
|
if profile != nil {
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ final class RelayConnection: ObservableObject {
|
|||||||
|
|
||||||
private var handleEvent: (NostrConnectionEvent) -> ()
|
private var handleEvent: (NostrConnectionEvent) -> ()
|
||||||
private let url: RelayURL
|
private let url: RelayURL
|
||||||
var log: RelayLog?
|
|
||||||
|
|
||||||
init(url: RelayURL, handleEvent: @escaping (NostrConnectionEvent) -> ()) {
|
init(url: RelayURL, handleEvent: @escaping (NostrConnectionEvent) -> ()) {
|
||||||
self.url = url
|
self.url = url
|
||||||
@@ -60,13 +59,11 @@ final class RelayConnection: ObservableObject {
|
|||||||
socket.ping { err in
|
socket.ping { err in
|
||||||
if err == nil {
|
if err == nil {
|
||||||
self.last_pong = .now
|
self.last_pong = .now
|
||||||
self.log?.add("Successful ping")
|
|
||||||
} else {
|
} else {
|
||||||
print("pong failed, reconnecting \(self.url.id)")
|
print("pong failed, reconnecting \(self.url.id)")
|
||||||
self.isConnected = false
|
self.isConnected = false
|
||||||
self.isConnecting = false
|
self.isConnecting = false
|
||||||
self.reconnect_with_backoff()
|
self.reconnect_with_backoff()
|
||||||
self.log?.add("Ping failed")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,23 +99,13 @@ final class RelayConnection: ObservableObject {
|
|||||||
isConnected = false
|
isConnected = false
|
||||||
isConnecting = false
|
isConnecting = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func send_raw(_ req: String) {
|
func send(_ req: NostrRequest) {
|
||||||
socket.send(.string(req))
|
guard let req = make_nostr_req(req) else {
|
||||||
}
|
print("failed to encode nostr req: \(req)")
|
||||||
|
return
|
||||||
func send(_ req: NostrRequestType) {
|
|
||||||
switch req {
|
|
||||||
case .typical(let req):
|
|
||||||
guard let req = make_nostr_req(req) else {
|
|
||||||
print("failed to encode nostr req: \(req)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
send_raw(req)
|
|
||||||
|
|
||||||
case .custom(let req):
|
|
||||||
send_raw(req)
|
|
||||||
}
|
}
|
||||||
|
socket.send(.string(req))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func receive(event: WebSocketEvent) {
|
private func receive(event: WebSocketEvent) {
|
||||||
@@ -156,10 +143,6 @@ final class RelayConnection: ObservableObject {
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.handleEvent(.ws_event(event))
|
self.handleEvent(.ws_event(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let description = event.description {
|
|
||||||
log?.add(description)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconnect_with_backoff() {
|
func reconnect_with_backoff() {
|
||||||
@@ -173,7 +156,6 @@ final class RelayConnection: ObservableObject {
|
|||||||
}
|
}
|
||||||
disconnect()
|
disconnect()
|
||||||
connect()
|
connect()
|
||||||
log?.add("Reconnecting...")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconnect_in(after: TimeInterval) {
|
func reconnect_in(after: TimeInterval) {
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
//
|
|
||||||
// RelayLog.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Bryan Montz on 6/1/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Combine
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/// Stores a running list of events and state changes related to a relay, so that users
|
|
||||||
/// will have information to help developers debug issues.
|
|
||||||
final class RelayLog: ObservableObject {
|
|
||||||
private static let line_limit = 250
|
|
||||||
private let relay_url: URL?
|
|
||||||
private lazy var formatter: DateFormatter = {
|
|
||||||
let formatter = DateFormatter()
|
|
||||||
formatter.dateStyle = .short
|
|
||||||
formatter.timeStyle = .medium
|
|
||||||
return formatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
private(set) var lines = [String]()
|
|
||||||
|
|
||||||
private var notification_token: AnyCancellable?
|
|
||||||
|
|
||||||
/// Creates a RelayLog
|
|
||||||
/// - Parameter relay_url: the relay url the log represents. Pass nil for the url to create
|
|
||||||
/// a RelayLog that does nothing. This is required to allow RelayLog to be used as a StateObject,
|
|
||||||
/// because they cannot be Optional.
|
|
||||||
init(_ relay_url: URL? = nil) {
|
|
||||||
self.relay_url = relay_url
|
|
||||||
|
|
||||||
setUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
private var log_files_directory: URL {
|
|
||||||
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("RelayLogs", isDirectory: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var log_file_url: URL? {
|
|
||||||
guard let file_name = relay_url?.absoluteString.data(using: .utf8) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return log_files_directory.appendingPathComponent(file_name.base64EncodedString())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets up the log file and prepares to listen to app state changes
|
|
||||||
private func setUp() {
|
|
||||||
guard let log_file_url else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try? FileManager.default.createDirectory(at: log_files_directory, withIntermediateDirectories: false)
|
|
||||||
|
|
||||||
if !FileManager.default.fileExists(atPath: log_file_url.path) {
|
|
||||||
// create the log file if it doesn't exist yet
|
|
||||||
FileManager.default.createFile(atPath: log_file_url.path, contents: nil)
|
|
||||||
} else {
|
|
||||||
// otherwise load it into memory
|
|
||||||
readFromDisk()
|
|
||||||
}
|
|
||||||
|
|
||||||
let willResignPublisher = NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
|
|
||||||
let willTerminatePublisher = NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)
|
|
||||||
notification_token = Publishers.Merge(willResignPublisher, willTerminatePublisher)
|
|
||||||
.sink { [weak self] _ in
|
|
||||||
self?.writeToDisk()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The current contents of the log
|
|
||||||
var contents: String? {
|
|
||||||
guard !lines.isEmpty else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return lines.joined(separator: "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds content to the log
|
|
||||||
/// - Parameter content: what to add to the log. The date and time are prepended to the content.
|
|
||||||
func add(_ content: String) {
|
|
||||||
Task {
|
|
||||||
await addLine(content)
|
|
||||||
await publishChanges()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor private func addLine(_ line: String) {
|
|
||||||
let line = "\(formatter.string(from: .now)) - \(line)"
|
|
||||||
lines.insert(line, at: 0)
|
|
||||||
truncateLines()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tells views that our log has been updated
|
|
||||||
@MainActor private func publishChanges() {
|
|
||||||
objectWillChange.send()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func truncateLines() {
|
|
||||||
lines = Array(lines.prefix(RelayLog.line_limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the contents of the log file from disk into memory
|
|
||||||
private func readFromDisk() {
|
|
||||||
guard let log_file_url else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let handle = try FileHandle(forReadingFrom: log_file_url)
|
|
||||||
let data = try handle.readToEnd()
|
|
||||||
try handle.close()
|
|
||||||
|
|
||||||
guard let data, let content = String(data: data, encoding: .utf8) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = content.components(separatedBy: "\n")
|
|
||||||
|
|
||||||
truncateLines()
|
|
||||||
} catch {
|
|
||||||
print("⚠️ Warning: RelayLog failed to read from \(log_file_url)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the contents of the lines in memory to disk
|
|
||||||
private func writeToDisk() {
|
|
||||||
guard let log_file_url, let relay_url,
|
|
||||||
!lines.isEmpty,
|
|
||||||
let content = lines.joined(separator: "\n").data(using: .utf8) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
let handle = try FileHandle(forWritingTo: log_file_url)
|
|
||||||
|
|
||||||
try handle.truncate(atOffset: 0)
|
|
||||||
try handle.write(contentsOf: content)
|
|
||||||
try handle.close()
|
|
||||||
} catch {
|
|
||||||
print("⚠️ Warning: RelayLog(\(relay_url)) failed to write to file: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,9 +14,8 @@ struct RelayHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct QueuedRequest {
|
struct QueuedRequest {
|
||||||
let req: NostrRequestType
|
let req: NostrRequest
|
||||||
let relay: String
|
let relay: String
|
||||||
let skip_ephemeral: Bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SeenEvent: Hashable {
|
struct SeenEvent: Hashable {
|
||||||
@@ -43,12 +42,6 @@ class RelayPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let self, path.status != self.last_network_status {
|
|
||||||
for relay in self.relays {
|
|
||||||
relay.connection.log?.add("Network state: \(path.status)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self?.last_network_status = path.status
|
self?.last_network_status = path.status
|
||||||
}
|
}
|
||||||
network_monitor.start(queue: network_monitor_queue)
|
network_monitor.start(queue: network_monitor_queue)
|
||||||
@@ -116,13 +109,6 @@ class RelayPool {
|
|||||||
self.relays.append(relay)
|
self.relays.append(relay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLog(_ log: RelayLog, for relay_id: String) {
|
|
||||||
// add the current network state to the log
|
|
||||||
log.add("Network state: \(network_monitor.currentPath.status)")
|
|
||||||
|
|
||||||
get_relay(relay_id)?.connection.log = log
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is used to retry dead connections
|
/// This is used to retry dead connections
|
||||||
func connect_to_disconnected() {
|
func connect_to_disconnected() {
|
||||||
for relay in relays {
|
for relay in relays {
|
||||||
@@ -192,18 +178,18 @@ class RelayPool {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func queue_req(r: NostrRequestType, relay: String, skip_ephemeral: Bool) {
|
func queue_req(r: NostrRequest, relay: String) {
|
||||||
let count = count_queued(relay: relay)
|
let count = count_queued(relay: relay)
|
||||||
guard count <= 10 else {
|
guard count <= 10 else {
|
||||||
print("can't queue, too many queued events for \(relay)")
|
print("can't queue, too many queued events for \(relay)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print("queueing request for \(relay)")
|
print("queueing request: \(r) for \(relay)")
|
||||||
request_queue.append(QueuedRequest(req: r, relay: relay, skip_ephemeral: skip_ephemeral))
|
request_queue.append(QueuedRequest(req: r, relay: relay))
|
||||||
}
|
}
|
||||||
|
|
||||||
func send_raw(_ req: NostrRequestType, to: [String]? = nil, skip_ephemeral: Bool = true) {
|
func send(_ req: NostrRequest, to: [String]? = nil, skip_ephemeral: Bool = true) {
|
||||||
let relays = to.map{ get_relays($0) } ?? self.relays
|
let relays = to.map{ get_relays($0) } ?? self.relays
|
||||||
|
|
||||||
for relay in relays {
|
for relay in relays {
|
||||||
@@ -220,7 +206,7 @@ class RelayPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard relay.connection.isConnected else {
|
guard relay.connection.isConnected else {
|
||||||
queue_req(r: req, relay: relay.id, skip_ephemeral: skip_ephemeral)
|
queue_req(r: req, relay: relay.id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,10 +214,6 @@ class RelayPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(_ req: NostrRequest, to: [String]? = nil, skip_ephemeral: Bool = true) {
|
|
||||||
send_raw(.typical(req), to: to, skip_ephemeral: skip_ephemeral)
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_relays(_ ids: [String]) -> [Relay] {
|
func get_relays(_ ids: [String]) -> [Relay] {
|
||||||
// don't include ephemeral relays in the default list to query
|
// don't include ephemeral relays in the default list to query
|
||||||
relays.filter { ids.contains($0.id) }
|
relays.filter { ids.contains($0.id) }
|
||||||
@@ -249,7 +231,7 @@ class RelayPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print("running queueing request: \(req.req) for \(relay_id)")
|
print("running queueing request: \(req.req) for \(relay_id)")
|
||||||
self.send_raw(req.req, to: [relay_id], skip_ephemeral: false)
|
self.send(req.req, to: [relay_id])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ enum WebSocketEvent {
|
|||||||
case .connected:
|
case .connected:
|
||||||
return "Connected"
|
return "Connected"
|
||||||
case .message(_):
|
case .message(_):
|
||||||
return nil // adding this to the RelayLog was too noisy
|
return "Received message"
|
||||||
case .disconnected(let close_code, let reason):
|
case .disconnected(let close_code, let reason):
|
||||||
return "Disconnected: Close code: \(close_code), reason: \(reason ?? "unknown")"
|
return "Disconnected: Close code: \(close_code), reason: \(reason ?? "unknown")"
|
||||||
case .error(let error):
|
case .error(let error):
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// TestData.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-07-13.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
|
|
||||||
let test_event_holder = EventHolder(events: [], incoming: [test_event])
|
|
||||||
|
|
||||||
let test_event =
|
|
||||||
NostrEvent(
|
|
||||||
content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jpg cool",
|
|
||||||
pubkey: "pk",
|
|
||||||
createdAt: Int64(Date().timeIntervalSince1970 - 100)
|
|
||||||
)
|
|
||||||
|
|
||||||
func test_damus_state() -> DamusState {
|
|
||||||
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
|
||||||
let damus = DamusState.empty
|
|
||||||
|
|
||||||
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io", damus_donation: nil)
|
|
||||||
let tsprof = TimestampedProfile(profile: prof, timestamp: 0, event: test_event)
|
|
||||||
damus.profiles.add(id: pubkey, profile: tsprof)
|
|
||||||
return damus
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -91,8 +91,8 @@ extension _AnyEncodable {
|
|||||||
try encode(nsnumber: number, into: &container)
|
try encode(nsnumber: number, into: &container)
|
||||||
case let date as Date:
|
case let date as Date:
|
||||||
try container.encode(date)
|
try container.encode(date)
|
||||||
case let profile_url as URL:
|
case let url as URL:
|
||||||
try container.encode(profile_url)
|
try container.encode(url)
|
||||||
#endif
|
#endif
|
||||||
case let array as [Any?]:
|
case let array as [Any?]:
|
||||||
try container.encode(array.map { AnyEncodable($0) })
|
try container.encode(array.map { AnyEncodable($0) })
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ enum Bech32Object {
|
|||||||
case nsec(String)
|
case nsec(String)
|
||||||
case npub(String)
|
case npub(String)
|
||||||
case note(String)
|
case note(String)
|
||||||
case nscript([UInt8])
|
|
||||||
|
|
||||||
static func parse(_ str: String) -> Bech32Object? {
|
static func parse(_ str: String) -> Bech32Object? {
|
||||||
guard let decoded = try? bech32_decode(str) else {
|
guard let decoded = try? bech32_decode(str) else {
|
||||||
@@ -25,8 +24,6 @@ enum Bech32Object {
|
|||||||
return .nsec(hex_encode(decoded.data))
|
return .nsec(hex_encode(decoded.data))
|
||||||
} else if decoded.hrp == "note" {
|
} else if decoded.hrp == "note" {
|
||||||
return .note(hex_encode(decoded.data))
|
return .note(hex_encode(decoded.data))
|
||||||
} else if decoded.hrp == "nscript" {
|
|
||||||
return .nscript(decoded.data.bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ struct PreloadPlan {
|
|||||||
let load_preview: Bool
|
let load_preview: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_preview(artifacts: NoteArtifactsSeparated) async -> Preview? {
|
func load_preview(artifacts: NoteArtifacts) async -> Preview? {
|
||||||
guard let link = artifacts.links.first else {
|
guard let link = artifacts.links.first else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -442,18 +442,14 @@ func preload_event(plan: PreloadPlan, state: DamusState) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if plan.load_preview, note_artifact_is_separated(kind: plan.event.known_kind) {
|
if plan.load_preview {
|
||||||
let arts = artifacts ?? render_note_content(ev: plan.event, profiles: profiles, privkey: our_keypair.privkey)
|
let arts = artifacts ?? render_note_content(ev: plan.event, profiles: profiles, privkey: our_keypair.privkey)
|
||||||
|
let preview = await load_preview(artifacts: arts)
|
||||||
// only separated artifacts have previews
|
DispatchQueue.main.async {
|
||||||
if case .separated(let sep) = arts {
|
if let preview {
|
||||||
let preview = await load_preview(artifacts: sep)
|
plan.data.preview_model.state = .loaded(preview)
|
||||||
DispatchQueue.main.async {
|
} else {
|
||||||
if let preview {
|
plan.data.preview_model.state = .loaded(.failed)
|
||||||
plan.data.preview_model.state = .loaded(preview)
|
|
||||||
} else {
|
|
||||||
plan.data.preview_model.state = .loaded(.failed)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-14
@@ -72,14 +72,6 @@ func bech32_pubkey(_ pubkey: String) -> String? {
|
|||||||
return bech32_encode(hrp: "npub", bytes)
|
return bech32_encode(hrp: "npub", bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bech32_pubkey_decode(_ pubkey: String) -> String? {
|
|
||||||
guard let decoded = try? bech32_decode(pubkey), decoded.hrp == "npub" else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex_encode(decoded.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func bech32_nopre_pubkey(_ pubkey: String) -> String? {
|
func bech32_nopre_pubkey(_ pubkey: String) -> String? {
|
||||||
guard let bytes = hex_decode(pubkey) else {
|
guard let bytes = hex_decode(pubkey) else {
|
||||||
return nil
|
return nil
|
||||||
@@ -101,18 +93,16 @@ func generate_new_keypair() -> Keypair {
|
|||||||
return Keypair(pubkey: pubkey, privkey: privkey)
|
return Keypair(pubkey: pubkey, privkey: privkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func privkey_to_pubkey_raw(sec: [UInt8]) -> String? {
|
func privkey_to_pubkey(privkey: String) -> String? {
|
||||||
|
guard let sec = hex_decode(privkey) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
guard let key = try? secp256k1.Signing.PrivateKey(rawRepresentation: sec) else {
|
guard let key = try? secp256k1.Signing.PrivateKey(rawRepresentation: sec) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return hex_encode(Data(key.publicKey.xonly.bytes))
|
return hex_encode(Data(key.publicKey.xonly.bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func privkey_to_pubkey(privkey: String) -> String? {
|
|
||||||
guard let sec = hex_decode(privkey) else { return nil }
|
|
||||||
return privkey_to_pubkey_raw(sec: sec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func save_pubkey(pubkey: String) {
|
func save_pubkey(pubkey: String) {
|
||||||
UserDefaults.standard.set(pubkey, forKey: "pubkey")
|
UserDefaults.standard.set(pubkey, forKey: "pubkey")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,3 @@ func localeToLanguage(_ locale: String) -> String? {
|
|||||||
return NSLocale(localeIdentifier: locale).languageCode
|
return NSLocale(localeIdentifier: locale).languageCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a localized string that is pluralized based on a single Int-typed count variable.
|
|
||||||
func pluralizedString(key: String, count: Int, locale: Locale = Locale.current) -> String {
|
|
||||||
let format = localizedStringFormat(key: key, locale: locale)
|
|
||||||
return String(format: format, locale: locale, count)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,32 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
func count_leading_hashes(_ str: String) -> Int {
|
|
||||||
var count = 0
|
|
||||||
for c in str {
|
|
||||||
if c == "#" {
|
|
||||||
count += 1
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_heading_title_size(count: Int) -> SwiftUI.Font {
|
|
||||||
if count >= 3 {
|
|
||||||
return Font.title3
|
|
||||||
} else if count >= 2 {
|
|
||||||
return Font.title2
|
|
||||||
} else if count >= 1 {
|
|
||||||
return Font.title
|
|
||||||
}
|
|
||||||
|
|
||||||
return Font.body
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Markdown {
|
public struct Markdown {
|
||||||
private var detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
|
private var detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
|
||||||
@@ -45,18 +19,6 @@ public struct Markdown {
|
|||||||
public static func parse(content: String) -> AttributedString {
|
public static func parse(content: String) -> AttributedString {
|
||||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||||
|
|
||||||
guard content.utf8.count > 0 else {
|
|
||||||
return AttributedString(stringLiteral: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
let leading_hashes = count_leading_hashes(content)
|
|
||||||
if leading_hashes > 0 {
|
|
||||||
if var str = try? AttributedString(markdown: content) {
|
|
||||||
str.font = get_heading_title_size(count: leading_hashes)
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: escape unintentional markdown
|
// TODO: escape unintentional markdown
|
||||||
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class PostBox {
|
|||||||
relayer.attempts += 1
|
relayer.attempts += 1
|
||||||
relayer.last_attempt = Int64(Date().timeIntervalSince1970)
|
relayer.last_attempt = Int64(Date().timeIntervalSince1970)
|
||||||
relayer.retry_after *= 1.5
|
relayer.retry_after *= 1.5
|
||||||
if pool.get_relay(relayer.relay) != nil {
|
if let relay = pool.get_relay(relayer.relay) {
|
||||||
print("flushing event \(event.event.id) to \(relayer.relay)")
|
print("flushing event \(event.event.id) to \(relayer.relay)")
|
||||||
} else {
|
} else {
|
||||||
print("could not find relay when flushing: \(relayer.relay)")
|
print("could not find relay when flushing: \(relayer.relay)")
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// RelayMetadatas.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-02-09.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class RelayMetadatas {
|
||||||
|
private var metadata: [String: RelayMetadata] = [:]
|
||||||
|
|
||||||
|
func lookup(relay_id: String) -> RelayMetadata? {
|
||||||
|
return metadata[relay_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func insert(relay_id: String, metadata: RelayMetadata) {
|
||||||
|
self.metadata[relay_id] = metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// RelayModel.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Bryan Montz on 6/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
final class RelayModel: Hashable {
|
|
||||||
|
|
||||||
let url: RelayURL
|
|
||||||
let log: RelayLog
|
|
||||||
let metadata: RelayMetadata
|
|
||||||
|
|
||||||
init(_ url: RelayURL, metadata: RelayMetadata) {
|
|
||||||
self.url = url
|
|
||||||
self.log = RelayLog(url.url)
|
|
||||||
self.metadata = metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
static func == (lhs: RelayModel, rhs: RelayModel) -> Bool {
|
|
||||||
lhs.url == rhs.url
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
hasher.combine(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// RelayModels.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Bryan Montz on 6/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
final class RelayModelCache {
|
|
||||||
private var models = [RelayURL: RelayModel]()
|
|
||||||
|
|
||||||
func model(withURL url: RelayURL) -> RelayModel? {
|
|
||||||
models[url]
|
|
||||||
}
|
|
||||||
|
|
||||||
func model(with_relay_id url_string: String) -> RelayModel? {
|
|
||||||
guard let url = RelayURL(url_string) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return model(withURL: url)
|
|
||||||
}
|
|
||||||
|
|
||||||
func insert(model: RelayModel) {
|
|
||||||
models[model.url] = model
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
//
|
|
||||||
// Router.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Scott Penrose on 5/7/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
enum Route: Hashable {
|
|
||||||
case ProfileByKey(pubkey: String)
|
|
||||||
case Profile(profile: ProfileModel, followers: FollowersModel)
|
|
||||||
case Followers(followers: FollowersModel)
|
|
||||||
case Relay(relay: String, showActionButtons: Binding<Bool>)
|
|
||||||
case RelayDetail(relay: String, metadata: RelayMetadata)
|
|
||||||
case Following(following: FollowingModel)
|
|
||||||
case MuteList(users: [String])
|
|
||||||
case RelayConfig
|
|
||||||
case Script(script: ScriptModel)
|
|
||||||
case Bookmarks
|
|
||||||
case Config
|
|
||||||
case EditMetadata
|
|
||||||
case DMChat(dms: DirectMessageModel)
|
|
||||||
case UserRelays(relays: [String])
|
|
||||||
case KeySettings(keypair: Keypair)
|
|
||||||
case AppearanceSettings(settings: UserSettingsStore)
|
|
||||||
case NotificationSettings(settings: UserSettingsStore)
|
|
||||||
case ZapSettings(settings: UserSettingsStore)
|
|
||||||
case TranslationSettings(settings: UserSettingsStore)
|
|
||||||
case SearchSettings(settings: UserSettingsStore)
|
|
||||||
case DeveloperSettings(settings: UserSettingsStore)
|
|
||||||
case Thread(thread: ThreadModel)
|
|
||||||
case Reposts(reposts: RepostsModel)
|
|
||||||
case Reactions(reactions: ReactionsModel)
|
|
||||||
case Zaps(target: ZapTarget)
|
|
||||||
case Search(search: SearchModel)
|
|
||||||
case EULA
|
|
||||||
case Login
|
|
||||||
case CreateAccount
|
|
||||||
case SaveKeys(account: CreateAccountModel)
|
|
||||||
case Wallet(wallet: WalletModel)
|
|
||||||
case WalletScanner(result: Binding<WalletScanResult>)
|
|
||||||
case FollowersYouKnow(friendedFollowers: [String], followers: FollowersModel)
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
func view(navigationCordinator: NavigationCoordinator, damusState: DamusState) -> some View {
|
|
||||||
switch self {
|
|
||||||
case .ProfileByKey(let pubkey):
|
|
||||||
ProfileView(damus_state: damusState, pubkey: pubkey)
|
|
||||||
case .Profile(let profile, let followers):
|
|
||||||
ProfileView(damus_state: damusState, profile: profile, followers: followers)
|
|
||||||
case .Followers(let followers):
|
|
||||||
FollowersView(damus_state: damusState, followers: followers)
|
|
||||||
case .Relay(let relay, let showActionButtons):
|
|
||||||
RelayView(state: damusState, relay: relay, showActionButtons: showActionButtons)
|
|
||||||
case .RelayDetail(let relay, let metadata):
|
|
||||||
RelayDetailView(state: damusState, relay: relay, nip11: metadata)
|
|
||||||
case .Following(let following):
|
|
||||||
FollowingView(damus_state: damusState, following: following)
|
|
||||||
case .MuteList(let users):
|
|
||||||
MutelistView(damus_state: damusState, users: users)
|
|
||||||
case .RelayConfig:
|
|
||||||
RelayConfigView(state: damusState)
|
|
||||||
case .Bookmarks:
|
|
||||||
BookmarksView(state: damusState)
|
|
||||||
case .Config:
|
|
||||||
ConfigView(state: damusState)
|
|
||||||
case .EditMetadata:
|
|
||||||
EditMetadataView(damus_state: damusState)
|
|
||||||
case .DMChat(let dms):
|
|
||||||
DMChatView(damus_state: damusState, dms: dms)
|
|
||||||
case .UserRelays(let relays):
|
|
||||||
UserRelaysView(state: damusState, relays: relays)
|
|
||||||
case .KeySettings(let keypair):
|
|
||||||
KeySettingsView(keypair: keypair)
|
|
||||||
case .AppearanceSettings(let settings):
|
|
||||||
AppearanceSettingsView(settings: settings)
|
|
||||||
case .NotificationSettings(let settings):
|
|
||||||
NotificationSettingsView(settings: settings)
|
|
||||||
case .ZapSettings(let settings):
|
|
||||||
ZapSettingsView(settings: settings)
|
|
||||||
case .TranslationSettings(let settings):
|
|
||||||
TranslationSettingsView(settings: settings)
|
|
||||||
case .SearchSettings(let settings):
|
|
||||||
SearchSettingsView(settings: settings)
|
|
||||||
case .DeveloperSettings(let settings):
|
|
||||||
DeveloperSettingsView(settings: settings)
|
|
||||||
case .Thread(let thread):
|
|
||||||
ThreadView(state: damusState, thread: thread)
|
|
||||||
case .Reposts(let reposts):
|
|
||||||
RepostsView(damus_state: damusState, model: reposts)
|
|
||||||
case .Reactions(let reactions):
|
|
||||||
ReactionsView(damus_state: damusState, model: reactions)
|
|
||||||
case .Zaps(let target):
|
|
||||||
ZapsView(state: damusState, target: target)
|
|
||||||
case .Search(let search):
|
|
||||||
SearchView(appstate: damusState, search: search)
|
|
||||||
case .EULA:
|
|
||||||
EULAView(nav: navigationCordinator)
|
|
||||||
case .Login:
|
|
||||||
LoginView(nav: navigationCordinator)
|
|
||||||
case .CreateAccount:
|
|
||||||
CreateAccountView(nav: navigationCordinator)
|
|
||||||
case .SaveKeys(let account):
|
|
||||||
SaveKeysView(account: account)
|
|
||||||
case .Wallet(let walletModel):
|
|
||||||
WalletView(damus_state: damusState, model: walletModel)
|
|
||||||
case .WalletScanner(let walletScanResult):
|
|
||||||
WalletScannerView(result: walletScanResult)
|
|
||||||
case .FollowersYouKnow(let friendedFollowers, let followers):
|
|
||||||
FollowersYouKnowView(damus_state: damusState, friended_followers: friendedFollowers, followers: followers)
|
|
||||||
case .Script(let load_model):
|
|
||||||
LoadScript(pool: damusState.pool, model: load_model)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func == (lhs: Route, rhs: Route) -> Bool {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case (.ProfileByKey (let lhs_pubkey), .ProfileByKey(let rhs_pubkey)):
|
|
||||||
return lhs_pubkey == rhs_pubkey
|
|
||||||
case (.Profile (let lhs_profile, _), .Profile(let rhs_profile, _)):
|
|
||||||
return lhs_profile == rhs_profile
|
|
||||||
case (.Followers (_), .Followers (_)):
|
|
||||||
return true
|
|
||||||
case (.Relay (let lhs_relay, _), .Relay (let rhs_relay, _)):
|
|
||||||
return lhs_relay == rhs_relay
|
|
||||||
case (.RelayDetail(let lhs_relay, _), .RelayDetail(let rhs_relay, _)):
|
|
||||||
return lhs_relay == rhs_relay
|
|
||||||
case (.Following(_), .Following(_)):
|
|
||||||
return true
|
|
||||||
case (.MuteList(let lhs_users), .MuteList(let rhs_users)):
|
|
||||||
return lhs_users == rhs_users
|
|
||||||
case (.RelayConfig, .RelayConfig):
|
|
||||||
return true
|
|
||||||
case (.Bookmarks, .Bookmarks):
|
|
||||||
return true
|
|
||||||
case (.Config, .Config):
|
|
||||||
return true
|
|
||||||
case (.EditMetadata, .EditMetadata):
|
|
||||||
return true
|
|
||||||
case (.DMChat(let lhs_dms), .DMChat(let rhs_dms)):
|
|
||||||
return lhs_dms.our_pubkey == rhs_dms.our_pubkey
|
|
||||||
case (.UserRelays(let lhs_relays), .UserRelays(let rhs_relays)):
|
|
||||||
return lhs_relays == rhs_relays
|
|
||||||
case (.KeySettings(let lhs_keypair), .KeySettings(let rhs_keypair)):
|
|
||||||
return lhs_keypair.pubkey == rhs_keypair.pubkey
|
|
||||||
case (.AppearanceSettings(_), .AppearanceSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.NotificationSettings(_), .NotificationSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.ZapSettings(_), .ZapSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.TranslationSettings(_), .TranslationSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.SearchSettings(_), .SearchSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.DeveloperSettings(_), .DeveloperSettings(_)):
|
|
||||||
return true
|
|
||||||
case (.Thread(let lhs_threadModel), .Thread(thread: let rhs_threadModel)):
|
|
||||||
return lhs_threadModel.event.id == rhs_threadModel.event.id
|
|
||||||
case (.Reposts(let lhs_reposts), .Reposts(let rhs_reposts)):
|
|
||||||
return lhs_reposts.target == rhs_reposts.target
|
|
||||||
case (.Reactions(let lhs_reactions), .Reactions(let rhs_reactions)):
|
|
||||||
return lhs_reactions.target == rhs_reactions.target
|
|
||||||
case (.Zaps(let lhs_target), .Zaps(let rhs_target)):
|
|
||||||
return lhs_target == rhs_target
|
|
||||||
case (.Search(let lhs_search), .Search(let rhs_search)):
|
|
||||||
return lhs_search.sub_id == rhs_search.sub_id && lhs_search.profiles_subid == rhs_search.profiles_subid
|
|
||||||
case (.EULA, .EULA):
|
|
||||||
return true
|
|
||||||
case (.Login, .Login):
|
|
||||||
return true
|
|
||||||
case (.CreateAccount, .CreateAccount):
|
|
||||||
return true
|
|
||||||
case (.SaveKeys(let lhs_account), .SaveKeys(let rhs_account)):
|
|
||||||
return lhs_account.pubkey == rhs_account.pubkey
|
|
||||||
case (.Wallet(_), .Wallet(_)):
|
|
||||||
return true
|
|
||||||
case (.WalletScanner(_), .WalletScanner(_)):
|
|
||||||
return true
|
|
||||||
case (.FollowersYouKnow(_, _), .FollowersYouKnow(_, _)):
|
|
||||||
return true
|
|
||||||
case (.Script(_), .Script(_)):
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(into hasher: inout Hasher) {
|
|
||||||
switch self {
|
|
||||||
case .ProfileByKey(let pubkey):
|
|
||||||
hasher.combine("profilebykey")
|
|
||||||
hasher.combine(pubkey)
|
|
||||||
case .Profile(let profile, _):
|
|
||||||
hasher.combine("profile")
|
|
||||||
hasher.combine(profile.pubkey)
|
|
||||||
case .Followers(_):
|
|
||||||
hasher.combine("followers")
|
|
||||||
case .Relay(let relay, _):
|
|
||||||
hasher.combine("relay")
|
|
||||||
hasher.combine(relay)
|
|
||||||
case .RelayDetail(let relay, _):
|
|
||||||
hasher.combine("relayDetail")
|
|
||||||
hasher.combine(relay)
|
|
||||||
case .Following(_):
|
|
||||||
hasher.combine("following")
|
|
||||||
case .MuteList(let users):
|
|
||||||
hasher.combine("muteList")
|
|
||||||
hasher.combine(users)
|
|
||||||
case .RelayConfig:
|
|
||||||
hasher.combine("relayConfig")
|
|
||||||
case .Bookmarks:
|
|
||||||
hasher.combine("bookmarks")
|
|
||||||
case .Config:
|
|
||||||
hasher.combine("config")
|
|
||||||
case .EditMetadata:
|
|
||||||
hasher.combine("editMetadata")
|
|
||||||
case .DMChat(let dms):
|
|
||||||
hasher.combine("dms")
|
|
||||||
hasher.combine(dms.our_pubkey)
|
|
||||||
case .UserRelays(let relays):
|
|
||||||
hasher.combine("userRelays")
|
|
||||||
hasher.combine(relays)
|
|
||||||
case .KeySettings(let keypair):
|
|
||||||
hasher.combine("keySettings")
|
|
||||||
hasher.combine(keypair.pubkey)
|
|
||||||
case .AppearanceSettings(_):
|
|
||||||
hasher.combine("appearanceSettings")
|
|
||||||
case .NotificationSettings(_):
|
|
||||||
hasher.combine("notificationSettings")
|
|
||||||
case .ZapSettings(_):
|
|
||||||
hasher.combine("zapSettings")
|
|
||||||
case .TranslationSettings(_):
|
|
||||||
hasher.combine("translationSettings")
|
|
||||||
case .SearchSettings(_):
|
|
||||||
hasher.combine("searchSettings")
|
|
||||||
case .DeveloperSettings:
|
|
||||||
hasher.combine("developerSettings")
|
|
||||||
case .Thread(let threadModel):
|
|
||||||
hasher.combine("thread")
|
|
||||||
hasher.combine(threadModel.event.id)
|
|
||||||
case .Reposts(let reposts):
|
|
||||||
hasher.combine("reposts")
|
|
||||||
hasher.combine(reposts.target)
|
|
||||||
case .Zaps(let target):
|
|
||||||
hasher.combine("zaps")
|
|
||||||
hasher.combine(target.id)
|
|
||||||
hasher.combine(target.pubkey)
|
|
||||||
case .Reactions(let reactions):
|
|
||||||
hasher.combine("reactions")
|
|
||||||
hasher.combine(reactions.target)
|
|
||||||
case .Search(let search):
|
|
||||||
hasher.combine("search")
|
|
||||||
hasher.combine(search.sub_id)
|
|
||||||
hasher.combine(search.profiles_subid)
|
|
||||||
case .EULA:
|
|
||||||
hasher.combine("eula")
|
|
||||||
case .Login:
|
|
||||||
hasher.combine("login")
|
|
||||||
case .CreateAccount:
|
|
||||||
hasher.combine("createAccount")
|
|
||||||
case .SaveKeys(let account):
|
|
||||||
hasher.combine("saveKeys")
|
|
||||||
hasher.combine(account.pubkey)
|
|
||||||
case .Wallet(_):
|
|
||||||
hasher.combine("wallet")
|
|
||||||
case .WalletScanner(_):
|
|
||||||
hasher.combine("walletScanner")
|
|
||||||
case .FollowersYouKnow(let friendedFollowers, let followers):
|
|
||||||
hasher.combine("followersYouKnow")
|
|
||||||
hasher.combine(friendedFollowers)
|
|
||||||
hasher.combine(followers.sub_id)
|
|
||||||
case .Script(let model):
|
|
||||||
hasher.combine("script")
|
|
||||||
hasher.combine(model.data.count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NavigationCoordinator: ObservableObject {
|
|
||||||
@Published var path = [Route]()
|
|
||||||
|
|
||||||
func push(route: Route) {
|
|
||||||
path.append(route)
|
|
||||||
}
|
|
||||||
|
|
||||||
func popToRoot() {
|
|
||||||
path = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -360,7 +360,7 @@ func determine_zap_target(_ ev: NostrEvent) -> ZapTarget? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func decode_bolt11(_ s: String) -> Invoice? {
|
func decode_bolt11(_ s: String) -> Invoice? {
|
||||||
var bs = note_blocks()
|
var bs = blocks()
|
||||||
bs.num_blocks = 0
|
bs.num_blocks = 0
|
||||||
blocks_init(&bs)
|
blocks_init(&bs)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class Zaps {
|
class Zaps {
|
||||||
private(set) var zaps: [String: Zapping]
|
var zaps: [String: Zapping]
|
||||||
let our_pubkey: String
|
let our_pubkey: String
|
||||||
var our_zaps: [String: [Zapping]]
|
var our_zaps: [String: [Zapping]]
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,11 @@ struct EventActionBar: View {
|
|||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
|
|
||||||
EventActionButton(img: "repost", col: bar.boosted ? Color.green : nil) {
|
EventActionButton(img: "repost", col: bar.boosted ? Color.green : nil) {
|
||||||
self.show_repost_action = true
|
if bar.boosted {
|
||||||
|
notify(.delete, bar.our_boost)
|
||||||
|
} else {
|
||||||
|
self.show_repost_action = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.accessibilityLabel(NSLocalizedString("Reposts", comment: "Accessibility label for boosts button"))
|
.accessibilityLabel(NSLocalizedString("Reposts", comment: "Accessibility label for boosts button"))
|
||||||
Text(verbatim: "\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
|
Text(verbatim: "\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
|
||||||
|
|||||||
@@ -25,27 +25,25 @@ struct EventDetailBar: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
if bar.boosts > 0 {
|
if bar.boosts > 0 {
|
||||||
NavigationLink(value: Route.Reposts(reposts: RepostsModel(state: state, target: target))) {
|
NavigationLink(destination: RepostsView(damus_state: state, model: RepostsModel(state: state, target: target))) {
|
||||||
let nounString = pluralizedString(key: "reposts_count", count: bar.boosts)
|
let noun = Text(verbatim: repostsCountString(bar.boosts)).foregroundColor(.gray)
|
||||||
let noun = Text(nounString).foregroundColor(.gray)
|
|
||||||
Text("\(Text(verbatim: bar.boosts.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.")
|
Text("\(Text(verbatim: bar.boosts.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.")
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
if bar.likes > 0 && !state.settings.onlyzaps_mode {
|
if bar.likes > 0 && !state.settings.onlyzaps_mode {
|
||||||
NavigationLink(value: Route.Reactions(reactions: ReactionsModel(state: state, target: target))) {
|
NavigationLink(destination: ReactionsView(damus_state: state, model: ReactionsModel(state: state, target: target))) {
|
||||||
let nounString = pluralizedString(key: "reactions_count", count: bar.likes)
|
let noun = Text(verbatim: reactionsCountString(bar.likes)).foregroundColor(.gray)
|
||||||
let noun = Text(nounString).foregroundColor(.gray)
|
|
||||||
Text("\(Text(verbatim: bar.likes.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reactions there are on a post. In source English, the first variable is the number of reactions, and the second variable is 'Reaction' or 'Reactions'.")
|
Text("\(Text(verbatim: bar.likes.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reactions there are on a post. In source English, the first variable is the number of reactions, and the second variable is 'Reaction' or 'Reactions'.")
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
if bar.zaps > 0 {
|
if bar.zaps > 0 {
|
||||||
NavigationLink(value: Route.Zaps(target: .note(id: target, author: target_pk))) {
|
let dst = ZapsView(state: state, target: .note(id: target, author: target_pk))
|
||||||
let nounString = pluralizedString(key: "zaps_count", count: bar.zaps)
|
NavigationLink(destination: dst) {
|
||||||
let noun = Text(nounString).foregroundColor(.gray)
|
let noun = Text(verbatim: zapsCountString(bar.zaps)).foregroundColor(.gray)
|
||||||
Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.")
|
Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.")
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
@@ -54,6 +52,21 @@ struct EventDetailBar: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func repostsCountString(_ count: Int, locale: Locale = Locale.current) -> String {
|
||||||
|
let format = localizedStringFormat(key: "reposts_count", locale: locale)
|
||||||
|
return String(format: format, locale: locale, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reactionsCountString(_ count: Int, locale: Locale = Locale.current) -> String {
|
||||||
|
let format = localizedStringFormat(key: "reactions_count", locale: locale)
|
||||||
|
return String(format: format, locale: locale, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func zapsCountString(_ count: Int, locale: Locale = Locale.current) -> String {
|
||||||
|
let format = localizedStringFormat(key: "zaps_count", locale: locale)
|
||||||
|
return String(format: format, locale: locale, count)
|
||||||
|
}
|
||||||
|
|
||||||
struct EventDetailBar_Previews: PreviewProvider {
|
struct EventDetailBar_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
EventDetailBar(state: test_damus_state(), target: "", target_pk: "")
|
EventDetailBar(state: test_damus_state(), target: "", target_pk: "")
|
||||||
|
|||||||
@@ -8,33 +8,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Kingfisher
|
import Kingfisher
|
||||||
|
|
||||||
struct EditBannerImageView: View {
|
|
||||||
|
|
||||||
var damus_state: DamusState
|
|
||||||
@ObservedObject var viewModel: ImageUploadingObserver
|
|
||||||
let callback: (URL?) -> Void
|
|
||||||
let defaultImage = UIImage(named: "profile-banner") ?? UIImage()
|
|
||||||
|
|
||||||
@State var banner_image: URL? = nil
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
Color(uiColor: .systemBackground)
|
|
||||||
KFAnimatedImage(get_banner_url(banner: banner_image?.absoluteString, pubkey: damus_state.pubkey, profiles: damus_state.profiles))
|
|
||||||
.imageContext(.banner, disable_animation: damus_state.settings.disable_animation)
|
|
||||||
.configure { view in
|
|
||||||
view.framePreloadCount = 3
|
|
||||||
}
|
|
||||||
.placeholder { _ in
|
|
||||||
Color(uiColor: .secondarySystemBackground)
|
|
||||||
}
|
|
||||||
.onFailureImage(defaultImage)
|
|
||||||
|
|
||||||
EditPictureControl(uploader: damus_state.settings.default_media_uploader, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InnerBannerImageView: View {
|
struct InnerBannerImageView: View {
|
||||||
let disable_animation: Bool
|
let disable_animation: Bool
|
||||||
let url: URL?
|
let url: URL?
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ struct BookmarksView: View {
|
|||||||
} else {
|
} else {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
InnerTimelineView(events: EventHolder(events: bookmarks, incoming: []), damus: state, filter: noneFilter)
|
InnerTimelineView(events: EventHolder(events: bookmarks, incoming: []), damus: state, filter: noneFilter)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,16 @@ struct ConfigView: View {
|
|||||||
@State var delete_account_warning: Bool = false
|
@State var delete_account_warning: Bool = false
|
||||||
@State var confirm_delete_account: Bool = false
|
@State var confirm_delete_account: Bool = false
|
||||||
@State var delete_text: String = ""
|
@State var delete_text: String = ""
|
||||||
|
|
||||||
@ObservedObject var settings: UserSettingsStore
|
@ObservedObject var settings: UserSettingsStore
|
||||||
|
|
||||||
private let DELETE_KEYWORD = "DELETE"
|
private let DELETE_KEYWORD = "DELETE"
|
||||||
|
|
||||||
init(state: DamusState) {
|
init(state: DamusState) {
|
||||||
self.state = state
|
self.state = state
|
||||||
_settings = ObservedObject(initialValue: state.settings)
|
_settings = ObservedObject(initialValue: state.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
func textColor() -> Color {
|
func textColor() -> Color {
|
||||||
colorScheme == .light ? DamusColors.black : DamusColors.white
|
colorScheme == .light ? DamusColors.black : DamusColors.white
|
||||||
}
|
}
|
||||||
@@ -36,34 +36,31 @@ struct ConfigView: View {
|
|||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
NavigationLink(value: Route.KeySettings(keypair: state.keypair)) {
|
NavigationLink(destination: KeySettingsView(keypair: state.keypair)) {
|
||||||
IconLabel(NSLocalizedString("Keys", comment: "Settings section for managing keys"), img_name: "Key", color: .purple)
|
IconLabel(NSLocalizedString("Keys", comment: "Settings section for managing keys"), img_name: "key", color: .purple)
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink(value: Route.AppearanceSettings(settings: settings)) {
|
|
||||||
IconLabel(NSLocalizedString("Appearance", comment: "Section header for text and appearance settings"), img_name: "eye", color: .red)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink(value: Route.SearchSettings(settings: settings)) {
|
|
||||||
IconLabel(NSLocalizedString("Search/Universe", comment: "Section header for search/universe settings"), img_name: "search", color: .red)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink(value: Route.NotificationSettings(settings: settings)) {
|
|
||||||
IconLabel(NSLocalizedString("Notifications", comment: "Section header for Damus notifications"), img_name: "notification-bell-on", color: .blue)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink(value: Route.ZapSettings(settings: settings)) {
|
|
||||||
IconLabel(NSLocalizedString("Zaps", comment: "Section header for zap settings"), img_name: "zap.fill", color: .orange)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink(value: Route.TranslationSettings(settings: settings)) {
|
|
||||||
IconLabel(NSLocalizedString("Translation", comment: "Section header for text and appearance settings"), img_name: "globe", color: .green)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: Route.DeveloperSettings(settings: settings)) {
|
NavigationLink(destination: AppearanceSettingsView(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Developer", comment: "Section header for developer settings"), img_name: "magic-stick2.fill", color: DamusColors.adaptableBlack)
|
IconLabel(NSLocalizedString("Appearance", comment: "Section header for text and appearance settings"), img_name: "eye", color: .red)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: SearchSettingsView(settings: settings)) {
|
||||||
|
IconLabel(NSLocalizedString("Search/Universe", comment: "Section header for search/universe settings"), img_name: "magnifyingglass", color: .red)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: NotificationSettingsView(settings: settings)) {
|
||||||
|
IconLabel(NSLocalizedString("Notifications", comment: "Section header for Damus notifications"), img_name: "notification-bell-on", color: .blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: ZapSettingsView(settings: settings)) {
|
||||||
|
IconLabel(NSLocalizedString("Zaps", comment: "Section header for zap settings"), img_name: "zap.fill", color: .orange)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationLink(destination: TranslationSettingsView(settings: settings)) {
|
||||||
|
IconLabel(NSLocalizedString("Translation", comment: "Section header for text and appearance settings"), img_name: "globe", color: .green)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
|
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
@@ -119,11 +116,11 @@ struct ConfigView: View {
|
|||||||
guard let full_kp = state.keypair.to_full() else {
|
guard let full_kp = state.keypair.to_full() else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard delete_text == DELETE_KEYWORD else {
|
guard delete_text == DELETE_KEYWORD else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let ev = created_deleted_account_profile(keypair: full_kp)
|
let ev = created_deleted_account_profile(keypair: full_kp)
|
||||||
state.postbox.send(ev)
|
state.postbox.send(ev)
|
||||||
notify(.logout, ())
|
notify(.logout, ())
|
||||||
@@ -167,7 +164,7 @@ func handle_string_amount(new_value: String) -> Int? {
|
|||||||
guard let amt = NumberFormatter().number(from: filtered) as? Int else {
|
guard let amt = NumberFormatter().number(from: filtered) as? Int else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return amt
|
return amt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import SwiftUI
|
|||||||
|
|
||||||
struct CreateAccountView: View {
|
struct CreateAccountView: View {
|
||||||
@StateObject var account: CreateAccountModel = CreateAccountModel()
|
@StateObject var account: CreateAccountModel = CreateAccountModel()
|
||||||
@StateObject var profileUploadObserver = ImageUploadingObserver()
|
@StateObject var profileUploadViewModel = ProfileUploadingViewModel()
|
||||||
var nav: NavigationCoordinator
|
|
||||||
|
@State var is_done: Bool = false
|
||||||
|
|
||||||
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View {
|
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View {
|
||||||
return VStack(alignment: .leading, spacing: 10.0, content: content)
|
return VStack(alignment: .leading, spacing: 10.0, content: content)
|
||||||
@@ -24,9 +25,13 @@ struct CreateAccountView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
|
NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
EditPictureControl(uploader: .nostrBuild, pubkey: account.pubkey, image_url: $account.profile_image , uploadObserver: profileUploadObserver, callback: uploadedProfilePicture)
|
ProfilePictureSelector(pubkey: account.pubkey, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:))
|
||||||
|
|
||||||
Text(NSLocalizedString("Public Key", comment: "Label to indicate the public key of the account."))
|
Text(NSLocalizedString("Public Key", comment: "Label to indicate the public key of the account."))
|
||||||
.bold()
|
.bold()
|
||||||
@@ -58,7 +63,7 @@ struct CreateAccountView: View {
|
|||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
nav.push(route: Route.SaveKeys(account: account))
|
self.is_done = true
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Create account now", comment: "Button to create account.")
|
Text("Create account now", comment: "Button to create account.")
|
||||||
@@ -67,8 +72,8 @@ struct CreateAccountView: View {
|
|||||||
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
|
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 12, alignment: .center)
|
||||||
}
|
}
|
||||||
.buttonStyle(GradientButtonStyle())
|
.buttonStyle(GradientButtonStyle())
|
||||||
.disabled(profileUploadObserver.isLoading)
|
.disabled(profileUploadViewModel.isLoading)
|
||||||
.opacity(profileUploadObserver.isLoading ? 0.5 : 1)
|
.opacity(profileUploadViewModel.isLoading ? 0.5 : 1)
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
|
|
||||||
LoginPrompt()
|
LoginPrompt()
|
||||||
@@ -83,7 +88,7 @@ struct CreateAccountView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func uploadedProfilePicture(image_url: URL?) {
|
func uploadedProfilePicture(image_url: URL?) {
|
||||||
account.profile_image = image_url
|
account.profile_image = image_url?.absoluteString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +112,7 @@ struct BackNav: View {
|
|||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Image("chevron-left")
|
Image("chevron-left")
|
||||||
.foregroundColor(DamusColors.adaptableBlack)
|
.foregroundColor(.white)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
}
|
}
|
||||||
@@ -130,7 +135,7 @@ extension View {
|
|||||||
struct CreateAccountView_Previews: PreviewProvider {
|
struct CreateAccountView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let model = CreateAccountModel(real: "", nick: "jb55", about: "")
|
let model = CreateAccountModel(real: "", nick: "jb55", about: "")
|
||||||
return CreateAccountView(account: model, nav: .init())
|
return CreateAccountView(account: model)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ struct DMChatView: View, KeyboardReadable {
|
|||||||
LazyVStack(alignment: .leading) {
|
LazyVStack(alignment: .leading) {
|
||||||
ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in
|
ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in
|
||||||
DMView(event: dms.events[ind], damus_state: damus_state)
|
DMView(event: dms.events[ind], damus_state: damus_state)
|
||||||
.contextMenu{MenuItems(event: ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey, bookmarks: damus_state.bookmarks, muted_threads: damus_state.muted_threads, settings: damus_state.settings)}
|
.contextMenu{MenuItems(event: ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey, bookmarks: damus_state.bookmarks, muted_threads: damus_state.muted_threads)}
|
||||||
}
|
}
|
||||||
EndBlock(height: 1)
|
EndBlock(height: 1)
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,8 @@ struct DMChatView: View, KeyboardReadable {
|
|||||||
|
|
||||||
var Header: some View {
|
var Header: some View {
|
||||||
let profile = damus_state.profiles.lookup(id: pubkey)
|
let profile = damus_state.profiles.lookup(id: pubkey)
|
||||||
return NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) {
|
let profile_page = ProfileView(damus_state: damus_state, pubkey: pubkey)
|
||||||
|
return NavigationLink(destination: profile_page) {
|
||||||
HStack {
|
HStack {
|
||||||
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,13 @@ struct DirectMessagesView: View {
|
|||||||
@State var dm_type: DMType = .friend
|
@State var dm_type: DMType = .friend
|
||||||
@ObservedObject var model: DirectMessagesModel
|
@ObservedObject var model: DirectMessagesModel
|
||||||
@ObservedObject var settings: UserSettingsStore
|
@ObservedObject var settings: UserSettingsStore
|
||||||
|
|
||||||
func MainContent(requests: Bool) -> some View {
|
func MainContent(requests: Bool) -> some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
let chat = DMChatView(damus_state: damus_state, dms: model.active_model)
|
||||||
|
NavigationLink(destination: chat, isActive: $model.open_dm) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
LazyVStack(spacing: 0) {
|
LazyVStack(spacing: 0) {
|
||||||
if model.dms.isEmpty, !model.loading {
|
if model.dms.isEmpty, !model.loading {
|
||||||
EmptyTimelineView()
|
EmptyTimelineView()
|
||||||
@@ -50,8 +54,7 @@ struct DirectMessagesView: View {
|
|||||||
if ok, let ev = model.events.last {
|
if ok, let ev = model.events.last {
|
||||||
EventView(damus: damus_state, event: ev, pubkey: model.pubkey, options: options)
|
EventView(damus: damus_state, event: ev, pubkey: model.pubkey, options: options)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
self.model.set_active_dm_model(model)
|
self.model.open_dm_by_model(model)
|
||||||
damus_state.nav.push(route: Route.DMChat(dms: self.model.active_model))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|||||||
@@ -56,13 +56,18 @@ By using our Application, you signify your acceptance of this EULA. If you do no
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
struct EULAView: View {
|
struct EULAView: View {
|
||||||
|
@State private var login = false
|
||||||
|
@State var accepted = false
|
||||||
@Environment(\.colorScheme) var colorScheme
|
@Environment(\.colorScheme) var colorScheme
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
var nav: NavigationCoordinator
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
NavigationLink(destination: LoginView(accepted: $accepted), isActive: $login) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
Text(Markdown.parse(content: eula))
|
Text(Markdown.parse(content: eula))
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
@@ -91,7 +96,8 @@ struct EULAView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
nav.push(route: Route.Login)
|
accepted = true
|
||||||
|
login.toggle()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.")
|
Text("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.")
|
||||||
@@ -120,6 +126,6 @@ struct EULAView: View {
|
|||||||
|
|
||||||
struct EULAView_Previews: PreviewProvider {
|
struct EULAView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
EULAView(nav: .init())
|
EULAView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ enum EventViewKind {
|
|||||||
case small
|
case small
|
||||||
case normal
|
case normal
|
||||||
case selected
|
case selected
|
||||||
case title
|
|
||||||
case subheadline
|
case subheadline
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +40,9 @@ struct EventView: View {
|
|||||||
if let zap = damus.zaps.zaps[event.id] {
|
if let zap = damus.zaps.zaps[event.id] {
|
||||||
ZapEvent(damus: damus, zap: zap, is_top_zap: options.contains(.top_zap))
|
ZapEvent(damus: damus, zap: zap, is_top_zap: options.contains(.top_zap))
|
||||||
} else {
|
} else {
|
||||||
|
Text("Invalid Zap", comment: "Text indicating that a zap event is malformed and could not be displayed.")
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
} else if event.known_kind == .longform {
|
|
||||||
LongformPreview(state: damus, ev: event, options: options)
|
|
||||||
} else {
|
} else {
|
||||||
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
|
TextEvent(damus: damus, event: event, pubkey: pubkey, options: options)
|
||||||
//.padding([.top], 6)
|
//.padding([.top], 6)
|
||||||
@@ -110,8 +108,6 @@ func eventviewsize_to_font(_ size: EventViewKind) -> Font {
|
|||||||
return .body
|
return .body
|
||||||
case .selected:
|
case .selected:
|
||||||
return .custom("selected", size: 21.0)
|
return .custom("selected", size: 21.0)
|
||||||
case .title:
|
|
||||||
return .title
|
|
||||||
case .subheadline:
|
case .subheadline:
|
||||||
return .subheadline
|
return .subheadline
|
||||||
}
|
}
|
||||||
@@ -127,8 +123,6 @@ func eventviewsize_to_uifont(_ size: EventViewKind) -> UIFont {
|
|||||||
return .preferredFont(forTextStyle: .title2)
|
return .preferredFont(forTextStyle: .title2)
|
||||||
case .subheadline:
|
case .subheadline:
|
||||||
return .preferredFont(forTextStyle: .subheadline)
|
return .preferredFont(forTextStyle: .subheadline)
|
||||||
case .title:
|
|
||||||
return .preferredFont(forTextStyle: .title1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,12 +136,15 @@ struct EventView_Previews: PreviewProvider {
|
|||||||
EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true, size: .big)
|
EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true, size: .big)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
EventView( damus: test_damus_state(), event: test_event )
|
EventView( damus: test_damus_state(), event: test_event )
|
||||||
|
|
||||||
EventView( damus: test_damus_state(), event: test_longform_event.event, options: [.wide] )
|
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let test_event =
|
||||||
|
NostrEvent(
|
||||||
|
content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jpg cool",
|
||||||
|
pubkey: "pk",
|
||||||
|
createdAt: Int64(Date().timeIntervalSince1970 - 100)
|
||||||
|
)
|
||||||
|
|||||||
@@ -59,20 +59,24 @@ struct BuilderEventView: View {
|
|||||||
|
|
||||||
func load() {
|
func load() {
|
||||||
subscribe(filters: [
|
subscribe(filters: [
|
||||||
NostrFilter(ids: [self.event_id], limit: 1)
|
NostrFilter(ids: [self.event_id], limit: 1),
|
||||||
|
NostrFilter(
|
||||||
|
kinds: [.zap],
|
||||||
|
referenced_ids: [self.event_id]
|
||||||
|
)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
if let event {
|
if let event {
|
||||||
EventView(damus: damus, event: event, options: .embedded)
|
let ev = event.get_inner_event(cache: damus.events) ?? event
|
||||||
.padding([.top, .bottom], 8)
|
let thread = ThreadModel(event: ev, damus_state: damus)
|
||||||
.onTapGesture {
|
let dest = ThreadView(state: damus, thread: thread)
|
||||||
let ev = event.get_inner_event(cache: damus.events) ?? event
|
NavigationLink(destination: dest) {
|
||||||
let thread = ThreadModel(event: ev, damus_state: damus)
|
EventView(damus: damus, event: event, options: .embedded)
|
||||||
damus.nav.push(route: .Thread(thread: thread))
|
.padding([.top, .bottom], 8)
|
||||||
}
|
}.buttonStyle(.plain)
|
||||||
} else {
|
} else {
|
||||||
ProgressView().padding()
|
ProgressView().padding()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// ContextButton.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ContextButton: View {
|
|
||||||
var body: some View {
|
|
||||||
Text(verbatim: /*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ContextButton_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
ContextButton()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
//
|
|
||||||
// EventTop.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct EventTop: View {
|
|
||||||
let state: DamusState
|
|
||||||
let event: NostrEvent
|
|
||||||
let pubkey: String
|
|
||||||
let is_anon: Bool
|
|
||||||
|
|
||||||
init(state: DamusState, event: NostrEvent, pubkey: String, is_anon: Bool) {
|
|
||||||
self.state = state
|
|
||||||
self.event = event
|
|
||||||
self.pubkey = pubkey
|
|
||||||
self.is_anon = is_anon
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProfileName(is_anon: Bool) -> some View {
|
|
||||||
let profile = state.profiles.lookup(id: self.pubkey)
|
|
||||||
let pk = is_anon ? "anon" : self.pubkey
|
|
||||||
return EventProfileName(pubkey: pk, profile: profile, damus: state, size: .normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack(alignment: .center, spacing: 0) {
|
|
||||||
ProfileName(is_anon: is_anon)
|
|
||||||
TimeDot()
|
|
||||||
RelativeTime(time: state.events.get_cache_data(event.id).relative_time)
|
|
||||||
Spacer()
|
|
||||||
EventMenuContext(damus: state, event: event)
|
|
||||||
}
|
|
||||||
|
|
||||||
.lineLimit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EventTop_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
EventTop(state: test_damus_state(), event: test_event, pubkey: test_event.pubkey, is_anon: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// RelativeTime.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct RelativeTime: View {
|
|
||||||
@ObservedObject var time: RelativeTimeModel
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Text(verbatim: "\(time.value)")
|
|
||||||
.font(.system(size: 16))
|
|
||||||
.foregroundColor(.gray)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct RelativeTime_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
RelativeTime(time: RelativeTimeModel())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// ReplyPart.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ReplyPart: View {
|
|
||||||
let event: NostrEvent
|
|
||||||
let privkey: String?
|
|
||||||
let profiles: Profiles
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Group {
|
|
||||||
if event_is_reply(event, privkey: privkey) {
|
|
||||||
ReplyDescription(event: event, profiles: profiles)
|
|
||||||
} else {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReplyPart_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
ReplyPart(event: test_event, privkey: nil, profiles: test_damus_state().profiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// TimeDot.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct TimeDot: View {
|
|
||||||
var body: some View {
|
|
||||||
Text(verbatim: "⋅")
|
|
||||||
.font(.footnote)
|
|
||||||
.foregroundColor(.gray)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TimeDot_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
TimeDot()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,24 +21,11 @@ struct EventBody: View {
|
|||||||
self.options = options
|
self.options = options
|
||||||
self.should_show_img = should_show_img ?? should_show_images(settings: damus_state.settings, contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
|
self.should_show_img = should_show_img ?? should_show_images(settings: damus_state.settings, contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
var note_content: some View {
|
var body: some View {
|
||||||
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, options: options)
|
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, size: size, options: options)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
if event.known_kind == .longform {
|
|
||||||
LongformPreviewBody(state: damus_state, ev: event, options: options)
|
|
||||||
|
|
||||||
// truncated longform bodies are just the preview
|
|
||||||
if !options.contains(.truncate_content) {
|
|
||||||
note_content
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
note_content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EventBody_Previews: PreviewProvider {
|
struct EventBody_Previews: PreviewProvider {
|
||||||
|
|||||||
@@ -13,29 +13,18 @@ struct EventMenuContext: View {
|
|||||||
let target_pubkey: String
|
let target_pubkey: String
|
||||||
let bookmarks: BookmarksManager
|
let bookmarks: BookmarksManager
|
||||||
let muted_threads: MutedThreadsManager
|
let muted_threads: MutedThreadsManager
|
||||||
@ObservedObject var settings: UserSettingsStore
|
|
||||||
|
|
||||||
init(damus: DamusState, event: NostrEvent) {
|
|
||||||
self.event = event
|
|
||||||
self.keypair = damus.keypair
|
|
||||||
self.target_pubkey = event.pubkey
|
|
||||||
self.bookmarks = damus.bookmarks
|
|
||||||
self.muted_threads = damus.muted_threads
|
|
||||||
self._settings = ObservedObject(wrappedValue: damus.settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Menu {
|
Menu {
|
||||||
|
|
||||||
MenuItems(event: event, keypair: keypair, target_pubkey: target_pubkey, bookmarks: bookmarks, muted_threads: muted_threads, settings: settings)
|
MenuItems(event: event, keypair: keypair, target_pubkey: target_pubkey, bookmarks: bookmarks, muted_threads: muted_threads)
|
||||||
|
|
||||||
} label: {
|
} label: {
|
||||||
Label("", systemImage: "ellipsis")
|
Label("", systemImage: "ellipsis")
|
||||||
.foregroundColor(Color.gray)
|
.foregroundColor(Color.gray)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding([.bottom], 4)
|
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onTapGesture {}
|
.onTapGesture {}
|
||||||
}
|
}
|
||||||
@@ -47,12 +36,11 @@ struct MenuItems: View {
|
|||||||
let target_pubkey: String
|
let target_pubkey: String
|
||||||
let bookmarks: BookmarksManager
|
let bookmarks: BookmarksManager
|
||||||
let muted_threads: MutedThreadsManager
|
let muted_threads: MutedThreadsManager
|
||||||
@ObservedObject var settings: UserSettingsStore
|
|
||||||
|
|
||||||
@State private var isBookmarked: Bool = false
|
@State private var isBookmarked: Bool = false
|
||||||
@State private var isMutedThread: Bool = false
|
@State private var isMutedThread: Bool = false
|
||||||
|
|
||||||
init(event: NostrEvent, keypair: Keypair, target_pubkey: String, bookmarks: BookmarksManager, muted_threads: MutedThreadsManager, settings: UserSettingsStore) {
|
init(event: NostrEvent, keypair: Keypair, target_pubkey: String, bookmarks: BookmarksManager, muted_threads: MutedThreadsManager) {
|
||||||
let bookmarked = bookmarks.isBookmarked(event)
|
let bookmarked = bookmarks.isBookmarked(event)
|
||||||
self._isBookmarked = State(initialValue: bookmarked)
|
self._isBookmarked = State(initialValue: bookmarked)
|
||||||
|
|
||||||
@@ -64,7 +52,6 @@ struct MenuItems: View {
|
|||||||
self.event = event
|
self.event = event
|
||||||
self.keypair = keypair
|
self.keypair = keypair
|
||||||
self.target_pubkey = target_pubkey
|
self.target_pubkey = target_pubkey
|
||||||
self.settings = settings
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -88,12 +75,10 @@ struct MenuItems: View {
|
|||||||
Label(NSLocalizedString("Copy note ID", comment: "Context menu option for copying the ID of the note."), image: "note-book")
|
Label(NSLocalizedString("Copy note ID", comment: "Context menu option for copying the ID of the note."), image: "note-book")
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.developer_mode {
|
Button {
|
||||||
Button {
|
UIPasteboard.general.string = event_to_json(ev: event)
|
||||||
UIPasteboard.general.string = event_to_json(ev: event)
|
} label: {
|
||||||
} label: {
|
Label(NSLocalizedString("Copy note JSON", comment: "Context menu option for copying the JSON text from the note."), image: "code.on.square")
|
||||||
Label(NSLocalizedString("Copy note JSON", comment: "Context menu option for copying the JSON text from the note."), image: "code.on.square")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ func eventview_pfp_size(_ size: EventViewKind) -> CGFloat {
|
|||||||
return PFP_SIZE
|
return PFP_SIZE
|
||||||
case .selected:
|
case .selected:
|
||||||
return PFP_SIZE
|
return PFP_SIZE
|
||||||
case .title:
|
|
||||||
return PFP_SIZE
|
|
||||||
case .subheadline:
|
case .subheadline:
|
||||||
return PFP_SIZE * 0.5
|
return PFP_SIZE * 0.5
|
||||||
}
|
}
|
||||||
@@ -39,7 +37,7 @@ struct EventProfile: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
VStack {
|
VStack {
|
||||||
NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) {
|
NavigationLink(destination: ProfileView(damus_state: damus_state, pubkey: pubkey)) {
|
||||||
ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation)
|
ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,146 +0,0 @@
|
|||||||
//
|
|
||||||
// EventShell.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct EventShell<Content: View>: View {
|
|
||||||
let state: DamusState
|
|
||||||
let event: NostrEvent
|
|
||||||
let pubkey: String
|
|
||||||
let options: EventViewOptions
|
|
||||||
let content: Content
|
|
||||||
|
|
||||||
init(state: DamusState, event: NostrEvent, pubkey: String, options: EventViewOptions, @ViewBuilder content: () -> Content) {
|
|
||||||
self.state = state
|
|
||||||
self.event = event
|
|
||||||
self.options = options
|
|
||||||
self.pubkey = pubkey
|
|
||||||
self.content = content()
|
|
||||||
}
|
|
||||||
|
|
||||||
init(state: DamusState, event: NostrEvent, options: EventViewOptions, @ViewBuilder content: () -> Content) {
|
|
||||||
self.state = state
|
|
||||||
self.event = event
|
|
||||||
self.options = options
|
|
||||||
self.pubkey = event.pubkey
|
|
||||||
self.content = content()
|
|
||||||
}
|
|
||||||
|
|
||||||
var has_action_bar: Bool {
|
|
||||||
!options.contains(.no_action_bar)
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_mention() -> Mention? {
|
|
||||||
if self.options.contains(.nested) || self.options.contains(.no_mentions) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return first_eref_mention(ev: event, privkey: state.keypair.privkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mention(_ mention: Mention) -> some View {
|
|
||||||
return BuilderEventView(damus: state, event_id: mention.ref.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ActionBar: some View {
|
|
||||||
return EventActionBar(damus_state: state, event: event)
|
|
||||||
.padding([.top], 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Pfp(is_anon: Bool) -> some View {
|
|
||||||
return MaybeAnonPfpView(state: state, is_anon: is_anon, pubkey: pubkey, size: options.contains(.small_pfp) ? eventview_pfp_size(.small) : PFP_SIZE )
|
|
||||||
}
|
|
||||||
|
|
||||||
var Threaded: some View {
|
|
||||||
HStack(alignment: .top) {
|
|
||||||
|
|
||||||
let is_anon = event_is_anonymous(ev: event)
|
|
||||||
VStack {
|
|
||||||
Pfp(is_anon: is_anon)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
EventTop(state: state, event: event, pubkey: pubkey, is_anon: is_anon)
|
|
||||||
|
|
||||||
if !options.contains(.no_replying_to) {
|
|
||||||
ReplyPart(event: event, privkey: state.keypair.privkey, profiles: state.profiles)
|
|
||||||
}
|
|
||||||
|
|
||||||
content
|
|
||||||
|
|
||||||
if let mention = get_mention() {
|
|
||||||
Mention(mention)
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_action_bar {
|
|
||||||
ActionBar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding([.leading], 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var Wide: some View {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
let is_anon = event_is_anonymous(ev: event)
|
|
||||||
|
|
||||||
HStack(spacing: 10) {
|
|
||||||
Pfp(is_anon: is_anon)
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
EventTop(state: state, event: event, pubkey: pubkey, is_anon: is_anon)
|
|
||||||
ReplyPart(event: event, privkey: state.keypair.privkey, profiles: state.profiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
content
|
|
||||||
|
|
||||||
if !options.contains(.no_mentions), let mention = get_mention() {
|
|
||||||
Mention(mention)
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_action_bar {
|
|
||||||
ActionBar
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Group {
|
|
||||||
if options.contains(.wide) {
|
|
||||||
Wide
|
|
||||||
} else {
|
|
||||||
Threaded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.contentShape(Rectangle())
|
|
||||||
.id(event.id)
|
|
||||||
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
|
|
||||||
.padding([.bottom], 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EventShell_Previews: PreviewProvider {
|
|
||||||
|
|
||||||
static var previews: some View {
|
|
||||||
VStack {
|
|
||||||
EventShell(state: test_damus_state(), event: test_event, options: [.no_action_bar]) {
|
|
||||||
Text(verbatim: "Hello")
|
|
||||||
}
|
|
||||||
|
|
||||||
EventShell(state: test_damus_state(), event: test_event, options: [.no_action_bar, .wide]) {
|
|
||||||
Text(verbatim: "Hello")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(height: 300)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
//
|
|
||||||
// LongformPreview.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct LongformPreviewBody: View {
|
|
||||||
let state: DamusState
|
|
||||||
let event: LongformEvent
|
|
||||||
let options: EventViewOptions
|
|
||||||
@ObservedObject var artifacts: NoteArtifactsModel
|
|
||||||
|
|
||||||
init(state: DamusState, ev: LongformEvent, options: EventViewOptions) {
|
|
||||||
self.state = state
|
|
||||||
self.event = ev
|
|
||||||
self.options = options
|
|
||||||
|
|
||||||
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.event.id).artifacts_model)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(state: DamusState, ev: NostrEvent, options: EventViewOptions) {
|
|
||||||
self.state = state
|
|
||||||
self.event = LongformEvent.parse(from: ev)
|
|
||||||
self.options = options
|
|
||||||
|
|
||||||
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.id).artifacts_model)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Words(_ words: Int) -> Text {
|
|
||||||
let wordCount = pluralizedString(key: "word_count", count: words)
|
|
||||||
return Text(wordCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Group {
|
|
||||||
if options.contains(.wide) {
|
|
||||||
Main.padding(.horizontal)
|
|
||||||
} else {
|
|
||||||
Main
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var Main: some View {
|
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
|
||||||
if let title = event.title {
|
|
||||||
Text(title)
|
|
||||||
.font(.title)
|
|
||||||
} else {
|
|
||||||
Text("Untitled", comment: "Text indicating that the long-form note title is untitled.")
|
|
||||||
.font(.title)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(event.summary ?? "")
|
|
||||||
.foregroundColor(.gray)
|
|
||||||
|
|
||||||
if case .loaded(let arts) = artifacts.state,
|
|
||||||
case .parts(let parts) = arts
|
|
||||||
{
|
|
||||||
Words(parts.words).font(.footnote)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LongformPreview: View {
|
|
||||||
let state: DamusState
|
|
||||||
let event: LongformEvent
|
|
||||||
let options: EventViewOptions
|
|
||||||
|
|
||||||
init(state: DamusState, ev: NostrEvent, options: EventViewOptions) {
|
|
||||||
self.state = state
|
|
||||||
self.event = LongformEvent.parse(from: ev)
|
|
||||||
self.options = options.union(.no_mentions)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
EventShell(state: state, event: event.event, options: options) {
|
|
||||||
LongformPreviewBody(state: state, ev: event, options: options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LongformPreview_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
VStack {
|
|
||||||
LongformPreview(state: test_damus_state(), ev: test_longform_event.event, options: [])
|
|
||||||
|
|
||||||
LongformPreview(state: test_damus_state(), ev: test_longform_event.event, options: [.wide])
|
|
||||||
}
|
|
||||||
.frame(height: 400)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
//
|
|
||||||
// LongformEvent.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by William Casarin on 2023-06-01.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct LongformEvent {
|
|
||||||
let event: NostrEvent
|
|
||||||
|
|
||||||
var title: String? = nil
|
|
||||||
var image: URL? = nil
|
|
||||||
var summary: String? = nil
|
|
||||||
var published_at: Date? = nil
|
|
||||||
|
|
||||||
static func parse(from ev: NostrEvent) -> LongformEvent {
|
|
||||||
var longform = LongformEvent(event: ev)
|
|
||||||
|
|
||||||
for tag in ev.tags {
|
|
||||||
guard tag.count >= 2 else { continue }
|
|
||||||
switch tag[0] {
|
|
||||||
case "title": longform.title = tag[1]
|
|
||||||
case "image": longform.image = URL(string: tag[1])
|
|
||||||
case "summary": longform.summary = tag[1]
|
|
||||||
case "published_at":
|
|
||||||
longform.published_at = Double(tag[1]).map { d in Date(timeIntervalSince1970: d) }
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return longform
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LongformView: View {
|
|
||||||
let state: DamusState
|
|
||||||
let event: LongformEvent
|
|
||||||
@ObservedObject var artifacts: NoteArtifactsModel
|
|
||||||
|
|
||||||
init(state: DamusState, event: LongformEvent, artifacts: NoteArtifactsModel? = nil) {
|
|
||||||
self.state = state
|
|
||||||
self.event = event
|
|
||||||
self._artifacts = ObservedObject(wrappedValue: artifacts ?? state.events.get_cache_data(event.event.id).artifacts_model)
|
|
||||||
}
|
|
||||||
|
|
||||||
var options: EventViewOptions {
|
|
||||||
return [.wide, .no_mentions, .no_replying_to]
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
EventShell(state: state, event: event.event, options: options) {
|
|
||||||
SelectableText(attributedString: AttributedString(stringLiteral: event.title ?? "Untitled"), size: .title)
|
|
||||||
|
|
||||||
NoteContentView(damus_state: state, event: event.event, show_images: true, size: .selected, options: options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let test_longform_event = LongformEvent.parse(from:
|
|
||||||
.init(id: "longform_id",
|
|
||||||
content: "## Let me tell you why coffee is awesome\n**IT JUST IS**",
|
|
||||||
pubkey: "pk",
|
|
||||||
kind: NostrKind.longform.rawValue,
|
|
||||||
tags: [
|
|
||||||
["title", "Coffee is awesome"],
|
|
||||||
["summary", "Did you know coffee is awesome?"],
|
|
||||||
["published_at", "1685638715"],
|
|
||||||
["t", "coffee"],
|
|
||||||
["t", "coffeechain"],
|
|
||||||
["image", "https://cdn.jb55.com/s/038fe8f558153b52.jpg"],
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
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, privkey: nil)
|
|
||||||
|
|
||||||
let model = NoteArtifactsModel(state: .loaded(artifacts))
|
|
||||||
LongformView(state: st, event: test_longform_event, artifacts: model)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,8 +35,9 @@ struct SelectedEventView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
EventMenuContext(damus: damus, event: event)
|
EventMenuContext(event: event, keypair: damus.keypair, target_pubkey: event.pubkey, bookmarks: damus.bookmarks, muted_threads: damus.muted_threads)
|
||||||
.padding([.bottom], 4)
|
.padding([.bottom], 4)
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.minimumScaleFactor(0.75)
|
.minimumScaleFactor(0.75)
|
||||||
@@ -47,8 +48,8 @@ struct SelectedEventView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
EventBody(damus_state: damus, event: event, size: size, options: [.wide])
|
EventBody(damus_state: damus, event: event, size: size, options: [.pad_content])
|
||||||
|
|
||||||
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
|
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
|
||||||
BuilderEventView(damus: damus, event_id: mention.ref.id)
|
BuilderEventView(damus: damus, event_id: mention.ref.id)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
@@ -88,5 +89,6 @@ struct SelectedEventView: View {
|
|||||||
struct SelectedEventView_Previews: PreviewProvider {
|
struct SelectedEventView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
SelectedEventView(damus: test_damus_state(), event: test_event, size: .selected)
|
SelectedEventView(damus: test_damus_state(), event: test_event, size: .selected)
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,15 +14,25 @@ struct EventViewOptions: OptionSet {
|
|||||||
static let no_replying_to = EventViewOptions(rawValue: 1 << 1)
|
static let no_replying_to = EventViewOptions(rawValue: 1 << 1)
|
||||||
static let wide = EventViewOptions(rawValue: 1 << 3)
|
static let wide = EventViewOptions(rawValue: 1 << 3)
|
||||||
static let truncate_content = EventViewOptions(rawValue: 1 << 4)
|
static let truncate_content = EventViewOptions(rawValue: 1 << 4)
|
||||||
static let no_translate = EventViewOptions(rawValue: 1 << 5)
|
static let pad_content = EventViewOptions(rawValue: 1 << 5)
|
||||||
static let small_pfp = EventViewOptions(rawValue: 1 << 6)
|
static let no_translate = EventViewOptions(rawValue: 1 << 6)
|
||||||
static let nested = EventViewOptions(rawValue: 1 << 7)
|
static let small_pfp = EventViewOptions(rawValue: 1 << 7)
|
||||||
static let top_zap = EventViewOptions(rawValue: 1 << 8)
|
static let nested = EventViewOptions(rawValue: 1 << 8)
|
||||||
static let no_mentions = EventViewOptions(rawValue: 1 << 9)
|
static let top_zap = EventViewOptions(rawValue: 1 << 9)
|
||||||
|
|
||||||
static let embedded: EventViewOptions = [.no_action_bar, .small_pfp, .wide, .truncate_content, .nested]
|
static let embedded: EventViewOptions = [.no_action_bar, .small_pfp, .wide, .truncate_content, .nested]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RelativeTime: View {
|
||||||
|
@ObservedObject var time: RelativeTimeModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(verbatim: "\(time.value)")
|
||||||
|
.font(.system(size: 16))
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct TextEvent: View {
|
struct TextEvent: View {
|
||||||
let damus: DamusState
|
let damus: DamusState
|
||||||
let event: NostrEvent
|
let event: NostrEvent
|
||||||
@@ -38,12 +48,94 @@ struct TextEvent: View {
|
|||||||
self.evdata = damus.events.get_cache_data(event.id)
|
self.evdata = damus.events.get_cache_data(event.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var has_action_bar: Bool {
|
||||||
|
!options.contains(.no_action_bar)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
EventShell(state: damus, event: event, pubkey: pubkey, options: options) {
|
Group {
|
||||||
EvBody(options: options)
|
if options.contains(.wide) {
|
||||||
|
WideStyle
|
||||||
|
} else {
|
||||||
|
ThreadedStyle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.id(event.id)
|
||||||
|
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
|
||||||
|
.padding([.bottom], 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Pfp(is_anon: Bool) -> some View {
|
||||||
|
MaybeAnonPfpView(state: damus, is_anon: is_anon, pubkey: pubkey, size: options.contains(.small_pfp) ? eventview_pfp_size(.small) : PFP_SIZE )
|
||||||
|
}
|
||||||
|
|
||||||
|
func TopPart(is_anon: Bool) -> some View {
|
||||||
|
HStack(alignment: .center, spacing: 0) {
|
||||||
|
ProfileName(is_anon: is_anon)
|
||||||
|
TimeDot
|
||||||
|
RelativeTime(time: self.evdata.relative_time)
|
||||||
|
Spacer()
|
||||||
|
ContextButton
|
||||||
|
}
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ReplyPart: some View {
|
||||||
|
Group {
|
||||||
|
if event_is_reply(event, privkey: damus.keypair.privkey) {
|
||||||
|
ReplyDescription(event: event, profiles: damus.profiles)
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var WideStyle: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
let is_anon = event_is_anonymous(ev: event)
|
||||||
|
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
Pfp(is_anon: is_anon)
|
||||||
|
VStack {
|
||||||
|
TopPart(is_anon: is_anon)
|
||||||
|
ReplyPart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
|
EvBody(options: self.options.union(.pad_content))
|
||||||
|
|
||||||
|
if let mention = get_mention() {
|
||||||
|
Mention(mention)
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_action_bar {
|
||||||
|
//EmptyRect
|
||||||
|
ActionBar
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var TimeDot: some View {
|
||||||
|
Text(verbatim: "⋅")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ContextButton: some View {
|
||||||
|
EventMenuContext(event: event, keypair: damus.keypair, target_pubkey: event.pubkey, bookmarks: damus.bookmarks, muted_threads: damus.muted_threads)
|
||||||
|
.padding([.bottom], 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProfileName(is_anon: Bool) -> some View {
|
||||||
|
let profile = damus.profiles.lookup(id: pubkey)
|
||||||
|
let pk = is_anon ? ANON_PUBKEY : pubkey
|
||||||
|
return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
func EvBody(options: EventViewOptions) -> some View {
|
func EvBody(options: EventViewOptions) -> some View {
|
||||||
let show_imgs = should_show_images(settings: damus.settings, contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey)
|
let show_imgs = should_show_images(settings: damus.settings, contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey)
|
||||||
return NoteContentView(
|
return NoteContentView(
|
||||||
@@ -53,8 +145,61 @@ struct TextEvent: View {
|
|||||||
size: .normal,
|
size: .normal,
|
||||||
options: options
|
options: options
|
||||||
)
|
)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Mention(_ mention: Mention) -> some View {
|
||||||
|
return BuilderEventView(damus: damus, event_id: mention.ref.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ActionBar: some View {
|
||||||
|
return EventActionBar(damus_state: damus, event: event)
|
||||||
|
.padding([.top], 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
var EmptyRect: some View {
|
||||||
|
return Rectangle().frame(height: 2).opacity(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_mention() -> Mention? {
|
||||||
|
if self.options.contains(.nested) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return first_eref_mention(ev: event, privkey: damus.keypair.privkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ThreadedStyle: some View {
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
|
||||||
|
let is_anon = event_is_anonymous(ev: event)
|
||||||
|
VStack {
|
||||||
|
Pfp(is_anon: is_anon)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
TopPart(is_anon: is_anon)
|
||||||
|
|
||||||
|
if !options.contains(.no_replying_to) {
|
||||||
|
ReplyPart
|
||||||
|
}
|
||||||
|
|
||||||
|
EvBody(options: self.options)
|
||||||
|
|
||||||
|
if let mention = get_mention() {
|
||||||
|
Mention(mention)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_action_bar {
|
||||||
|
EmptyRect
|
||||||
|
ActionBar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding([.leading], 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func event_has_tag(ev: NostrEvent, tag: String) -> Bool {
|
func event_has_tag(ev: NostrEvent, tag: String) -> Bool {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user