Compare commits

...

718 Commits

Author SHA1 Message Date
b6c811dedf Fix localization issues and export strings for translation
Changelog-Fixed: Fix localization issues and export strings for translation
2023-09-24 14:02:31 -04:00
Daniel D’Aquino
6d055be3cd Fix UI freeze after swiping back from profile (#1449)
On iOS 17.0, swiping back from a view that uses
`.navigationBarHidden(true)` caused the `NavigationStack` view to
freeze. This fixes the issue by creating a custom toolbar using
`.toolbar` instead.

Issue reproduction steps
------------------------

I found a very good clue to reproduce this issue from
[https://damus.io/note162rt4fctxepnj9cdtr9a82k7jtw3e33hj742ejly3q84tkwsfars4k9glr](https://damus.io/note162rt4fctxepnj9cdtr9a82k7jtw3e33hj742ejly3q84tkwsfars4k9glr)

**Device:** iPhone 13 mini (Physical device)
**iOS:** 17.0.1
**Damus:** 1.6 (18) 4377cf28ef
**Steps:**
1. Go to the home timeline view.
2. Click on a profile on any post
3. Swipe back to the home timeline view (Do not press "back" button)
4. Click on that same profile again

**Ideal behaviour:** On step 4, you should be taken to the profile view
**Actual behaviour:** The whole timeline view, top bar, etc seems to "freeze" and no longer respond.

Root causing investigation
--------------------------

I attempted various things until I could narrow it down. Here is a
summary of what I discovered:
1. First I attempted to investigate where the deadlock would live, by
   analyzing the thread states in the debugger. However:
    1. I did not find much differences between the thread states of a
       normal app running and the app running after the issue
    2. **I noticed that the tab bar at the bottom was still working, so
       unless those views are running on different threads, it might not
       have been a deadlock**
    3. NostrDB ingested and writer threads seemed to be waiting on a
       mutex most of the time I paused execution, but that also happened
       under normal conditions
    4. The crux of what made this difficult is that most of the UI
       related threads were in assembly, which was harder to interpret.
       However, the top of the stack in those threads were usually
       `mach_msg_trap`, which I believe is just the debugger
       interrupting execution. Below it, there were usually normal
       assembly instructions being run, such as `mov` and `ldp`
       instructions _(Move value and load a pair of registers)_, and
       stepping through some of those seemed to move the program
       counter. So I believe that the threads are running
    5. Running `thread info` on some of the threads (e.g. main) revealed
       that it seemed to be waiting on `mach_msg2_trap`, which again I
       believe is just the debugger pausing execution.
2. **After some more testing, I realized that swiping back only breaks
   when swiping back from the `ProfileView`, but not other views**
2. I tried to check if the issue was incorrect hashing of `Router`
   objects: `NavigationStack` uses `NavigationPath`, which needs a
   collection of hashable elements. I thought that if hashing was done
   incorrectly, the NavigationStack might have issues managing views.
   But that did not seem to be it either.
    1. I tried experimenting with the hashing logic for the Profile
       router. No changes
    2. I tried purposefully messing up with the hashing logic of a good,
       working view by adding random numbers into the hash. No issues on
       swiping out of that view either.
3. That lead me to the possibility that the issue is within the
   `ProfileView` body. I commented parts of the code out and tested each
   portion of it in a binary search fashion, and narrowed it down a
   specific line:

```
.navigationBarHidden(true)
```

Whenever I remove this line or set this to `false`, the freezing no
longer occurs. According to the Apple developer docs, this is
deprecated:
[https://developer.apple.com/documentation/swiftui/view/navigationbarhidden(_:)](https://developer.apple.com/documentation/swiftui/view/navigationbarhidden(_:))

I tried to replace it with its newer replacement: `.toolbar(.hidden,
for: .automatic)`, but that also causes the freeze.

So, just removing that line fixes the freeze, however it breaks the
layout by showing the unwanted back button.

Fix
---

I was able to fix it by implementing the custom toolbar under `.toolbar`
modifier, and hiding the back button (as opposed to the whole nav bar)

Testing of the fix
------------------

**PASS**

**Device:** iPhone 13 mini (Physical device)
**iOS:** iOS 17.0.1
**Damus:** This commit

**Special remarks:** Some entitlements removed locally to be able to
build to device without access to development certificate

**Test steps:** Same as reproduction steps

**Results:** Swiping back from profile does not cause any issues. View
layout of the custom navbar is unaltered in appearance.

iOS 16 smoke test
-----------------

**PASS**

**Device:** iPhone 14 Pro simulator
**iOS:** 16.4
**Damus:** This commit
**Special remarks:** Same as test above

**Test steps:** Same as reproduction steps. However here we are not
checking the freezing (as it was not reproducible in the simulator). We
are checking that the changes did not break navigation, nor layout, nor
caused any build issues.

**Results:** Working as expected

Closes: https://github.com/damus-io/damus/issues/1449
Changelog-Fixed: Fix UI freeze after swiping back from profile (#1449)
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-23 08:03:32 -07:00
William Casarin
01c6e3e9ab v1.6 (18) changelog 2023-09-21 18:08:56 -04:00
William Casarin
4377cf28ef v1.6 (18) 2023-09-21 18:05:59 -04:00
William Casarin
6f67c159ff ndb: switch to case-insensitive profile searches 2023-09-21 18:03:22 -04:00
William Casarin
7a85ae29ca search: switch to nostrdb profile searching
Changelog-Changed: Switch to nostrdb for @'s and user search
2023-09-21 13:19:22 -04:00
William Casarin
fafe3b4b3e ndb: add nostrdb migrations 2023-09-21 09:10:06 -04:00
William Casarin
69c7acea76 tests: add ndb support to tests
stops it from crashing
2023-09-21 09:10:06 -04:00
William Casarin
22d635d850 ndb: don't verify flatbuffers in release builds 2023-09-21 09:10:06 -04:00
William Casarin
fc9b9f2940 ndb: switch profile queries to use transactions
this should ensure no crashing occurs when querying profiles
2023-09-21 09:10:06 -04:00
William Casarin
622a436589 ndb: add NdbTxn transaction class
This will be used for transactions
2023-09-21 09:10:06 -04:00
William Casarin
9398877415 nostrdb/c: update to include transaction support 2023-09-21 09:10:06 -04:00
William Casarin
129d3ff101 ids: introduce NoteKey
These will be used to reference nostr notes from nostrdb
2023-09-21 09:10:06 -04:00
William Casarin
bb4fd75576 nostrdb: add profiles to nostrdb
This adds profiles to nostrdb

- Remove in-memory Profiles caches, nostrdb is as fast as an in-memory cache
- Remove ProfileDatabase and just use nostrdb directly

Changelog-Changed: Use nostrdb for profiles
2023-09-21 09:10:06 -04:00
Daniel D’Aquino
8586eed635 ui: add followed hashtags to FollowingView
When users view who a certain person follows, now they will see an extra
tab to see the hashtags that they follow.

This new tab contains a list of followed hashtags, each of which
includes an option to follow/unfollow the hashtag, as well as the
ability to visit the hashtag timeline

Testing

**iOS:** 17.0 (iPhone 14 Pro Simulator)
**Damus:** (This commit)
**Test steps:**
1. Go to search view, search for a couple of hashtags: #apple, #orange, #kiwi
2. Go to the test accounts own profile via the drawer menu
3. Click on "Following". Make sure there are two tabs now.
4. Scroll down, switch tabs between "People" and "Hashtags". Make sure that scrolling and switching tabs work
5. Unfollow and follow a user. Make sure that this still works
6. Make sure that #apple, #orange, #kiwi hashtags are visible under the "Hashtags" tab
7. Unfollow "#kiwi". Check that the button label now switches from "Unfollow" to "Follow"
8. Click on "#kiwi". Make sure that it takes you to the page where posts with that hashtag appears
9. Go to @jb55's profile
10. Click on "Following"
11. Ensure that there is a "Hashtags" tab
12. Check that @jb55's followed hashtags are shown (not your own)
13. Follow one of the same hashtags as @jb55's
14. Go back to your own profile and go to your own following view again.
15. Make sure that this newly added tag is present on the list, and that #kiwi is not.

Closes: https://github.com/damus-io/damus/issues/606
Changelog-Added: Add followed hashtags to your following list
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-21 09:10:06 -04:00
William Casarin
440e37c1d3 filters: generalize ContentFilter
This simplifies our content filters so that it is a bit more flexible
for future additions.

Fixes: 0957cc896cc8 ("Add "Do not show #nsfw tagged posts" setting")
2023-09-21 09:10:06 -04:00
Daniel D’Aquino
49283f2bb2 filters: add "Do not show #nsfw tagged posts" setting
This commit adds a setting where the user can choose to hide notes with
a #nsfw hashtag. This setting was implemented to allow users to filter
out adult or other unsafe content.

I moved the code logic for content filtering into a new file, and
defined a protocol for content filters. Although the logic is still
simple, this might help in developing a flexible API in case we have
more complex filtering needs in the future.

I also modified the name of the "Appearance" setting to "Appearance and
filters", to make it easier for users to intuitively find this setting.
(Note: Re-translations of this string might be necessary)

**PASS**
**iOS:**
- iOS 17.0 (iPhone 14 Pro)

**Damus:** (This commit)
**Steps:**
1. Follow another account that you control (Account B)
2. On account B, post a note saying "#test this is a test". This note should show up on the home feed.
3. On account B, post a note saying "#nsfw this is a test". This note should NOT show up on the home feed
4. Go to settings and disable the NSFW filter. Go back to the home view. The #nsfw post should now show up.
5. Close app and reopen. NSFW post should still show up (i.e. Setting should be persistent)
6. Unfollow account B
7. Close app and reopen.
8. Follow the "#grownostr" hashtag
9. Turn on the NSFW filter
10. On account B, post a note saying "#grownostr this is a test". This note should show up on the home view.
11. On account B, post a note saying "#grownostr #nsfw this is a test". This note should NOT show up.
12. Double-check the "notes and replies" tab. Note should NOT show up there either.
12. Turn off NSFW filter
13. Note from step 11 should now show up.
14. Go to Universe view and find a post with a hashtag. Remember where the post is.
14. Locally change the tag keyword from "nsfw" to that hashtag (Note: I had to test this way because my posts were not showing up in the Universe view)
15. Turn off the filter. Check post is there, in the Universe view.
16. Turn on the filter. Check post is no longer there in the Universe view. (Check the neighboring posts are the same, to make sure)
17. Bring back the code to its normal state.
18. Search for "#nsfw". Make sure that #nsfw appears (I believe this is ok, because it means the person is purposefully searching for it)

Closes: https://github.com/damus-io/damus/issues/1412
Changelog-Added: Add "Do not show #nsfw tagged posts" setting
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-21 09:10:06 -04:00
William Casarin
305ee03b0e relays: fix tld extraction performance issues
This uses a simpler variant that doesn't require a library. It is also
much faster and doesn't cause a delay when you open the relay view.

Not sure why I needed to touch other parts of the code to make the build
work. Probably xcode beta thing?
2023-09-21 09:10:06 -04:00
William Casarin
a88f5db10b Revert "deps: add tldextract"
This reverts commit 4263b9690f.
2023-09-21 08:48:20 -04:00
Suhail Saqan
d39a3da3b7 util: add separate_images and separate_invoices 2023-09-21 08:37:42 -04:00
ericholguin
40459e247e relays: user relay design 2023-09-16 14:15:27 -05:00
ericholguin
fff4549933 relays: remove usage of show action button binding 2023-09-16 14:15:27 -05:00
ericholguin
c4dfae9ede relays: update relay view to use new design
Changelog-Changed: Updated relay view
Closes: https://github.com/damus-io/damus/pull/1543
2023-09-16 14:15:27 -05:00
Daniel D’Aquino
bfda0d1b74 ui: increase size of the hitbox on note ellipsis button
Changelog-Changed: Increase size of the hitbox on note ellipsis button
Closes: https://github.com/damus-io/damus/issues/1454
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-16 14:15:20 -05:00
Daniel D’Aquino
01b8e43a6e compose: fix text wrapping issue when mentioning npub
Closes: https://github.com/damus-io/damus/issues/1211
Changelog-Fixed: Fix text composer wrapping issue when mentioning npub
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-16 13:58:27 -05:00
Jon Marrs
aa4ecc2139 test: add test cases for ASCII and UTF-8 characters in hashtags
Closes: https://github.com/damus-io/damus/pull/1546
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-15 12:31:17 -05:00
Jon Marrs
617dee3e6b damus-c: remove UTF-8 punctuation from hashtags
Check for UTF-8 punctuation (such as ellipsis) in addition to regular punctuation in hashtags.

Closes: https://github.com/damus-io/damus/issues/1518

Closes: https://github.com/damus-io/damus/pull/1546
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-15 12:31:17 -05:00
Daniel D’Aquino
510432bb98 ui: make blurred videos viewable by allowing blur to disappear once tapped
Closes: https://github.com/damus-io/damus/issues/1247
Changelog-Fixed: Make blurred videos viewable by allowing blur to disappear once tapped
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-15 12:31:05 -05:00
Jericho Hasselbush
c4a9f2fdb2 ui: hold tap to preview status URL
Applied a WKWebkitView inside a .contextMenu to show preview status for
URL links in user status messages.

Closes: https://github.com/damus-io/damus/issues/1523
Changelog-Added: Hold tap to preview status URL
Signed-off-by: Jericho Hasselbush <jericho@sal-et-lucem.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-15 12:30:26 -05:00
Daniel D’Aquino
b1e0a62109 nwc: fix parsing issue with NIP-47 compliant NWC urls without double-slashes
Closes: https://github.com/damus-io/damus/issues/1547
Changelog-Fixed: Fix parsing issue with NIP-47 compliant NWC urls without double-slashes
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-13 12:59:21 -06:00
William Casarin
1fc5ceff3b relays: fix withAnimation on older versions
Maybe this is an iOS17 thing?
2023-09-13 05:41:08 -07:00
William Casarin
16edc3fe13 relays: bouncy edit animation 2023-09-11 14:39:44 -07:00
William Casarin
6a88ca2777 relays: fix crash in new RelayPicView 2023-09-11 14:36:08 -07:00
William Casarin
e3ccf95780 ui: fix padding of username next to pfp on some views
Changelog-Fixed: Fix padding of username next to pfp on some views
2023-09-11 14:36:08 -07:00
Bryan Montz
9bac83352b ui: improve bottom spacing for ImageView's tab indicator dots
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-11 14:36:08 -07:00
Bryan Montz
0803594553 ui: make ImageView's tab indicator dots tappable
Changelog-Changed: Make carousel tab dots tappable
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-11 14:35:50 -07:00
Jericho Hasselbush
8dad8e6703 posting: fix issue with username and multiple emojis
Fixes issue where username with multiple emojis would place cursor in
strange position. Now properly moves the cursor to space past the
multiple emoji user name.

Any amount would be great. Not a complex issue to fix!

Tipjar: lnbc1pj0eddtpp5km07jgrfm47nfswqqp33ngv374gzad2hshkra7zm3l0cmpusnp3qdqqcqzzsxqyz5vqsp5rklkzj9upf32z3c3nmc9xg4pdlz5p5mp3s332ygefexf79tq8ucs9qyyssqxfh4kz3sg9zczsnj49w23aw35z87jwyx9m5su8kkyxlspyjk4ajy7vhxuw2rzw4lz8vfutfakm2rggvpzhzs9ehfus4nl683dl99f4sqgm9zkq
Changelog-Fixed: Fixes issue where username with multiple emojis would place cursor in strange position.
Signed-off-by: Jericho Hasselbush <jericho@sal-et-lucem.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-11 07:48:36 -07:00
William Casarin
e30d38e69f router: use tap gestures instead of nav links
I was hoping this would fix something but it did not
2023-09-10 18:27:37 -07:00
William Casarin
c13f29e98c router: hash bytes for a quick sanity check
probably a no-op
2023-09-10 18:27:37 -07:00
William Casarin
5b901656f3 perf: fix weird lag when switching timelines 2023-09-10 18:25:35 -07:00
William Casarin
36acdf420e perf: remove zstack on profile pictures
helps a bit? I think?
2023-09-10 18:25:35 -07:00
William Casarin
76a6dbc406 perf: remove unused zstack on like button 2023-09-10 18:25:35 -07:00
William Casarin
1b1d4bd6d1 perf: use plain images for actionbar buttons
The action bar is really slow to render for some reason, start
removing stuff
2023-09-10 18:25:35 -07:00
William Casarin
14586b616c log: remove some verbose preload logs 2023-09-10 18:25:35 -07:00
ericholguin
7baf7e66dc relays: add relay pic view for displaying relay icons 2023-09-10 09:54:35 -07:00
William Casarin
4263b9690f deps: add tldextract
This is needed for the new relay view
2023-09-10 09:52:54 -07:00
Suhail Saqan
7f6a702412 emojis: make width dynamic and font bigger
add calculateOverlayWidth to support this

Closes: https://github.com/damus-io/damus/pull/1542
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-09 10:24:14 -07:00
ericholguin
6f35de65f9 relays: add icon field to metadata 2023-09-09 09:48:18 -07:00
ericholguin
65f3651896 ui: dont display globe image for free relay types 2023-09-09 09:45:45 -07:00
ericholguin
94ce604b9d components: add neutral button style component 2023-09-09 09:45:16 -07:00
ericholguin
b934d66f64 components: add lighter gradient 2023-09-09 09:45:16 -07:00
ericholguin
20b6627799 colors: add variables for the new color assets 2023-09-09 09:45:16 -07:00
ericholguin
3e15f15a57 colors: add color sets from figma 2023-09-09 09:45:15 -07:00
petrikaj
5c87b8e610 transations: add finnish translation
Changelog-Added: Finnish translations
Closes: https://github.com/damus-io/damus/pull/1535
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:37 -07:00
William Casarin
42234b1cf3 remove timeline render logs 2023-09-07 10:33:37 -07:00
Bryan Montz
54ba64535d video: remove GSPlayer dependency
Changelog-Fixed: Fixed audio in video playing twice
Closes: https://github.com/damus-io/damus/pull/1539
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:34 -07:00
Bryan Montz
9cf53a9e93 video: remove VideoPlayer and switch to VideoController for cache
Closes: https://github.com/damus-io/damus/pull/1539
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:31 -07:00
Bryan Montz
3569da5687 video: switch player to use new view model
pass VideoController through containing views

Closes: https://github.com/damus-io/damus/pull/1539
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:19 -07:00
Bryan Montz
f1f3abfb98 video: add DamusVideoPlayerViewModel
Closes: https://github.com/damus-io/damus/pull/1539
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:14 -07:00
Bryan Montz
dec07df2c1 video: add VideoController, which hold cached metadata and mute states
Closes: https://github.com/damus-io/damus/pull/1539
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:12 -07:00
Bryan Montz
53734ea483 video: add AVPlayerView, a simple wrapper for AVPlayerViewController
Closes: https://github.com/damus-io/damus/pull/1539
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-07 10:33:07 -07:00
Grimless
b18a0c573e profile: move the "Follow you" badge into the profile header
Move the "Follow you" badge into the profile header he profile header
out-of-line with the often long and already space-constrained
username/display name text

Changelog-Changed: Move the "Follow you" badge into the profile header
Closes: https://github.com/damus-io/damus/pull/1529
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-03 18:02:54 -07:00
Grimless
f6f7d13f12 Properly implement top-level tests and fix one test using the wrong Block conversion property
Closes: https://github.com/damus-io/damus/pull/1528
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-03 18:02:32 -07:00
Grimless
6ee0be40e9 Create helper extensions for Block and update tests for the Block helper model
Closes: https://github.com/damus-io/damus/pull/1528
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-03 18:02:32 -07:00
Grimless
a64f898df7 Move the Block helper type to its own file, collapse the various standalone functions for parsing block data, and refactor consumers to initialize a Block with given data and access its members as needed.
Closes: https://github.com/damus-io/damus/pull/1528
Signed-off-by: William Casarin <jb55@jb55.com>
2023-09-03 18:02:32 -07:00
Jon Marrs
dd29e87146 test: pass keypair instead of privkey for test cases
Tests were not building due to recent changes in the Damus source code that replaced privkey with keypair. This patch extends those changes to the test cases, allowing the tests to build and pass.

Signed-off-by: Jon Marrs <jdmarrs@gmail.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-31 08:52:28 -07:00
William Casarin
c71b0ee916 blocks: pass keypair instead of privkey to avoid pubkey gen
Generating a pubkey is quite slow, so pass a keypair instead of privkey
2023-08-28 11:47:29 -07:00
William Casarin
8e92e28faf test: optionally remove lmdb db
otherwise tests fail on CI
2023-08-28 10:34:37 -07:00
William Casarin
5657512370 ndb: restore escaped slash fix 2023-08-28 10:09:49 -07:00
William Casarin
882f6e2534 ndb: update nostrdb, fix alignment issues 2023-08-28 08:19:03 -07:00
William Casarin
2f60888fb1 ndb: remove patch from copy script, just use sed 2023-08-28 08:18:25 -07:00
William Casarin
ba6792640d flatbuffers: update bindings, add verifier 2023-08-28 08:17:25 -07:00
William Casarin
984c7b6932 ndb: ensure profile flatbuffers are not copied
These are pointers into LMDB's virtual memory map of the database. No
copy required.
2023-08-28 08:00:45 -07:00
William Casarin
0bbc2c6348 ndb: save in documents instead of cache dir
This is more long term storage
2023-08-28 08:00:45 -07:00
William Casarin
c44c0d0863 profile: remove deleted flag
it's not used anymore
2023-08-28 08:00:45 -07:00
William Casarin
50d55572be Fix crash when long pressing custom reactions
Changelog-Fixed: Fix crash when long pressing custom reactions
2023-08-28 08:00:45 -07:00
William Casarin
caffa0398b nostrdb: profile flatbuffers in nostrdb working! 2023-08-26 20:46:42 -07:00
William Casarin
92bbc9766d project: disable compile warnings for lmdb and nostrdb 2023-08-26 20:46:42 -07:00
William Casarin
699f77d9e1 add extended virtual memory entitlement
This will allow larger nostrdb databases
2023-08-26 20:46:42 -07:00
William Casarin
4c0166bd31 add swift flatbuffers 2023-08-26 20:46:42 -07:00
William Casarin
35b67dc08d nostrdb: initial Ndb class 2023-08-26 17:11:41 -07:00
William Casarin
1f5f1e28a4 nostrdb: pull latest, adding flatcc and lmdb 2023-08-25 19:05:34 -07:00
William Casarin
f30f93f65c Revert "Move the Block helper type to its own file"
This fixes the broken tests

This reverts commit 286ae68fd6.
2023-08-25 19:05:34 -07:00
William Casarin
7255481705 v1.6 (17) changelog 2023-08-23 17:49:30 -07:00
William Casarin
16fa701509 v1.6 (17) 2023-08-23 17:48:32 -07:00
William Casarin
2c6999e15c status: support clickable status urls
Changelog-Added: Add support for status URLs
2023-08-23 17:46:31 -07:00
William Casarin
981d500c25 status: click music urls to display in spotify
Changelog-Added: Click music statuses to display in spotify
2023-08-23 17:17:53 -07:00
William Casarin
d02fc9142d status: add settings for disabling statuses in the UI
Suggested-by: Tanel
Changelog-Added: Add settings for disabling user statuses
2023-08-23 16:43:55 -07:00
William Casarin
db59f74970 status: add missing status to some thread event views 2023-08-23 16:31:10 -07:00
William Casarin
bf3ca4a186 status: truncate statuses to a single line
Changelog-Fixed: Fix long status lines
2023-08-23 16:23:18 -07:00
William Casarin
53c2b3a48d status: clear statuses if they only contain whitespace
Changelog-Changed: clear statuses if they only contain whitespace
2023-08-23 16:19:19 -07:00
William Casarin
23a8d6fb6b status: fix status events not expiring locally
Changelog-Fixed: Fix status events not expiring locally
2023-08-23 16:11:48 -07:00
William Casarin
042b7da315 status: ignore processing expired events 2023-08-23 15:56:41 -07:00
William Casarin
e62ba5826b v1.6-16 changelog 2023-08-23 13:31:41 -07:00
William Casarin
1d11bb40b5 v1.6 (16) 2023-08-23 13:30:38 -07:00
William Casarin
0338297bfe Live Music & Generic Statuses
Changelog-Added: Added live music statuses
Changelog-Added: Added generic user statuses
2023-08-23 13:26:55 -07:00
William Casarin
59cf8056bd sidemenu: split out profile section
We will be adding to this and it is getting messy
2023-08-23 09:52:50 -07:00
William Casarin
d34d417fcc home: collapse guard statement
small nit refactor
2023-08-23 09:27:09 -07:00
William Casarin
b665a40a11 fix build 2023-08-23 09:25:47 -07:00
gladiusKatana
5caa4a6e97 videos: improve precision & sensitivity of auto-pause mechanism
Closes: https://github.com/damus-io/damus/pull/1308
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-23 09:24:13 -07:00
Grimless
c5d8e4a4a1 Simplify and inline Report event logic.
Closes: https://github.com/damus-io/damus/pull/1498
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-23 09:13:38 -07:00
tappu75e@duck.com
8b600a9774 Avoid notification for zap from mute profiles
Changelog-Fixed: Avoid notification for zaps from muted profiles
Closes: https://github.com/damus-io/damus/pull/1494
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-23 09:10:22 -07:00
Grimless
286ae68fd6 Move the Block helper type to its own file
Collapse the various standalone functions for parsing block data, and
refactor consumers to initialize a Block with given data and access its
members as needed.

Closes: https://github.com/damus-io/damus/pull/1496
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-21 17:11:43 -07:00
William Casarin
6ab893a617 profile: remove redundant view builder 2023-08-21 13:26:33 -07:00
William Casarin
9bfb59c4cc docs: people like centralized tools
This was confusing people, make it clear that github PRs are fine
2023-08-21 13:26:33 -07:00
Daniel D’Aquino
dcb94635ea Fix text editing issues on characters added right after mention link
Changelog-Fixed: Fix text editing issues on characters added right after mention link
Closes: https://github.com/damus-io/damus/issues/1375
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Tested-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-20 17:25:06 -07:00
Fishcake
c464a26151 use nostr.build api v2 with optional nip98 support
Closes: https://github.com/damus-io/damus/pull/1471
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-20 16:29:33 -07:00
Fishcake
9104ddb051 add function to create nip98 http authorization header
Closes: https://github.com/damus-io/damus/pull/1471
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-20 16:29:33 -07:00
Fishcake
1432087edf add nostr event 27235 (nip-98)
Closes: https://github.com/damus-io/damus/pull/1471
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-20 16:29:33 -07:00
William Casarin
ae2f7255a7 Mute hellthreads everywhere
Changelog-Fixed: Mute hellthreads everywhere
Fixes: https://damus.io/note1rn3ckl76myga6xcefr0le52d8czd0wqe8apguewqknyv7m55mmpq3rv3hv
2023-08-20 11:45:25 -07:00
William Casarin
d5b944170f actually build 15 because reasons 2023-08-20 11:25:01 -07:00
William Casarin
9fb1cc5b57 v1.16 (13) changelog 2023-08-18 11:20:51 -07:00
William Casarin
2e512317e7 v1.6 (13) 2023-08-18 10:10:18 -07:00
tappu75e@duck.com
f9eb669132 replies: fix bug where it would sometimes show -1
Changelog-Fixed: Fix bug where it would sometimes show -1 in replies
Closes: https://github.com/damus-io/damus/pull/1476
2023-08-18 08:41:21 -07:00
Daniel D‘Aquino
066b3cdde8 Fix image links appearing with escaped slashes
Changelog-Fixed: Fix images and links occasionally appearing with escaped slashes
Closes: https://github.com/damus-io/damus/issues/1468
Signed-off-by: Daniel D‘Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
Rewarded-sats: 50000
2023-08-18 08:41:21 -07:00
William Casarin
7f313dcbd4 nostrscript: add comment about iOS virtual memory allocs
I'm really just doing this because I forgot a changelog entry

Changelog-Fixed: Fixed nostrscript not working on smaller phones
2023-08-18 08:41:21 -07:00
William Casarin
1dabd88355 nostrscript: reduce size of wasm page allocation
smaller phones don't like this
2023-08-11 07:47:15 -07:00
Suhail Saqan
4f33641244 change button scale effect
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-11 07:08:44 -07:00
William Casarin
006a6ef16f Show possibly invalid zaps if we don't have the event in cache
Changelog-Fixed: Fix zaps sometimes not appearing
2023-08-10 10:32:57 -07:00
William Casarin
7467a9d5b1 Fix empty lines in profile and reposting-the-wrong-thing bugs
Changelog-Fixed: Fixed issue where reposts would sometimes repost the wrong thing
Changelog-Fixed: Fixed issues where sometimes there would be empty entries on your profile
2023-08-09 09:16:29 -07:00
William Casarin
9f01cab2be simplify reduce_text_block 2023-08-08 17:09:45 -07:00
William Casarin
502917012c v1.6 (11) changelog 2023-08-07 08:46:23 -07:00
William Casarin
916f7d789e v1.6 (11) 2023-08-07 08:45:07 -07:00
William Casarin
21eda288c4 timeline: show renotes in Notes timelines
Changelog-Changed: Show renotes in Notes timeline
Fixes: https://github.com/damus-io/damus/issues/676
2023-08-07 08:24:02 -07:00
William Casarin
25e022d933 reply: ensure the person you're replying to is the first entry in the reply description
Suggested-by: Tanel
Changelog-Fixed: Ensure the person you're replying to is the first entry in the reply description
2023-08-06 15:37:32 -07:00
William Casarin
e642913944 notifications: don't cutoff text
Changelog-Fixed: don't cutoff text in notifications
2023-08-06 14:58:32 -07:00
cr0bar
967785392f note: fix paragraphs not appearing on iOS17
In some edge cases, the inflated UiTextView didn't render properly
causing a black screen which needed the user to scroll. Dropped the
inflate size and now only set where selectedTextHeight is .zero, seems
more reliable.

Closes: https://github.com/damus-io/damus/pull/1427
Changelog-Fixed: Fix paragraphs not appearing on iOS17
2023-08-06 14:23:11 -07:00
William Casarin
9e6fbeefcd url: smartparens hack
support urls like (https://jb55.com/something)
2023-08-06 14:16:43 -07:00
William Casarin
de58e52199 dms: move timestamp outside of bubble 2023-08-06 14:07:04 -07:00
William Casarin
53e9269da6 urls: fix wikipedia url detection with parenthesis
Fixes: f0df4aa218 ("Strip common punctuations from URLs")
Fixes: https://github.com/damus-io/damus/issues/1027
Closes: https://github.com/damus-io/damus/pull/1063
Changelog-Fixed: Fix wikipedia url detection with parenthesis
2023-08-06 13:53:28 -07:00
Joel Klabo
85930df8e3 tests: add url parens tests 2023-08-06 13:51:39 -07:00
William Casarin
cf3a9a576d test: move existing url tests to UrlTests 2023-08-06 13:50:20 -07:00
William Casarin
e397fc069b make: add tags target 2023-08-06 13:50:20 -07:00
William Casarin
2529797dfb todo: add local todo helper 2023-08-06 13:50:20 -07:00
William Casarin
bd2193251f build: fix some build issues with the last revert
Fixes: 1a2ac976a3 ("Fix old notifications always appearing on first start")
2023-08-06 11:30:28 -07:00
William Casarin
1a2ac976a3 Fix old notifications always appearing on first start
Revert "home: debounce last notified"

This is technically incorrect, as debouncing can prevent saving
important events.

The proper way to do this is to save it locally in memory, and then
debouncing the saving itself. Will do this soon.

Reverts: a9b4cfd424
Fixes: https://github.com/damus-io/damus/issues/1439
Changelog-Fixed: Fixed old notifications always appearing on first start
2023-08-06 09:22:28 -07:00
William Casarin
d4faacb99f relays: strip trailing / from relay urls
Fixes: https://github.com/damus-io/damus/issues/1443
Changelog-Fixed: Fix issue with slashes on relay urls causing relay connection problems
2023-08-06 09:07:33 -07:00
William Casarin
a73271e3d4 debug: remove note size debug
ThreadSanitizer was complaining about a data race
2023-08-06 09:07:33 -07:00
William Casarin
624a7b4e88 notifications: fix rare crash with local notification
This shouldn't happen, but I found a log that crashed here, so we will
fix this anyways.

Changelog-Fixed: Fix rare crash triggered by local notifications
2023-08-06 08:33:51 -07:00
William Casarin
5b9803d234 script: add build-git-hash.txt build output
Otherwise we get warnings
2023-08-06 07:54:23 -07:00
William Casarin
3098d4b4fa bar: fix crash when long pressing emoji selection
Changelog-Fixed: Fix crash when long-pressing reactions
2023-08-06 07:10:01 -07:00
William Casarin
0178478199 decoding: fix decoding of large events like nostr reports
I was trying to do an initial malloc that was somewhat efficient. Looks
like our ndb_builder needs a bit more space when allocating the
ndb_note.

Changelog-Fixed: Fixed nostr reporting decoding
2023-08-06 06:56:24 -07:00
William Casarin
d489bcc586 test: add test for failing nostr report event 2023-08-06 06:56:24 -07:00
William Casarin
453d540255 search: find_event_with_subid
I needed this to find a bug in event decoding
2023-08-06 06:56:24 -07:00
Suhail Saqan
5ded564bdc settings: change settings order: Reactions -> Developer
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-05 18:48:44 -07:00
Suhail Saqan
3908192fe2 reactions: add close button to custom reactions
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Added: Add close button to custom reactions
2023-08-05 18:48:35 -07:00
Suhail Saqan
92020e551b reactions: add ability to change order of emojis
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Added: Add ability to change order of custom reactions
2023-08-05 18:48:30 -07:00
Suhail Saqan
ccd52a09d8 reactions: remove some left padding from add and remove buttons
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-05 18:48:24 -07:00
Suhail Saqan
ced3c76996 reactions: only allow copy emoji when editing
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-05 18:48:16 -07:00
Suhail Saqan
29bba15230 qr: dismiss qrcode fullScreenCover on scan
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Fixed: Dismiss qr screen on scan
2023-08-05 18:48:11 -07:00
Suhail Saqan
fb179ac1d4 qr: show QRCameraView regardless of same user
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Fixed: Show QRCameraView regardless of same user
2023-08-05 18:48:11 -07:00
Suhail Saqan
7900865c02 bar: wiggle long press reactions
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Fixed: Fix wiggle when long press reactions
2023-08-05 18:48:01 -07:00
Suhail Saqan
0350809e82 bar: fix reaction button breaking scrolling
Signed-off-by: William Casarin <jb55@jb55.com>
Changelog-Fixed: Fix reaction button breaking scrolling
2023-08-05 18:44:54 -07:00
Bryan Montz
cddb88b890 fix: crash when muting threads
Fixes a crash when the user mutes a thread. UserDefaults didn't know how
to serialize a NoteId for storage, so we'll convert it to the hex id
first.

Changelog-Fixed: Crash when muting threads
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-04 09:35:35 -07:00
William Casarin
14ba33674b setting: adjustable font size for jack the zapper
Changelog-Added: Adjustable font size
2023-08-03 18:38:20 -07:00
William Casarin
c0f4e3fe03 v1.6 (9) 2023-08-03 17:25:52 -07:00
William Casarin
dae2e8ef56 Revert "Fix for missing bottom half of a note"
This reverts commit 39dce64131.
2023-08-03 17:23:53 -07:00
William Casarin
b2d2fbee0d v1.6-8 changelog 2023-08-03 13:36:24 -07:00
William Casarin
cebd1f48ca ndb: switch to nostrdb notes
This is a refactor of the codebase to use a more memory-efficient
representation of notes. It should also be much faster at decoding since
we're using a custom C json parser now.

Changelog-Changed: Improved memory usage and performance when processing events
2023-08-03 13:20:36 -07:00
William Casarin
55bbe8f855 disable nostrscript test for now 2023-08-03 13:15:32 -07:00
cr0bar
39dce64131 Fix for missing bottom half of a note
Strange fix, but by increasing the height of a UiTextView past the size
of any legitimate content, then re-sizes back to the correct size
displaying the full content.

Changelog-Fixed: Fixed disappearing text on iOS17
2023-08-03 12:34:18 -07:00
William Casarin
b556257edd util: add structured logger 2023-08-03 12:17:56 -07:00
Daniel D‘Aquino
cdc4a7b7a4 Fix UTF support for hashtags
Changelog-Fixed: Fix UTF support for hashtags
Closes: https://github.com/damus-io/damus/issues/1411
Signed-off-by: Daniel D‘Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-03 12:17:32 -07:00
Daniel D‘Aquino
ef5a3030a6 Add unit tests surrounding creation of posts with non-latin hashtags, as well as the rendering of non-latin hashtag
Signed-off-by: Daniel D‘Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-03 12:17:32 -07:00
Daniel D‘Aquino
f0b8dcc5e9 Split view previews in NoteContentView to make both variants visible
Signed-off-by: Daniel D‘Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-03 12:17:32 -07:00
Daniel D‘Aquino
72b60573de Fix compilation error on test target in UserSearchCacheTests
Changelog-Fixed: Fix compilation error on test target in UserSearchCacheTests
Signed-off-by: Daniel D‘Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-08-03 12:17:32 -07:00
William Casarin
6e6c1eb7b6 ndb: make AsciiCharacter a CustomStringConvertible 2023-08-01 21:53:19 -07:00
William Casarin
07dfa3b1fb ndb: update nostrdb
This include various fixes for parsing and key decoding
2023-08-01 21:53:19 -07:00
William Casarin
88306d00a3 key: generate a FullKeypair when generating new keys 2023-08-01 21:53:19 -07:00
William Casarin
616de2eebc state: improve damus state init
It's a bit cleaner now
2023-08-01 21:53:19 -07:00
William Casarin
709aab549b nav: fix nav crashes and buggyness
just use the hashable for equality

Changelog-Fixed: Fix nav crashing and buggyness
2023-08-01 21:53:05 -07:00
William Casarin
15ab9f7135 scroll: allow any hashable target 2023-08-01 21:52:23 -07:00
William Casarin
d4aa8a5602 config: show git hash in version info
This will be useful for sanity checks and bisecting
2023-08-01 09:29:09 -07:00
William Casarin
a9b4cfd424 home: debounce last notified
Calling UserDefaults fast in a loop is not good
2023-07-31 05:38:19 -07:00
William Casarin
2b99f94d13 profiledb: disable database lookups for now
This is causing extremely bad lag in the UI
2023-07-31 05:38:19 -07:00
William Casarin
66e204eb91 notifications: don't do expensive id calculation 2023-07-31 05:38:19 -07:00
William Casarin
7040235605 refactor: add Pubkey, Privkey, NoteId string aliases
This is a non-behavioral change in preparation for the actual switchover
from Strings to Ids. The purpose of this kit is to reduce the size of
the switchover commit which is going to be very large.
2023-07-31 05:38:19 -07:00
William Casarin
f9d21ef901 test: rename test_event to test_note 2023-07-31 05:38:19 -07:00
William Casarin
a08d0a5a19 ndb: more id transition helpers 2023-07-31 04:08:07 -07:00
William Casarin
ff20cc4767 tests: enable code coverage 2023-07-31 03:25:50 -07:00
William Casarin
aacb336002 Update Translations 2023-07-30 11:57:18 -07:00
William Casarin
b40c595a7c notify: switch over to new typesafe notifications 2023-07-30 11:02:44 -07:00
William Casarin
80063af19a notify: add typesafe notifications 2023-07-30 11:02:44 -07:00
William Casarin
df3b94a1fc notify: add typesafe notify class 2023-07-30 11:02:44 -07:00
William Casarin
06a66a3709 add some type aliases to make the ndb move more incremental 2023-07-30 10:52:02 -07:00
William Casarin
1463ce5e3a profile: don't notify on notice
this is just a waste of cpu at this point and could cause main thread
blocking issues
2023-07-30 10:52:02 -07:00
Joel Klabo
480921db20 Suggested Users to Follow
ui: Add Suggested Users Views and Helpers
ui: Add Logic to Launch Suggested User Screen

Changelog-Added: Suggested Users to Follow
2023-07-29 10:25:24 -07:00
doffing.brett
f0de8721c7 Center and Pad buttons in EULA 2023-07-29 10:11:38 -07:00
Suhail Saqan
d11cd76e6a Add multiple reaction support
Changelog-Added: Add support for multiple reactions
Closes: https://github.com/damus-io/damus/issues/1335
2023-07-29 10:03:55 -07:00
Daniel D'Aquino' via patches
815f4d4a96 Allow relay logs to be opened in dev mode even if relay is disconnected
Changelog-Fixed: Allow relay logs to be opened in dev mode even if relay
Closes: https://github.com/damus-io/damus/issues/1368
Signed-off-by: Daniel D'Aquino <daniel@daquino.me>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-29 09:44:36 -07:00
Bryan Montz
4fecf72963 fix: endless connection attempt loop after user removes relay
This patch fixes an issue where, after the user removes a misbehaving
relay, the RelayConnection will keep trying to reconnect endlessly. You
can reproduce the issue prior to this change by adding the relay
wss://brb.io. It will fail to connect over and over. Then remove the
relay in the UI. In the console, you will see that it keeps trying to
connect, and the corresponding RelayConnection never gets deallocated.
After the change, it stops connecting and deallocates the
RelayConnection.

Changelog-Fixed: endless connection attempt loop after user removes relay
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-29 09:18:03 -07:00
William Casarin
593d0e2abe ndb: sync up a few remaining NdbNote tag differences 2023-07-25 16:22:25 -07:00
William Casarin
2f8aa29e92 ndb: make NostrEvents immutable
Since we can't mutate NdbNotes, let's update the existing codebase to
generate and sign ids on NostrEvent constructions. This will allow us to
match NdbNote's constructor
2023-07-25 15:34:05 -07:00
William Casarin
e3c04465fc ndb: move to uint32 for kind and created_at 2023-07-25 15:24:26 -07:00
William Casarin
54d40f7ffd ndb: move hexchar into header
since it's used in a few places
2023-07-25 15:23:36 -07:00
William Casarin
2053033b25 ndb: make note equatble
We need this for the switchover
2023-07-24 13:09:27 -07:00
William Casarin
45801f3e6c ndb: rename NostrEvent to NostrEventOld
This facilitates the switch to NdbNote by allowing us to switch back and
forth to fix things.
2023-07-24 13:08:55 -07:00
William Casarin
2d44f2744b ndb: switch to computed property for tags
this will allows us to change less code on the switchover
2023-07-24 13:08:18 -07:00
William Casarin
04e408bfea ndb: implement a few more event things
We're basically done. Time to try the switch-over
2023-07-24 12:41:12 -07:00
William Casarin
b3c87bdc07 test: remove unused var 2023-07-24 12:40:04 -07:00
William Casarin
b5dd90b36a notes: generalize event_is_reply a bit
so that it works with NdbNote as well
2023-07-24 12:39:55 -07:00
William Casarin
6fa9149939 ndb: avoid double constructor on References 2023-07-24 11:05:18 -07:00
William Casarin
1e9e4a7f3a ndb: implement eventref building from ndb notes 2023-07-24 10:55:34 -07:00
William Casarin
c8e236b6d5 ndb/test: add more test coverage on char iter 2023-07-23 12:21:36 -07:00
William Casarin
e8d0f1db8d test: fix some ndb test warnings 2023-07-23 12:12:42 -07:00
William Casarin
99b5dc94cb ndb: copy over perf improvements 2023-07-23 12:11:08 -07:00
William Casarin
e34351ca37 ndb: fix iterators, pack id tags, more tests 2023-07-23 11:55:36 -07:00
William Casarin
1a33d639ed test: remove some unused perf tests 2023-07-23 11:54:58 -07:00
William Casarin
5c1043b4e5 ndb: add cchar constructors to AsciiCharacter
This will be used for the cchar iterator
2023-07-23 11:54:07 -07:00
William Casarin
23b5763a6b git: ignore perf baselines
this is system-dependent
2023-07-23 11:50:02 -07:00
William Casarin
dd65209a20 Revert "ndb: remove TagIterators and just use sequences"
This reverts commit f0d07c3663.
2023-07-23 10:56:12 -07:00
William Casarin
f0d07c3663 ndb: remove TagIterators and just use sequences
Still learning...
2023-07-22 21:12:53 -07:00
William Casarin
b3119fa41e test: small test fix 2023-07-22 17:23:11 -07:00
William Casarin
7ec8da6c73 ndb: start implementing existing NostrEvent functionality
We eventually want to switch over to NdbNote instead of NostrEvent. To
facilitate this, the plan is to eventually make NostrEvent an alias of
NdbNote. For this to work, let's make sure the NostrEvent extensions are
implemented on NdbNote.

We will likely switch away from string properties as well, but for now
we will try to emulate as much as possible to make sure everything is
working first.
2023-07-22 17:19:47 -07:00
William Casarin
9e659c49b5 ndb/test: add a few more tests 2023-07-22 17:19:47 -07:00
William Casarin
c72666b352 ndb: add subscript and count for TagsSequence
These are helpful
2023-07-22 17:19:47 -07:00
William Casarin
1854e10486 mentions: add ndb mention parser 2023-07-22 17:19:47 -07:00
William Casarin
58e2fb40ef iter: make safer by using NdbNote instead of unsafe pointers
If we have an owned note, we could lose track of the lifetime and then
crash. Let's make sure we always have an NdbNote instead
2023-07-22 17:19:47 -07:00
William Casarin
af7ea7024f misc: don't immediately hex encode event commitment
keep it separate for now, since we're moving to more low level. We
probably won't even use this, but this is cleaner logicwise anyway.
2023-07-22 17:19:47 -07:00
William Casarin
0263c11a94 ndb: add content and owned_size 2023-07-22 17:19:47 -07:00
William Casarin
6d43754e71 ndb: add pubkey to NdbNote 2023-07-22 17:19:47 -07:00
William Casarin
4da23390f8 ndb: update lib 2023-07-22 17:19:47 -07:00
William Casarin
c74993366b move copyndb to the right folder 2023-07-22 17:19:47 -07:00
William Casarin
ad0e1f28b7 test: fix build and tests 2023-07-21 15:26:03 -07:00
William Casarin
61051ee853 nostrdb: add initial swift integration 2023-07-21 15:02:01 -07:00
William Casarin
dc7826c4e5 c: add nostrdb c lib 2023-07-21 15:02:01 -07:00
William Casarin
4eee715bcd c: add jsmn json parser
This is used by the nostrdb lib. Let's add it here.
This doesn't unescape things, so we'll still need to do that manually.
2023-07-21 14:56:24 -07:00
William Casarin
08bea16be0 c: add new cursor util
this is used by nostrdb as well. so add it here ahead of time.
2023-07-21 14:55:54 -07:00
William Casarin
8f04b12a90 c: add copy nostrdb devtool 2023-07-21 14:55:54 -07:00
William Casarin
9cfed9f3aa c: update protoverse_cursor to jb55_cursor
Will be using this in the new db implementation
2023-07-21 14:39:21 -07:00
William Casarin
123ca3b802 test: add my contact list for as json parsing test data 2023-07-21 14:39:21 -07:00
William Casarin
5e7b1f4ff3 event: separate logic from data using extensions
I'm not a huge fan of this pattern but it's getting messy in here
2023-07-21 14:39:11 -07:00
12594e35c1 Update translations
47	Translate Localizable.stringsdict in de
6	Translate Localizable.strings in de
3	Translate Localizable.strings in zh_CN
2	Translate Localizable.strings in sv_SE
2	Translate Localizable.strings in es_419
1	Translate Localizable.stringsdict in zh_TW
1	Translate Localizable.stringsdict in zh_HK
1	Translate Localizable.stringsdict in zh_CN
1	Translate Localizable.stringsdict in sv_SE
1	Translate Localizable.stringsdict in pl_PL
1	Translate Localizable.stringsdict in es_419
1	Translate Localizable.strings in zh_TW
1	Translate Localizable.strings in zh_HK
1	Translate Localizable.strings in pl_PL
1	Translate Localizable.strings in nl

Closes: https://github.com/damus-io/damus/pull/1373
2023-07-19 10:11:42 -07:00
ab92f7b561 Update localization issues and export strings for translation 2023-07-19 10:08:30 -07:00
William Casarin
11b9062865 test: fix some warnings 2023-07-19 10:04:25 -07:00
William Casarin
5c5b55bf67 v1.6 (7) changelog 2023-07-17 14:39:27 -07:00
William Casarin
dd6c082a8e v1.6 (7) 2023-07-17 14:35:54 -07:00
William Casarin
2a4ee6c48c zaps: don't spam lnurls when validate zaps
lnurls.lookup_or_fetch not fetched lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0df3r2dg3mj444
fetching static payreq lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0df3r2dg3mj444
lnurls.lookup_or_fetch already fetching lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0df3r2dg3mj444
lnurls.lookup_or_fetch already fetching lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0df3r2dg3mj444
lnurls.lookup_or_fetch already fetching lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0df3r2dg3mj444
lnurls.lookup_or_fetch already fetching lnurl1dp68gurn8ghj7um9dej8xct5wvhxcmmv9uh8wetvdskkkmn0wahz7mrww4excup0df3r2dg3mj444

Changelog-Fixed: Don't spam lnurls when validating zaps
2023-07-17 14:12:41 -07:00
William Casarin
fa520d48d3 zap: remove unnecessary main thread dispatches when zapping 2023-07-17 14:11:23 -07:00
William Casarin
160b293359 performance: don't spam nip05 validation on startup
Since we don't show these on events anymore, we don't need to spam nip05
validation. We can just check when we go to the profile page

Changelog-Fixed: Eliminate nostr address validation bandwidth on startup
2023-07-17 13:25:56 -07:00
William Casarin
7d17b9b476 nip05: hide nip05 username if it matches the username 2023-07-17 13:25:56 -07:00
William Casarin
d04f1c6867 login: allow user to login to deleted profile
If they every change their mind.

Changelog-Fixed: Allow user to login to deleted profile
2023-07-17 13:25:56 -07:00
William Casarin
5c87dd5bbb nip05: remove clickable option
they're always clickable now
2023-07-17 13:25:56 -07:00
William Casarin
12febf9671 view: extract ProfileEditButton to its own file
profile view file is getting cray cray
2023-07-17 13:25:56 -07:00
William Casarin
4033ad66ba test: fix crash in ci 2023-07-17 13:25:56 -07:00
William Casarin
2c0296cce3 project: bump deployment target
not sure how this is different than the previous setting that was
updated.

Cc: Bryan Montz <bryanmontz@me.com>
2023-07-17 13:25:56 -07:00
William Casarin
080aaf2d1b nip05: show username and support _ usernames
Changelog-Added: Show nostr address username and support abbreviated _ usernames
2023-07-17 11:01:57 -07:00
William Casarin
0e55b08b6c Revert removing nip05 badges on profiles
Changelog-Added: Re-add nip05 badges to profiles

This partially reverts commit 7ae7584135.
2023-07-17 10:52:20 -07:00
William Casarin
ff70cb7ebf posting: don't prepad user tag if its a newline
This fixes one more edgecase with the tag prepend logic.
2023-07-17 10:45:05 -07:00
William Casarin
fe82134a75 posting: switch to new tested composition logic
This switches to the new post composition logic in the post view. It
adds a space at the begging of a mention if it is needed.

We still need to make the state in these view more pure so we can test
more of the posting logic like cursor positions after posting, etc.

Changelog-Added: Add space when tagging users in posts if needed
Changelog-Fixed: Fix issue where typing cc@bob would produce brokenb ccnostr:bob mention
2023-07-17 10:25:09 -07:00
William Casarin
60a0c21272 test: add post composition tests
This adds post composition tests so that we can avoid composition bugs.
This still does not capture all of the dynamics of post composition,
because it ignores much of the mutable cursor position and related state
when editing posts.

We will need to make post editing more pure and less mutable in the
future to get test coverage on those.
2023-07-17 10:25:09 -07:00
William Casarin
8242ca27d2 profile: make constructor args optional
This makes it easier to create one-off profiles for testing. eg:

Profile(name: "jb55")
2023-07-17 10:25:09 -07:00
William Casarin
c7baa153af posting: add some functions for appending mention tags
These are easy-to-test functions for appending user tags to attributed
strings. We will use these in the next couple of commits to replace the
existing buggy functionality.
2023-07-17 10:25:09 -07:00
William Casarin
ff654c4e11 test: add text attribute testing function
This will be used for testing attributed strings
2023-07-17 10:25:09 -07:00
William Casarin
deaf5f042a search: refactor appendUserTag to make logic more clear
ocd mostly
2023-07-17 10:25:09 -07:00
William Casarin
4f56ff3dfb longform: add padding under words count
Changelog-Added: Added padding under word count on longform account
2023-07-17 10:25:09 -07:00
William Casarin
fd59407171 test: fix old markdown tests 2023-07-17 10:25:09 -07:00
William Casarin
9b759247ee v1.6 (6) changelog 2023-07-16 15:34:40 -07:00
William Casarin
cd7998b69d v1.6 (6) 2023-07-16 15:33:00 -07:00
William Casarin
bd4c29604f Fix broken markdown renderer
This switches away from the old markdown renderer to the new one at
https://github.com/damus-io/swift-markdown-ui

Changelog-Fixed: Fix broken markdown renderer
2023-07-16 15:27:24 -07:00
William Casarin
bf1175f22c markdown: add some helpers for counting markdown words
Will use this in the new word counter
2023-07-16 15:27:06 -07:00
William Casarin
064888f78d markdown: use a real-world longform preview 2023-07-16 15:26:31 -07:00
William Casarin
fc640b85ed add swift-markdown-ui
We will be using this lib which is much better than the builtin
framework for markdown rendering. We use a modified version that removes
html tag rendering which looks horrible.
2023-07-16 15:25:09 -07:00
William Casarin
d5766253cf build: fix unused variable warning 2023-07-16 15:24:06 -07:00
William Casarin
571ed39d52 Fixed issue where hashtags were leaking in DMs
Now we never add any tags to DMs, we only add the p tag of the user
you're talking to.

Changelog-Fixed: Fixed issue where hashtags were leaking in DMs
2023-07-16 15:24:06 -07:00
cr0bar
16d81ed40f Hide nsec when logging in
Fix for Hide nsec when logging in & add hide/show toggle

Closes: https://github.com/damus-io/damus/issues/1206
Changelog-Changed: Hide nsec when logging in
2023-07-16 13:05:18 -07:00
William Casarin
1135c19fea test: add setting property tests
Some initial UserSettingsStore property tests
2023-07-16 13:05:18 -07:00
William Casarin
77331644cb Fix issue with emojis next to hashtags and urls
Treat utf8 bytes next to hashtags and urls as boundary conditions

Changelog-Fixed: Fix issue with emojis next to hashtags and urls
2023-07-16 11:46:23 -07:00
William Casarin
8d14fdffb5 content: add utf8 char at url left boundary test 2023-07-16 11:46:23 -07:00
William Casarin
0c95071de7 project: rename parse_mentions to parse_note_content
This is more accurate
2023-07-16 11:46:23 -07:00
William Casarin
da78a217a3 docs: clarify the section on using -v2,v3, etc
Some patches are still not getting sent with version information. Let's
clarify that in the contribution docs.

Cc: dev@damus.io
2023-07-16 10:05:57 -07:00
William Casarin
f53b824122 docs: patch changelogs when submitting patches
This adds a section on creating patch changelogs when submitting
patches. It helps reviewers know what changed between many different
versions of a patch
2023-07-16 09:44:27 -07:00
Bryan Montz
45ab394b09 fixed: relay detail view is not immediately available after adding new relay
Changelog-Fixed: relay detail view is not immediately available after adding new relay
Closes: https://github.com/damus-io/damus/issues/1369
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:56:18 -07:00
Bryan Montz
47e7505573 fix typos
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00
Bryan Montz
0f1390f412 Swift cleanup: remove duplicate or unnecessary initializers using default values
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00
Bryan Montz
6bf5293701 Swift cleanup: don't capture case values only to ignore them in switch statements
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00
Bryan Montz
3d6909bf62 Swift cleanup: simplify "Task.init {}" to "Task {}"
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00
Bryan Montz
ecd8b64b8b Swift cleanup: prefer case list over fallthrough in switch statements
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00
Bryan Montz
0c627ae0a0 Swift cleanup: "init (" -> "init("
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-16 07:37:55 -07:00
William Casarin
16c86c1d1c update bad commit mailmap 2023-07-14 22:25:19 -07:00
Daniel D'Aquino' via patches
29140d956b Add feedback message when user adds a relay already in the list
Changelog-Added: Added feedback when user adds a relay that is already on the list
Closes: https://github.com/damus-io/damus/issues/1053
Signed-off-by: Daniel D'Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-14 22:11:34 -07:00
William Casarin
7ae7584135 ui: remove nip05 badge on events
Changelog-Changed: Remove nip05 on events
2023-07-14 17:31:28 -07:00
William Casarin
139be9eef2 Fix nostr: mention prefix bugs
The zero-width space was causing parsing issues. Not sure why we need
this so I just removed it.

Changelog-Fixed: Fix nostr:nostr:... bugs
2023-07-14 17:28:24 -07:00
William Casarin
72a060c7b3 nip05: rename nip05 to Nostr Address in search
Forgot this one
2023-07-14 17:05:01 -07:00
William Casarin
9db81fd6b8 views: refactor post_changed in PostView
Use some helper functions instead of the full switch
2023-07-14 15:54:17 -07:00
William Casarin
f08efd7e30 nip05: rename nip05 verification to nostr address
nip05 identifiers and nip05 verification is too confusing, and also
wrong. Let's use the "nostr address" terminology.

Suggested-by: Derek Ross
Suggested-by: Semisol <hi@semisol.dev>
Changelog-Changed: Rename NIP05 to "nostr address"
2023-07-14 13:26:10 -07:00
William Casarin
fb2a69acd8 project: fix test fixtures 2023-07-14 13:07:52 -07:00
8a9e3ea76b Fix localization issues and export strings for translation
Changelog-Fixed: Fix localization issues and export strings for translation
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-14 09:34:29 -07:00
William Casarin
4830a6f3b7 Run actions on pushes to the ci branch 2023-07-14 09:34:29 -07:00
William Casarin
9879c78e41 build 5 because I broked something 2023-07-13 15:43:21 -07:00
William Casarin
05e73a3711 actually subscribe to likes. oops 2023-07-13 11:43:52 -07:00
William Casarin
731fdb108b build: fix a few warnings and errors 2023-07-13 11:17:00 -07:00
William Casarin
2f3737c2b5 v1.6-4 changelog 2023-07-13 11:14:29 -07:00
William Casarin
e36747a81a v1.6 (4) 2023-07-13 11:13:50 -07:00
William Casarin
505ce0bd39 Add the ability to follow hashtags
Changelog-Added: Add the ability to follow hashtags
2023-07-13 11:10:53 -07:00
William Casarin
31fa63debf home: hide users and hashtags from home timeline when you unfollow
Add the ability to resubscribe to home filters so that it will be
updated when you follow and unfollow people

Changelog-Fixed: Hide users and hashtags from home timeline when you unfollow
2023-07-13 11:08:09 -07:00
William Casarin
122655bea3 home: separate home filters
we will want to resubscribe to these, so pull them out
2023-07-13 11:08:09 -07:00
William Casarin
9a714943fd contacts: get followed hashtags function
todo: cache these
2023-07-13 11:08:09 -07:00
William Casarin
17df2972d9 ui: add follow hashtag ui on search view 2023-07-13 11:08:04 -07:00
William Casarin
bebaffd247 contacts: unify following logic
We are about to add hashtag following, so let's prepare handle_follow
for this. Generalize pubkey following to ReferenceId follows in the
handle_{follow,unfollow} functions.

We also split out the notification part into its own function.
2023-07-13 09:32:42 -07:00
William Casarin
0fae54a98d components: make GradientButtonStyle padding configurable
There is too much padding on the follow hashtag button so we need to fix
that
2023-07-13 09:04:55 -07:00
William Casarin
90818c12e8 components: create PinkGradientView and use PinkGradient directly
Still need to do this for the other gradients as well but this is fine
for now.
2023-07-13 09:04:55 -07:00
William Casarin
1136808afa contacts: generalize following to allow any reference
I noticed we are not using the PostBox when following new users. Not
good! This is probably why following users sometimes does not work.

Changelog-Fixed: Fixed a bug where following a user might not work due to poor connectivity
2023-07-13 09:04:55 -07:00
William Casarin
b7d139ffb3 refid: add .t helper
This is used for quickly creating hashtag refs
2023-07-13 09:04:55 -07:00
William Casarin
7fc270725f test: add newline mention test
This is currently passing but it shouldn't be. This is because we are
not testing the build_post function directly. We will do this soon.
2023-07-13 07:32:27 -07:00
William Casarin
7b73a54de5 test: switch to test data file
We only added the file before, let's actually use it now
2023-07-13 07:32:27 -07:00
Bryan Montz
fdaf785869 fixed: icon color for developer mode setting is incorrect in low-light mode
Changelog-Fixed: icon color for developer mode setting is incorrect in low-light mode
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-13 07:24:44 -07:00
William Casarin
c0f9b0a8c0 views: allow embeddable views at top of timeline
This allows you to put stuff at the top of a timeline inside the scroll
view. We could also remove the scrollview from the timeline
eventually... but this works for now.
2023-07-13 06:56:23 -07:00
William Casarin
7123b225a1 search: make model an ObjservedObject
This should not be a state object because the data is passed in
elsewhere
2023-07-13 06:56:23 -07:00
William Casarin
b6f25a85f8 Fix nip05 badge icon 2023-07-13 06:56:23 -07:00
William Casarin
7046fe0d4f ui: add DamusBackground helper
We will be using this in more places
2023-07-13 06:56:23 -07:00
William Casarin
201e9a427f post: extract build_post from post view
I need to test this function because there is a bug with nostr: mentions
2023-07-13 06:56:23 -07:00
William Casarin
6481f96488 add bech32_pubkey_decode
I need this for a test
2023-07-13 06:56:23 -07:00
William Casarin
3845d32074 test: add test data file
We can organize test data in here
2023-07-13 06:56:23 -07:00
William Casarin
c1c33518ea don't follow jb55 by default
This was funny initially but it confuses people.

Changelog-Removed: Remove following Damus Will by default
2023-07-12 14:42:23 -07:00
William Casarin
f2cf30a728 Scroll to top for longform events only
Fixes: ad6a1962 ("Scroll to top of event instead of bottom")
2023-07-12 08:23:53 -07:00
William Casarin
69922b1d77 Remove LoadMoreButton
Was an old unused thing
2023-07-12 08:21:44 -07:00
William Casarin
7343fcd399 Allow longform content to be long
Changelog-Changed: Remove note size restriction for longform events
2023-07-12 05:38:48 -07:00
ericholguin
5571052cfd Update nav to use adaptable color for dark and light modes
Changelog-Fixed: Fixed nav bar color on login, eula, and account creation
Closes: https://github.com/damus-io/damus/pull/1361
2023-07-12 05:38:10 -07:00
William Casarin
de63e96664 v1.6-3 changelog
A few longform fixes
2023-07-11 12:59:09 -07:00
William Casarin
7f9371d85f v1.6 (3) 2023-07-11 12:58:15 -07:00
William Casarin
de4e8e5748 Only show longform preview in notifications
Changelog-Fixed: Show longform previews in notifications instead of the entire post
2023-07-11 12:56:30 -07:00
William Casarin
ad6a1962bb Scroll to top of event instead of bottom
This is pretty important for longform events

Changelog-Changed: Start at top when reading longform events
2023-07-11 12:55:54 -07:00
William Casarin
828e417726 Allow reposting and quote reposting multiple times
Changelog-Changed: Allow reposting and quote reposting multiple times
2023-07-11 12:28:38 -07:00
William Casarin
d2374aa6ec I broked dms. i fixed. 2023-07-11 12:28:38 -07:00
William Casarin
495859e07f Fix various padding issues related to longform posts
1. Make a proper threaded EventShell variant
2. Fix padding everywhere

Changelog-Fixed: Fix padding on longform events
2023-07-11 12:17:59 -07:00
William Casarin
d96ea593a5 search: allow searching longform articles by hashtag 2023-07-11 12:17:22 -07:00
William Casarin
7514a741c0 docs: make note to replace old bech32 parser 2023-07-11 12:17:09 -07:00
William Casarin
dc7b0004bc Hide action bar in longform quote reposts
Changelog-Fixed: Fix action bar appearing on quoted longform previews
2023-07-11 10:26:29 -07:00
William Casarin
8e33d5f6b9 v1.6-2 changelog 2023-07-11 09:22:39 -07:00
William Casarin
db2ec0a00a Fix npub mention bugs, fix slowness when parsing large posts
Switch the post parser to use the same code as the content parser. This
was causing many issues, including performance issues.

Changelog-Fixed: Fix lag when creating large posts
Changelog-Fixed: Fix npub mentions failing to parse in some cases
Changelog-Added: Add r tag when mentioning a url
Changelog-Removed: Remove old @ and & hex key mentions
2023-07-11 09:15:13 -07:00
cr0bar
dc21b6139c Add support for multilingual hashtags
Changelog-Added: Add support for multilingual hashtags
Reviewed-by: William Casarin <jb55@jb55.com>
Closes: https://github.com/damus-io/damus/issues/949
2023-07-11 07:22:44 -07:00
William Casarin
031c7823ae refactor: move hashtag tests to their own file 2023-07-11 07:21:16 -07:00
cr0bar
ac2b5b26bb Added non-latin test and amended emoji test to include emoji in hashtag 2023-07-11 06:39:12 -07:00
cr0bar
c1220f50af Handle percent encoding of colon for some hashtags 2023-07-11 06:39:12 -07:00
cr0bar
2353f97114 Change to is_hashtag_chat to support non-latin characters 2023-07-11 06:39:12 -07:00
cr0bar
e83e110adb Fix to is_boundary to support non-latin characters 2023-07-11 06:39:12 -07:00
William Casarin
aae97c5cb7 git: add .mailmap file
This ensures that author emails are correct when using various git tools
2023-07-11 06:39:05 -07:00
William Casarin
45d9121ed7 fix project issues 2023-07-10 20:56:52 -07:00
4c774f2dda Fix PostView initial string to skip mentioning self when on own profile
Changelog-Fixed: Fix PostView initial string to skip mentioning self when on own profile
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Reviewed-by: William Casarin <jb55@jb55.com>
2023-07-10 20:16:18 -07:00
b8ec3493dc Fix freezing bug when tapping Developer settings menu
Changelog-Fixed: Fix freezing bug when tapping Developer settings menu
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Reviewed-by: William Casarin <jb55@jb55.com>
2023-07-10 20:16:18 -07:00
William Casarin
e299389866 Add initial longform note support
Changelog-Added: Add initial longform note support
2023-07-10 18:29:18 -07:00
William Casarin
374610a21a artifacts: allow unseparated note artifacts
This is needed for longform events. Right now we treat unseparated note
artifacts as a list of blocks, but we will likely need to render these
blocks into lists of attributed texts with image blocks inbetween.
2023-07-10 18:24:43 -07:00
William Casarin
4d995fd04c Longform Notes 2023-07-10 17:39:13 -07:00
William Casarin
518886912c refactor: carve out TextEvent body into EventShell
We'll need this for other event types
2023-07-10 17:39:13 -07:00
William Casarin
ab5eea330a options: add no_mentions to event view options
We don't need mentions in longform previews so we'll need this
2023-07-10 17:39:13 -07:00
William Casarin
41de715067 query: add longform kind, add to home filter 2023-07-10 17:39:13 -07:00
William Casarin
6ca9bda01e notes: count words in notes during artifact parsing 2023-07-10 17:39:13 -07:00
William Casarin
fe077fa5c2 reposts: don't always show text events in reposts
This will allow longform reposts to work properly

Changelog-Fixed: Don't always show text events in reposts
2023-07-10 17:39:13 -07:00
William Casarin
cb2380e218 docs: add git-contacts example
git-contacts is a great way to cc people who have touched the same hunk
of code before.

Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-10 17:38:54 -07:00
Joel Klabo
196cfdec4b Fix Image Orientation 2023-07-10 17:27:51 -07:00
Joel Klabo
bfb47c0f85 Update Control Style to Stand Out More 2023-07-10 17:27:51 -07:00
Joel Klabo
9e7e128d9a Refactoring Edit Picture Views 2023-07-10 17:27:51 -07:00
Joel Klabo
bf95a8b328 Banner Image Upload
Changelog-Added: Enable banner image editing
2023-07-10 17:27:42 -07:00
William Casarin
e316d5d635 docs: move security.md to docs subdir 2023-07-10 16:37:45 -07:00
William Casarin
37a5abc9e3 gitignore: add tags 2023-07-10 16:35:43 -07:00
William Casarin
cf83ac1fe8 docs: add patch submission guidelines 2023-07-10 16:22:09 -07:00
cr0bar
7a1269bd68 Fix for test issue due to recently implemented RelayPool change 2023-07-10 13:49:07 -07:00
William Casarin
acb4e6d17e wasm: fix intptr warning 2023-07-10 11:14:03 -07:00
William Casarin
e957c3b703 wasm: fix clz64 warning 2023-07-10 11:14:03 -07:00
William Casarin
82fc4ff15e wasm: comment out some unnused code for now
fixes some warnings
2023-07-10 11:14:03 -07:00
William Casarin
15d633a42f project: update to recommend settings 2023-07-10 11:08:20 -07:00
William Casarin
7158f07bb1 Translate all the things 2023-07-10 08:20:28 -07:00
Bryan Montz
07abc5c04b Fix issue where first row is always selected on Form views
Changlog-Fixed: Fix issue where first row is always selected on Form views
Signed-off-by: Bryan Montz <bryanmontz@me.com>
2023-07-10 07:54:47 -07:00
transifex-integration[bot]
79fb352d96 Translate Localizable.strings in el_GR
100% translated source file: 'Localizable.strings'
on 'el_GR'.
2023-07-10 08:46:21 +00:00
transifex-integration[bot]
94ef9bb42a Translate Localizable.strings in el_GR
100% translated source file: 'Localizable.strings'
on 'el_GR'.
2023-07-10 08:46:09 +00:00
transifex-integration[bot]
78a64165e1 Translate Localizable.stringsdict in el_GR
100% translated source file: 'Localizable.stringsdict'
on 'el_GR'.
2023-07-10 08:43:55 +00:00
transifex-integration[bot]
ad216b1f11 Translate Localizable.strings in nl
100% translated source file: 'Localizable.strings'
on 'nl'.
2023-07-10 08:35:54 +00:00
transifex-integration[bot]
4abd227cf7 Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-07-10 08:35:46 +00:00
transifex-integration[bot]
800ce44f5e Translate Localizable.strings in ja
100% translated source file: 'Localizable.strings'
on 'ja'.
2023-07-10 08:12:11 +00:00
transifex-integration[bot]
0e9e44d8f2 Translate Localizable.stringsdict in ja
100% translated source file: 'Localizable.stringsdict'
on 'ja'.
2023-07-10 07:51:47 +00:00
transifex-integration[bot]
3eba4b0af9 Translate Localizable.strings in ja
100% translated source file: 'Localizable.strings'
on 'ja'.
2023-07-10 07:51:06 +00:00
140c3505ba Update translations 2023-07-09 15:36:35 -04:00
fcd7d2beab Fix localization issues and export strings for translation 2023-07-09 15:33:15 -04:00
William Casarin
83ef50586a zaps/refactor: use guard instead of if block
not a fan of unncessary nesting
2023-07-09 07:44:33 -07:00
William Casarin
87992f4bb9 Add RelayLog in developer mode
Changelog-Added: Add relay log in developer mode
2023-07-09 07:41:45 -07:00
Bryan Montz
faaa3e3bd9 only show the relay log in developer mode
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
2d9f7128ee fix crash when adding line to log from background thread
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
51d71f11c1 replace RelayMetadatas with RelayModelCache in DamusState
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
f619fef410 add RelayModel and RelayModelCache classes
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
91f02ccff5 add RelayLog to the bottom of the RelayDetailView
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
a63ea1e22b add network state changes to RelayLogs
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
40e5e4a026 add a RelayLog to each RelayConnection and send events to it
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
Bryan Montz
ef4aeb40e0 add RelayLog class
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-09 07:40:39 -07:00
William Casarin
13f98659a4 Prevent forged profile zap attacks
The fake note zap attack made me realize that there is a way to do fake
profile zaps using a similar technique. Since damus only checks the
first ptag if it is a profile zap, this means you could include multiple
ptags, the first one being the fake profile with the fake zapper, and
the second p tag as the real target.

This would allow a fake zapper to create a fake a zap, while the zap
notification would still appear for the second ptag because damus
listens for zap events via #p, and that would match the second ptag.

To fix this, ensure that zaps only have at most 1 ptag and 0 or 1 etag.
my CLN zapper checks this but if we don't check this here as well then
we run into fake zap issues.

Changelog-Fixed: Fix potential fake profile zap attacks
Cc: Tony Giorgio <tonygiorgio@protonmail.com>
Cc: benthecarman <benthecarman@live.com>
Cc: Vitor Pamplona <vitor@vitorpamplona.com>
2023-07-08 22:10:34 -07:00
William Casarin
f5ba909784 zaps: move pubkey check into standalone function 2023-07-08 22:09:30 -07:00
William Casarin
6031fe0847 Fix fake note zaps with forged p-tags
This fixes a zap issue where someone could send a fake zap with a zapper
that doesn't match the user's nostrPubkey zapper. This is possible
because damus looks up the zapper via the ptag on note zaps.

Fix this by first looking up the cached event's ptag instead. This
prevents zappers from trying to trick Damus into picking the wrong
zapper.

Fixes: #1357
Changelog-Fixed: Fix issue where malicious zappers can send fake zaps to another user's posts
Reported-by: benthecarman <benthecarman@live.com>
Cc: Tony Giorgio <tonygiorgio@protonmail.com>
2023-07-08 21:22:58 -07:00
William Casarin
1be2a9e1b1 ui: remove invalid zap text 2023-07-08 20:47:11 -07:00
cr0bar
4478348d10 Fix profile post button mentions
Fix for second part of issue #1352 where if you submit a reply from the
+ on a profile, it uses the hex nostr url rather than the bech32
version. When typing the @ manually it uses the bech32 so updated to
mirror this.

Changelog-Fixed: Fix profile post button mentions
Closes: #1355
2023-07-08 19:24:35 -07:00
Anthony de Broise
cf4131f867 Minor update to ConfigView.swift to fix key and search icon
Replaced icon names with names existing in assets to avoid them being left blank.

Changelog-Fixed: Fix icons on settings view
Closes: #1353
2023-07-08 08:14:26 -07:00
Bryan Montz
81b69bc2ea add explanatory footer to Developer Mode setting view
Signed-off-by: Bryan Montz <bryanmontz@me.com>
Reviewed-by: William Casarin <jb55@jb55.com>
2023-07-08 08:06:52 -07:00
William Casarin
0c736a18a9 docs: annotate might be causing issues for some people
suhail was having trouble when this option was enabled. let's remove it
just in case.
2023-07-07 09:25:33 -07:00
Bryan Montz
d2efe06610 make "Copy Note JSON" a developer mode setting
Signed-off-by: Bryan Montz <bryanmontz@me.com>
2023-07-07 09:02:52 -07:00
Bryan Montz
ebcfe3c25f add developer mode view and setting
Signed-off-by: Bryan Montz <bryanmontz@me.com>
2023-07-07 09:02:52 -07:00
William Casarin
6dfda93ff9 Fix Invalid Zap bug in reposts
Changelog-Fixed: Fix Invalid Zap bug in reposts
2023-07-04 13:48:49 -07:00
William Casarin
ea50f9214a Switch to navigation stack in BuilderEventView 2023-07-04 13:48:49 -07:00
William Casarin
6c8cf8421c zaps: make zap setting private 2023-07-04 13:47:44 -07:00
Ben Harvie
cbbe203d84 Create SECURITY.md 2023-07-04 12:45:15 -07:00
William Casarin
19217f47a4 v1.6 changelog 2023-07-04 12:21:12 -07:00
William Casarin
3451e7d88f v1.6 2023-07-04 12:18:33 -07:00
William Casarin
b5ea1e011e Revert "profile: make profile loading more lightweight for now"
Changelog-Fixed: Load more content on profile view
2023-07-04 11:51:07 -07:00
William Casarin
a04a401292 nscript: load script view
This allows you to open and run scripts for testing purposes, but only
from external links such as nostr:nscript...
2023-07-04 11:48:27 -07:00
640fbf23ea Fix UI bug with user search and fix race conditions on profiles NIP-05 cache
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-04 09:09:14 -07:00
William Casarin
3d0448a929 smaller nostrscript 2023-07-03 17:10:57 -07:00
William Casarin
30e33a01c1 nostrscript: add a helper function 2023-07-03 16:59:50 -07:00
William Casarin
a6cbf50def settings: record bool option keys
so that NostrScripts know which bool settings can be set
2023-07-03 16:28:25 -07:00
prprhyt
94bd194287 Added event id validation 2023-07-03 15:03:33 -07:00
William Casarin
97f10e865f NostrScript
NostrScript is a WebAssembly implementation that interacts with Damus.
It enables dynamic scripting that can be used to power custom list views,
enabling pluggable algorithms.

The web has JavaScript, Damus has NostrScript. NostrScripts can be
written in any language that compiles to WASM.

This commit adds a WASM interpreter I've written as a mostly-single C
file for portability and embeddability. In the future we could
JIT-compile these for optimal performance if NostrScripts get large and
complicated. For now an interpreter is simple enough for algorithm list
view plugins.

Changelog-Added: Add initial NostrScript implementation
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-03 14:31:38 -07:00
0c0c58c0cc Fix bug with Trie search
Exact matches were not being returned first in the array of results

Signed-off-by: Terry Yiu <git@tyiu.xyz>
Reviewed-by: William Casarin <jb55@jb55.com>
Signed-off-by: William Casarin <jb55@jb55.com>
2023-07-03 13:37:52 -07:00
William Casarin
7d49d3d9f1 refactor: make guard statement a bit more readible
It's a bit confusing to guard on a negative boolean expression
2023-07-03 12:48:25 -07:00
cb1e16b1a4 Fix reports to conform to NIP-56
Changelog-Fixed: Fix reports to conform to NIP-56
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Tested-by: William Casarin <jb55@jb55.com>
2023-07-03 12:25:12 -07:00
6e964f71ff Add trie-based user search cache to replace non-performant linear scans
Changelog-Added: Speed up user search
Tested-by: William Casarin <jb55@jb55.com>
Fixes: #1219
Closes: #1342
2023-07-03 12:06:01 -07:00
4b7444f338 Fix profile navigation bugs from muted users list and relay list views
Changelog-Fixed: Fix profile navigation bugs from muted users list and relay list views
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Reviewed-by: William Casarin <jb55@jb55.com>
2023-07-03 08:59:28 -07:00
57159f7df9 Fix build warnings
Signed-off-by: Terry Yiu <git@tyiu.xyz>
Reviewed-by: William Casarin <jb55@jb55.com>
2023-07-03 08:50:17 -07:00
4712c6b288 Fix navigation to translation settings view
Changelog-Fixed: Fix navigation to translation settings view
Signed-off-by: Terry Yiu <git@tyiu.xyz>
2023-07-03 08:25:04 -07:00
William Casarin
6a9b3cad20 Merge remote-tracking branch 'github/translations' 2023-07-02 13:34:04 -07:00
William Casarin
e5bd52b1f6 New paid email patch policy 2023-07-02 13:15:24 -07:00
Bryan Montz
7cd3aef157 Updated test target to deployment target of iOS 16.0
Changelog-Updated: Bumped minimum verison to iOS 16.0
Signed-off-by: Bryan Montz <bryanmontz@me.com>
2023-07-02 13:03:13 -07:00
transifex-integration[bot]
8f3d8ced90 Translate Localizable.stringsdict in es_ES
100% translated source file: 'Localizable.stringsdict'
on 'es_ES'.
2023-07-02 19:51:54 +00:00
transifex-integration[bot]
98ff4ee363 Translate Localizable.stringsdict in es_ES
100% translated source file: 'Localizable.stringsdict'
on 'es_ES'.
2023-07-02 19:51:22 +00:00
transifex-integration[bot]
ce7c4799c9 Translate Localizable.stringsdict in es_ES
100% translated source file: 'Localizable.stringsdict'
on 'es_ES'.
2023-07-02 19:50:09 +00:00
transifex-integration[bot]
11a4a85bdf Translate Localizable.strings in es_ES
100% translated source file: 'Localizable.strings'
on 'es_ES'.
2023-07-02 19:47:59 +00:00
transifex-integration[bot]
6fe4ac1bd0 Translate Localizable.stringsdict in ja
100% translated source file: 'Localizable.stringsdict'
on 'ja'.
2023-07-02 09:13:54 +00:00
William Casarin
f702733654 nav: remove environmentObjects
environment objects are implicit arguments that cannot be checked by the
compiler. They are a common source of crashes. Use a main
NavigationCoordinator in DamusState for the core app, and pass in other
coordinators in the account setup view for the parts of the app that
don't have a DamusState.
2023-06-30 09:59:58 -07:00
Scott Penrose
9008c609e2 Switch to NavigationStack
Changelog-Changed: Drop iOS15 support
Changelog-Fixed: Fixed navigation popping issues
2023-06-30 06:44:26 -07:00
William Casarin
5bac6405b9 validation: make sure to run on a detached task
so we don't do sig validation on the main thread accidentally
2023-06-30 06:42:56 -07:00
Scott Penrose
69663b8207 A few more navigation links from rebase 2023-06-30 06:42:56 -07:00
Scott Penrose
58a707685c Fix FollowUserView not allowing profile tapping 2023-06-30 06:42:56 -07:00
Scott Penrose
a76ddea7da Remove popToRoot when tapping damus:// internal links 2023-06-30 06:42:56 -07:00
Scott Penrose
0018e7ad57 Convert remaining navigation links 2023-06-30 06:42:56 -07:00
Scott Penrose
8258c5beb0 Convert ContentView navigation links 2023-06-30 06:42:56 -07:00
Scott Penrose
f361f55bd5 Convert wallet NavigationLinks 2023-06-30 06:42:56 -07:00
Scott Penrose
c50ccef56d Convert onboarding flow navigation links 2023-06-30 06:42:56 -07:00
Scott Penrose
242455410e Convert more NavigationLinks to router 2023-06-30 06:42:56 -07:00
Scott Penrose
f0b0eade37 Convert to NavigationStack
- Fixes linking issues on SideMenu and tab switching issues
- I currently bumped to iOS 16+ to get iterate and get this working.
2023-06-30 06:42:56 -07:00
William Casarin
3e3b689647 readme: include new mailing lists 2023-06-29 07:45:50 -07:00
transifex-integration[bot]
67e3ee8978 Translate Localizable.strings in es_419
100% translated source file: 'Localizable.strings'
on 'es_419'.
2023-06-28 21:22:27 +00:00
transifex-integration[bot]
90891622e4 Translate Localizable.stringsdict in es_419
100% translated source file: 'Localizable.stringsdict'
on 'es_419'.
2023-06-28 21:20:50 +00:00
William Casarin
62f052daa5 nozaps: fix zap button in freedom edition 2023-06-28 21:16:56 +02:00
25b3df8b89 Disable post button when media upload in progress
Changelog-Fixed: Disable post button when media upload in progress
Closes: #1324
2023-06-28 19:31:57 +02:00
14accd222e Fix taps on mentions in note drafts to not redirect to other Nostr clients
Changelog-Fixed: Fix taps on mentions in note drafts to not redirect to other Nostr clients
Closes: #1319
2023-06-28 19:31:16 +02:00
William Casarin
abcff3b928 profile: allow post button on every profile and prefill user tag
Changelog-Added: Add post button to profile pages
2023-06-28 17:40:27 +02:00
William Casarin
c8f18958a2 refactor: cleanup processFocusedWordForMention 2023-06-28 17:09:08 +02:00
transifex-integration[bot]
7a055efda8 Translate Localizable.stringsdict in hu_HU
100% translated source file: 'Localizable.stringsdict'
on 'hu_HU'.
2023-06-28 15:01:14 +00:00
transifex-integration[bot]
1ad8773c26 Translate Localizable.strings in hu_HU
100% translated source file: 'Localizable.strings'
on 'hu_HU'.
2023-06-28 14:57:46 +00:00
William Casarin
3b07a207c4 post: extract createUserTag so it can be re-used 2023-06-28 16:26:59 +02:00
William Casarin
d9a06e69ae misc: remove some dead code 2023-06-28 16:25:04 +02:00
32c71a4770 Add post button when logged in with private key and on own profile view
Changelog-Added: Add post button when logged in with private key and on own profile view
Closes: #1325
2023-06-28 15:49:34 +02:00
Bryan Montz
087d3e16a1 After loading the user's relays from their contact event, connect to new relays
Closes: #1298
2023-06-28 15:16:36 +02:00
7cae61a86a Fix missing profile zap notification text
Changelog-Fixed: Fix missing profile zap notification text
Closes: #1332
2023-06-28 14:48:06 +02:00
transifex-integration[bot]
b868119277 Translate Localizable.strings in ja
100% translated source file: 'Localizable.strings'
on 'ja'.
2023-06-28 08:15:41 +00:00
William Casarin
82c53e43e5 v1.5 (8) 2023-06-27 06:30:50 +02:00
William Casarin
3e274a820a nozaps: restore zap button with zap info, just make it not clickable 2023-06-27 06:04:36 +02:00
William Casarin
1a0282fe21 Revert "nozaps: don't pull thread zaps in nozaps mode"
This reverts commit 6003a3c6f8.
2023-06-27 05:59:59 +02:00
William Casarin
b2b687fb79 Revert "nozaps: hide zap total"
This reverts commit 57789de5cd.
2023-06-27 05:59:33 +02:00
William Casarin
94448a10bd Revert "nozaps: hide zap details on notes for now"
This reverts commit b0d6d33573.
2023-06-27 05:58:42 +02:00
William Casarin
66db4c5215 Revert "nozaps: don't show note zaps in notifications"
This reverts commit c5b0e539d8.
2023-06-27 05:58:39 +02:00
William Casarin
1e2326cccf v1.5 (7)
rip zap button
2023-06-27 05:35:53 +02:00
William Casarin
959f208e36 profile: make profile loading more lightweight for now 2023-06-27 05:31:22 +02:00
William Casarin
7d80985b06 nozaps: remove zap button on posts 2023-06-27 05:31:14 +02:00
transifex-integration[bot]
3b085ab826 Translate Localizable.strings in zh_HK
100% translated source file: 'Localizable.strings'
on 'zh_HK'.
2023-06-26 10:46:06 +00:00
transifex-integration[bot]
28077ab91d Translate Localizable.stringsdict in zh_HK
100% translated source file: 'Localizable.stringsdict'
on 'zh_HK'.
2023-06-26 10:45:27 +00:00
transifex-integration[bot]
8d0a8909b9 Translate Localizable.strings in zh_TW
100% translated source file: 'Localizable.strings'
on 'zh_TW'.
2023-06-26 10:43:34 +00:00
transifex-integration[bot]
23bc6d0710 Translate Localizable.stringsdict in zh_TW
100% translated source file: 'Localizable.stringsdict'
on 'zh_TW'.
2023-06-26 10:41:55 +00:00
transifex-integration[bot]
993444d24b Translate Localizable.stringsdict in zh_CN
100% translated source file: 'Localizable.stringsdict'
on 'zh_CN'.
2023-06-26 10:40:49 +00:00
transifex-integration[bot]
cd30154990 Translate Localizable.strings in zh_CN
100% translated source file: 'Localizable.strings'
on 'zh_CN'.
2023-06-26 10:40:42 +00:00
transifex-integration[bot]
4513863c95 Translate Localizable.strings in zh_CN
100% translated source file: 'Localizable.strings'
on 'zh_CN'.
2023-06-26 10:37:50 +00:00
transifex-integration[bot]
19684bae36 Translate Localizable.strings in zh_CN
100% translated source file: 'Localizable.strings'
on 'zh_CN'.
2023-06-26 10:37:19 +00:00
transifex-integration[bot]
6b878e96cd Translate Localizable.stringsdict in zh_CN
100% translated source file: 'Localizable.stringsdict'
on 'zh_CN'.
2023-06-26 10:32:17 +00:00
William Casarin
40a51edafe readme: patchstr bounties 2023-06-26 12:03:05 +02:00
cfe14fac23 Deduplicate users in group notifications
Changelog-Fixed: Deduplicate users in notifications
Closes: #1326
2023-06-26 11:31:36 +02:00
2046fe5502 Fix notification content rendering of repost and reaction events
Closes: #1318
Changelog-Fixed: Fix notification content rendering of repost and reaction events
2023-06-26 11:20:11 +02:00
Bryan Montz
2d4ddc7b9c Fix crash related to VideoPlayer and CMTime
Closes: #1321
Changelog-Fixed: Fix crash related to VideoPlayer
2023-06-26 11:20:11 +02:00
William Casarin
6003a3c6f8 nozaps: don't pull thread zaps in nozaps mode 2023-06-26 11:20:11 +02:00
transifex-integration[bot]
572cae7dc5 Translate Localizable.strings in nl
100% translated source file: 'Localizable.strings'
on 'nl'.
2023-06-26 08:59:40 +00:00
transifex-integration[bot]
8d7d3d0d37 Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-26 08:58:56 +00:00
transifex-integration[bot]
c76fc5bcce Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-26 08:58:25 +00:00
transifex-integration[bot]
3a357c8d82 Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-26 08:58:10 +00:00
transifex-integration[bot]
ac59ee6285 Translate Localizable.strings in el_GR
100% translated source file: 'Localizable.strings'
on 'el_GR'.
2023-06-26 08:52:08 +00:00
transifex-integration[bot]
a870b86490 Translate Localizable.stringsdict in el_GR
100% translated source file: 'Localizable.stringsdict'
on 'el_GR'.
2023-06-26 08:43:40 +00:00
transifex-integration[bot]
cb2da7f3c6 Translate Localizable.stringsdict in sv_SE
100% translated source file: 'Localizable.stringsdict'
on 'sv_SE'.
2023-06-26 06:28:24 +00:00
transifex-integration[bot]
71f3b9b013 Translate Localizable.strings in sv_SE
100% translated source file: 'Localizable.strings'
on 'sv_SE'.
2023-06-26 06:27:13 +00:00
e220b0756f Export strings for translation 2023-06-25 23:46:53 -04:00
transifex-integration[bot]
83abedb4d6 Translate Localizable.stringsdict in sv_SE
100% translated source file: 'Localizable.stringsdict'
on 'sv_SE'.
2023-06-25 23:45:19 -04:00
transifex-integration[bot]
23b057779a Translate Localizable.strings in sv_SE
100% translated source file: 'Localizable.strings'
on 'sv_SE'.
2023-06-25 23:45:19 -04:00
422167f7aa Add indication of followers you know in a profile
Changelog-Added: Add indication of followers you know in a profile
2023-06-25 09:38:57 +02:00
William Casarin
de84456a57 mediaurl: fix is_img returning true for videos
This makes the image preloader try to download videos... not good.
2023-06-25 09:38:46 +02:00
William Casarin
b70bf1f647 v1.5-6 2023-06-25 09:38:46 +02:00
William Casarin
3db77a16a0 Fix timeline from moving when you're scrolling 2023-06-24 17:39:24 +02:00
William Casarin
2256e2e625 v1.5-5 changelog 2023-06-24 08:39:51 +02:00
William Casarin
a641f972ff v1.5-5 2023-06-24 08:38:02 +02:00
William Casarin
c5846008f2 fix weird quirk with universe toolbar filter button 2023-06-24 08:27:12 +02:00
William Casarin
62bf767be5 nozaps: fix some tests 2023-06-23 20:46:27 +02:00
William Casarin
c00746758e nozaps: disable donation star unless you're on freedom edition 2023-06-23 20:46:27 +02:00
William Casarin
a89f90d7ee nozaps: hide SupportDamus on appstore builds 2023-06-23 20:46:27 +02:00
William Casarin
07d0818ee8 nozaps: disable zap delay on appstore since we don't have 1-tap anymore 2023-06-23 20:46:27 +02:00
William Casarin
0ce7414488 nozaps: disable donation zaps on appstore 2023-06-23 20:46:27 +02:00
William Casarin
f090596067 nozaps: switch to global sheet when zapping
This fixes many popping bugs

Changelog-Fixed: Fix zap sheet popping
2023-06-23 20:46:27 +02:00
William Casarin
61b3ad2990 nozaps: update zap sheet style to make it clear its a user zap 2023-06-23 20:46:27 +02:00
William Casarin
8b24befaf7 nozaps: don't include zaps in thread replies 2023-06-23 20:46:27 +02:00
William Casarin
57789de5cd nozaps: hide zap total 2023-06-23 20:46:27 +02:00
William Casarin
62c539afbf nozaps: never show orange button 2023-06-23 20:46:27 +02:00
William Casarin
b53e6db96b nozaps: show the user you are zapping in CustomizeZapView
This should make it clear that it's definitely not a note zap
2023-06-23 20:46:27 +02:00
William Casarin
b0d6d33573 nozaps: hide zap details on notes for now 2023-06-23 20:46:27 +02:00
William Casarin
c5b0e539d8 nozaps: don't show note zaps in notifications
apple sucks
2023-06-23 20:46:27 +02:00
William Casarin
601fa49a6e nozaps: don't show top zaps or zap replies
We can't associate zaps with notes anymore
2023-06-23 20:46:27 +02:00
William Casarin
216029410b nozaps: add nozaps setting
This will be used to restore functionality in the future
2023-06-23 20:46:27 +02:00
William Casarin
a5b2a5c8b9 tests: disable invalid tests 2023-06-23 20:46:27 +02:00
William Casarin
980394bf0b Revert "ui: remove nip05 badge on events"
This reverts commit d205be3e0a.
2023-06-23 20:46:17 +02:00
William Casarin
ed73899e5b Revert "Initial actionbar model refactor"
This reverts commit d0eb86dfa3.
2023-06-23 12:04:54 +02:00
William Casarin
d0eb86dfa3 Initial actionbar model refactor 2023-06-23 11:53:53 +02:00
William Casarin
337c4de337 reduce ContentView redraws
Remove observability from the home model, and use inner models for
updating specific parts of the UI, such as notification dots on the tab
bar.
2023-06-23 11:51:51 +02:00
William Casarin
e885f38c54 refactor: switch CustomizeZapView to use a model
Changelog-Fixed: Fix CustomizeZapView from randomly disappearing
2023-06-23 11:51:51 +02:00
William Casarin
3dbdc42d8b view/refactor: remove extra spacer 2023-06-23 11:46:42 +02:00
William Casarin
1389e50b8e fix some warnings 2023-06-23 11:46:42 +02:00
William Casarin
092d84f499 eventholder: don't push view updates on queued events 2023-06-23 11:46:42 +02:00
William Casarin
c6a226fff8 Revert "ping: switch to async style"
This was causing crashes =/
2023-06-23 11:46:42 +02:00
William Casarin
e023d1e9cb settings: turn off wallet selector by default
This is a bit confusing for new users
2023-06-23 11:46:42 +02:00
William Casarin
e6b8e39106 actionbar: rename accessibility label boosts to reposts 2023-06-23 11:46:42 +02:00
William Casarin
ced028755c debug: InnerTimeline render counts 2023-06-23 11:46:42 +02:00
William Casarin
dabf737654 Merge remote-tracking branch 'github/translations' 2023-06-22 10:25:14 +02:00
William Casarin
72d141af61 wording: send a "message" with the zap, not "reply" 2023-06-22 10:23:56 +02:00
William Casarin
4d43e590e0 view: Add ZapUserView
This will be used to make it clear that we are zapping a user
2023-06-22 10:23:56 +02:00
William Casarin
c413589582 eventgroup: add is_note_zap
We'll need this to hide note zaps as per apple's insanely dumb
guidelines.
2023-06-22 10:23:56 +02:00
William Casarin
c218e0dcdd refactor: use zap.is_anon instead of recomputing
I believe this code was written before we computed is_anon inside the
zap.
2023-06-22 10:23:56 +02:00
William Casarin
892765eaa5 UserView: Make spacer optional
We need this to center this view sometimes. We should look into removing
this in the future?
2023-06-22 10:23:56 +02:00
William Casarin
455f1f7e1f view/refactor: remove redundant view structs 2023-06-22 10:23:56 +02:00
William Casarin
797762e7d2 view/refactor: move sheet handler in CustomizeZapView 2023-06-22 10:23:56 +02:00
transifex-integration[bot]
efe6689bfb Translate Localizable.stringsdict in ja
100% translated source file: 'Localizable.stringsdict'
on 'ja'.
2023-06-22 01:14:40 +00:00
transifex-integration[bot]
e30541c37e Translate Localizable.strings in ja
100% translated source file: 'Localizable.strings'
on 'ja'.
2023-06-22 01:12:38 +00:00
transifex-integration[bot]
b126257d05 Translate Localizable.stringsdict in es_ES
100% translated source file: 'Localizable.stringsdict'
on 'es_ES'.
2023-06-21 23:22:27 +00:00
transifex-integration[bot]
fde21559c7 Translate Localizable.strings in es_ES
100% translated source file: 'Localizable.strings'
on 'es_ES'.
2023-06-21 23:15:17 +00:00
transifex-integration[bot]
d551b5f28b Translate Localizable.strings in es_ES
100% translated source file: 'Localizable.strings'
on 'es_ES'.
2023-06-21 23:14:48 +00:00
transifex-integration[bot]
961ff6f28b Translate Localizable.strings in fr
100% translated source file: 'Localizable.strings'
on 'fr'.
2023-06-21 15:20:57 +00:00
transifex-integration[bot]
fdfd0f0275 Translate Localizable.strings in nl
100% translated source file: 'Localizable.strings'
on 'nl'.
2023-06-21 15:16:19 +00:00
transifex-integration[bot]
8b1b597f2a Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-21 15:16:05 +00:00
transifex-integration[bot]
f3de41ff08 Translate Localizable.stringsdict in vi
100% translated source file: 'Localizable.stringsdict'
on 'vi'.
2023-06-21 14:33:19 +00:00
transifex-integration[bot]
bf010be27a Translate Localizable.strings in vi
100% translated source file: 'Localizable.strings'
on 'vi'.
2023-06-21 14:32:11 +00:00
transifex-integration[bot]
b013c1f1fd Translate Localizable.stringsdict in zh_HK
100% translated source file: 'Localizable.stringsdict'
on 'zh_HK'.
2023-06-21 12:37:01 +00:00
transifex-integration[bot]
50dfa9e2ed Translate Localizable.stringsdict in zh_TW
100% translated source file: 'Localizable.stringsdict'
on 'zh_TW'.
2023-06-21 12:36:51 +00:00
transifex-integration[bot]
78450792cb Translate Localizable.strings in zh_TW
100% translated source file: 'Localizable.strings'
on 'zh_TW'.
2023-06-21 12:36:17 +00:00
transifex-integration[bot]
4dc2571177 Translate Localizable.strings in zh_HK
100% translated source file: 'Localizable.strings'
on 'zh_HK'.
2023-06-21 12:35:58 +00:00
transifex-integration[bot]
04493b53dc Translate Localizable.strings in zh_CN
100% translated source file: 'Localizable.strings'
on 'zh_CN'.
2023-06-21 12:35:14 +00:00
transifex-integration[bot]
f383388f42 Translate Localizable.stringsdict in zh_CN
100% translated source file: 'Localizable.stringsdict'
on 'zh_CN'.
2023-06-21 12:34:30 +00:00
transifex-integration[bot]
d4cdc7706d Translate Localizable.stringsdict in fr
100% translated source file: 'Localizable.stringsdict'
on 'fr'.
2023-06-21 11:57:54 +00:00
b70406d669 Fix "zapped your profile" strings to say "zapped you"
Changelog-Fixed: Fix "zapped your profile" strings to say "zapped you"
2023-06-21 07:35:16 -04:00
William Casarin
a2866ff6b3 Merge remote-tracking branch 'github/translations' 2023-06-21 10:19:47 +02:00
transifex-integration[bot]
1f0e31faa0 Translate Localizable.strings in hu_HU
100% translated source file: 'Localizable.strings'
on 'hu_HU'.
2023-06-21 08:14:48 +00:00
William Casarin
2ff12cdfa6 video: switch videoplayer to use detached tasks 2023-06-20 17:00:48 +02:00
William Casarin
b7b7d65612 perf: don't use string concat when calculing SeenEven hash
Profiler was complaining about this one
2023-06-20 17:00:48 +02:00
William Casarin
d205be3e0a ui: remove nip05 badge on events
Changelog-Changed: Remove nip05 on events
2023-06-20 17:00:48 +02:00
William Casarin
0bea81c632 perf: move blurhash processing to a background task
Using Task directly will only inherent the parent thread. We need
detached to do heaving processing.
2023-06-20 17:00:48 +02:00
William Casarin
e4842cca3c logs: don't print filters
It can be slow
2023-06-20 17:00:48 +02:00
William Casarin
87d4752aa4 ui: use AboutView in existing views 2023-06-20 17:00:48 +02:00
William Casarin
6ec533b0cd view: Add AboutView
This will be used by different views for the user's about section
2023-06-20 17:00:48 +02:00
William Casarin
51a58360f9 debug slow scroll 2023-06-20 15:29:44 +02:00
William Casarin
fe025532e8 Merge remote-tracking branch 'github/translations' 2023-06-20 11:27:29 +02:00
William Casarin
6eb548a0a9 Fix reconnect loop issues on iOS17
Changelog-Fixed: Fix reconnect loop issues on iOS17
2023-06-20 11:21:21 +02:00
William Casarin
bcaa1d2354 ping: switch to async style
because reasons
2023-06-20 11:21:07 +02:00
Bryan Montz
296d96d6df rename RelayStatus to RelayStatusView 2023-06-20 10:18:34 +02:00
Bryan Montz
28854fdc93 simplify and fix issues with RelayStatus 2023-06-20 10:18:34 +02:00
Bryan Montz
2901cc860f make RelayConnection's state observable 2023-06-20 10:18:34 +02:00
William Casarin
3db13ae171 Fix build for iOS17 2023-06-20 10:18:23 +02:00
transifex-integration[bot]
49dedaec04 Translate Localizable.strings in sv_SE
100% translated source file: 'Localizable.strings'
on 'sv_SE'.
2023-06-20 07:50:51 +00:00
transifex-integration[bot]
491d4c4d25 Translate Localizable.strings in sv_SE
100% translated source file: 'Localizable.strings'
on 'sv_SE'.
2023-06-20 07:49:38 +00:00
transifex-integration[bot]
afcbaea331 Translate Localizable.strings in zh_HK
100% translated source file: 'Localizable.strings'
on 'zh_HK'.
2023-06-19 10:21:11 +00:00
transifex-integration[bot]
340e134046 Translate Localizable.strings in zh_TW
100% translated source file: 'Localizable.strings'
on 'zh_TW'.
2023-06-19 10:21:08 +00:00
transifex-integration[bot]
e68952fa0c Translate Localizable.strings in zh_CN
100% translated source file: 'Localizable.strings'
on 'zh_CN'.
2023-06-19 10:20:07 +00:00
transifex-integration[bot]
83af4ddd89 Translate Localizable.strings in zh_CN
100% translated source file: 'Localizable.strings'
on 'zh_CN'.
2023-06-19 10:19:29 +00:00
transifex-integration[bot]
53262afa01 Translate Localizable.strings in nl
100% translated source file: 'Localizable.strings'
on 'nl'.
2023-06-19 08:39:13 +00:00
transifex-integration[bot]
95148e9c9c Translate Localizable.strings in cs
100% translated source file: 'Localizable.strings'
on 'cs'.
2023-06-19 08:04:14 +00:00
transifex-integration[bot]
5bf4a2cc5a Translate Localizable.strings in ja
100% translated source file: 'Localizable.strings'
on 'ja'.
2023-06-19 07:30:18 +00:00
2d29403145 Remove string referencing tipping posts, add missing localized string comments, and export strings for translation 2023-06-19 00:39:35 -04:00
gladiusKatana
df20b67fc1 video: stop video in post when it disappears from view
Changelog-Fixes: Stop video when it disappears from view
2023-06-16 18:34:14 +02:00
William Casarin
0b5d68c0b8 Revert "threads: attempt to fix state jankiness"
This reverts commit f4024895ba.
2023-06-14 09:30:58 +02:00
William Casarin
f4024895ba threads: attempt to fix state jankiness
Changelog-Fixed: Fix some more thread jankiness
2023-06-14 09:20:51 +02:00
William Casarin
bcdd0b4e23 Revert "Add Inter font"
This reverts commit 271e3ad54a.
2023-06-12 11:25:27 +02:00
William Casarin
1c655d47b2 home: add comment explaining send_home_filters 2023-06-10 15:09:43 +02:00
William Casarin
24cc361d60 refactor: make send_initial_filter more readible 2023-06-10 15:09:43 +02:00
Bryan Montz
71bb9d6c92 Add text description to WebSocket for state logging 2023-06-10 12:39:25 +02:00
Ben Weeks
271e3ad54a Add Inter font
ChangeLog-Changed: Switch to new font (Inter)
2023-06-10 12:38:13 +02:00
transifex-integration[bot]
dac21a1562 Update Translations
Translate Localizable.strings in cs
Translate Localizable.strings in el_GR
Translate Localizable.strings in nl
Translate Localizable.strings in sv_SE
Translate Localizable.strings in zh_CN
Translate Localizable.strings in zh_HK
Translate Localizable.strings in zh_TW
Translate Localizable.stringsdict in el_GR
Translate Localizable.stringsdict in nl
Translate Localizable.stringsdict in sv_SE
Translate Localizable.stringsdict in zh_CN
Translate Localizable.stringsdict in zh_HK
Translate Localizable.stringsdict in zh_TW

Closes: #1262
2023-06-10 11:43:32 +02:00
ae9ae66b39 Fix spelling of Nostr to use Titlecase instead of lowercase
Changelog-Fixed: Fix spelling of Nostr to use Titlecase instead of lowercase
2023-06-10 11:42:25 +02:00
e7281fdacc Add missing localized string comment 2023-06-10 11:42:25 +02:00
baa5454e2a Rename all usages of the term Post as a noun to Note to conform to the Nostr spec
Changelog-Fixed: Rename all usages of the term Post as a noun to Note to conform to the Nostr spec
2023-06-10 11:42:25 +02:00
gladiusKatana
60a892d73b LoginView: prevent explainer-text cutoff on login with npub
Changelog-Fixed: Fix text cutoff on login with npub
Closes: #1264
2023-06-10 11:39:00 +02:00
William Casarin
0ee360f2fa Fix video player hangs
Changelog-Fixed: Fix hangs due to video player
2023-06-10 11:33:35 +02:00
William Casarin
c59d2a96af Make profile picture placeholder gray instead of purple 2023-06-10 11:33:18 +02:00
William Casarin
ba3a6b07b2 Fix warnings 2023-06-10 11:33:07 +02:00
William Casarin
043eb5b436 Show zap comments in threads and show top zap
Changelog-Added: Top zaps
Changelog-Added: Show zap comments in threads
2023-06-09 10:11:25 +02:00
William Casarin
8f237b47eb qrscan: use explicit types when scanning 2023-06-07 08:06:06 +02:00
William Casarin
a0caf9ce07 find_event: refactor with more explicit types 2023-06-07 08:06:06 +02:00
William Casarin
3277aac220 refactor: use guard in qr profile lookup 2023-06-07 08:06:03 +02:00
William Casarin
e67dac13c6 refactor: use guard in handleProfileScan 2023-06-07 08:06:03 +02:00
Suhail Saqan
5f2c8223bd Add qr code scanner
Changelog-Added: Add qr code scanner
Closes: #733
2023-06-07 08:05:27 +02:00
14977fe3dd Replace indexed mentions with NIP-27
Changelog-Fixed: Replace indexed mentions with NIP-27
Closes: #1213
2023-06-07 06:22:38 +02:00
William Casarin
1d3c181b85 Translations
Translate Localizable.strings in ja
Translate Localizable.strings in de
Translate Localizable.strings in vi
Translate Localizable.strings in zh_TW
Translate Localizable.strings in zh_HK
Translate Localizable.strings in zh_CN
Translate Localizable.strings in ru
Localizable.strings in cs

Closes: #1253
2023-06-07 06:17:30 +02:00
8ca377bec9 Add max length truncation to displayed profile attributes to mitigate spam
Changelog-Fixed: Add max length truncation to displayed profile attributes to mitigate spam
Fixes: #1237
2023-06-04 17:50:52 -04:00
952d6746d5 Add profile zaps
Refactor profile zaps to reuse same BOLT11 Lightning invoice logic as
note zaps, which fixes profile zaps from Cash App and Muun wallets

Changelog-Added: Add profile zaps
Changelog-Fixed: Fix profile zapping for Muun and Strike wallets
Closes: #1236
Fixes: #1067
2023-06-04 10:56:43 -07:00
Suhail Saqan
b3b335f917 Add NWC paste button
Changelog-Added: Added Wallet Connect paste button
Closes: #1235
2023-06-04 10:56:43 -07:00
dde48132c9 Fix CI tests
Closes: #1204
2023-05-31 16:40:59 -07:00
William Casarin
809a08ef63 v1.5 (3) 2023-05-30 19:40:37 -07:00
William Casarin
57e6f083b8 Revert "Updated UI to use custom font"
This reverts commit 020a00bf7e.
2023-05-30 19:35:21 -07:00
William Casarin
cfa1e13887 Revert "Add .frame & .position modifiers to TextEntry using ScrollView geometry"
This reverts commit f7a0370824.
2023-05-30 19:29:23 -07:00
William Casarin
6eecb5ef26 video-player: don't stop audio, mix with external media 2023-05-30 18:44:36 -07:00
William Casarin
5dc3e2635e video-player: don't randomly stop video 2023-05-30 18:38:04 -07:00
William Casarin
2713e76e17 v1.5-2 changelog 2023-05-30 18:08:57 -07:00
William Casarin
6aa28fce6c v1.5-2 2023-05-30 18:04:21 -07:00
William Casarin
a6fb175b98 Add Full-Bleed Video Player
Changelog-Added: Add new full-bleed video player
2023-05-30 18:02:19 -07:00
William Casarin
554c091d57 video-player: hide mute button when we have no audio 2023-05-30 17:58:48 -07:00
William Casarin
9e359650bf carousel: fix image positioning 2023-05-30 17:58:48 -07:00
William Casarin
bb091d072f cache: move event-specific media metadata to EventCache 2023-05-30 17:58:48 -07:00
William Casarin
88b04fde09 xcode: remove .git ext from package 2023-05-30 17:58:48 -07:00
William Casarin
a1753b2c24 video-player: add tap gesture to prevent nav 2023-05-30 17:58:48 -07:00
William Casarin
80fac1903e carousel: switch to media carousel and include video 2023-05-30 09:36:39 -07:00
William Casarin
6214ab8d8f video: add DamusVideoPlayer view 2023-05-29 17:11:14 -07:00
William Casarin
85cd1bea19 urls: combine url classification 2023-05-29 17:11:13 -07:00
William Casarin
4d95d36a1e Add GSPlayer + VideoPlayer 2023-05-29 17:10:14 -07:00
William Casarin
185fba150f carousel: fix scroll-up bug 2023-05-29 16:09:16 -07:00
Ben Weeks
06ba0f7387 Add ability to show multiple posts per user in Universe
ChangeLog-Added: Add ability to show multiple posts per user in Universe
Closes: #1198
Fixes: #1189
2023-05-29 15:35:29 -07:00
William Casarin
51c4fa1e32 Initial redesign
Changelog-Changed: Redesign phase 1 (text, icons)
2023-05-29 15:31:07 -07:00
Ben Weeks
ed4ef0e215 Tweaked action bar icon sizes. 2023-05-29 14:47:10 -07:00
Ben Weeks
973e9fe714 Custom iconography added for other areas of the app.
Changelog-Added: Custom iconography added for other areas of the app.
2023-05-29 14:47:05 -07:00
Ben Weeks
d12281fcc5 Custom iconography for the left navigation.
ChangeLog-Added: Custom iconography for the left navigation.
2023-05-29 14:46:59 -07:00
Ben Weeks
156d885e6e Custom iconography for the tab buttons.
ChangeLog-Added: Custom iconography for the tab buttons.
2023-05-29 14:46:52 -07:00
ericholguin
bf01d08ea0 setupview: remove damus white button implementation 2023-05-29 14:38:24 -07:00
ericholguin
548d8e49ec createaccount: ui: modify text field look 2023-05-29 14:38:24 -07:00
ericholguin
0fc38e5c05 createaccount: ui: change color of copy icon 2023-05-29 14:38:24 -07:00
ericholguin
028cab9cf6 createaccount: ui: replace damus white buttons with gradient style buttons 2023-05-29 14:38:24 -07:00
ericholguin
29068a40c8 createaccount: ui: remove damus gradient and white font color add background 2023-05-29 14:38:24 -07:00
ericholguin
ff06cfaf0c createaccount: ui: refactor signup form 2023-05-29 14:38:24 -07:00
ericholguin
eb72fb481c createaccount: remove username from the form 2023-05-29 14:38:24 -07:00
ericholguin
5b7339a0de createaccount: ui: move key text and profile picture selection into frame 2023-05-29 14:38:24 -07:00
ericholguin
06ba72a23d createaccount: ui: remove create account text 2023-05-29 14:38:24 -07:00
ericholguin
47d594af00 createaccount: ui: center pubkey text and add gradient style 2023-05-29 14:38:24 -07:00
ericholguin
dd022a953c createaccount: ui: add navigation title and use general padding 2023-05-29 14:38:24 -07:00
ericholguin
a4cbf6a12a createaccount: ui: include login prompt and add padding 2023-05-29 14:38:24 -07:00
ericholguin
62dfc24e58 createaccount: ui: change the look of the form label 2023-05-29 14:38:24 -07:00
ericholguin
cf15bd3463 createaccount: ui: change the look of the form text field 2023-05-29 14:38:24 -07:00
ericholguin
fd44a56f9b createaccount: ui: remove fixed color from back nav 2023-05-29 14:38:24 -07:00
ericholguin
9a9fb28a48 createaccount: ui: add login prompt view struct 2023-05-29 14:38:24 -07:00
ericholguin
ba7f675300 createaccount: ui: remove damus white button and use gradient style button 2023-05-29 14:38:24 -07:00
ericholguin
8e84b446a5 createaccount: ui: remove damus gradient 2023-05-29 14:38:24 -07:00
ericholguin
8dbe2c728f loginview: ui: update login preview 2023-05-29 14:38:24 -07:00
ericholguin
f975723c0f loginview: ui: include create account prompt view 2023-05-29 14:38:24 -07:00
ericholguin
8ed04f0b0e loginview: ui: add orange color and bold font to pubkey message 2023-05-29 14:38:24 -07:00
ericholguin
e33cb3b1c3 loginview: ui: replace damus white button with gradient style button 2023-05-29 14:38:24 -07:00
ericholguin
f171cfffe7 loginview: functionality: removed the toggle for hex npub - NEEDS UPDATING 2023-05-29 14:38:24 -07:00
ericholguin
46556c8bdc loginview: ui: replace existing views with new sign in header and entry views 2023-05-29 14:38:24 -07:00
ericholguin
10fec3cbf8 loginview: add create account prompt view struct 2023-05-29 14:38:24 -07:00
ericholguin
7250cf0d8f loginview: add sign in entry view struct 2023-05-29 14:38:24 -07:00
ericholguin
2bddd5ce92 loginview: add sign in header view struct 2023-05-29 14:38:24 -07:00
ericholguin
027b39caed loginview: ui: fix up key input textfield appearance 2023-05-29 14:38:24 -07:00
ericholguin
122d0e451e loginview: ui: replace damus gradient with new login background 2023-05-29 14:38:24 -07:00
ericholguin
f99e311e58 eulaview: ui: use new eula background 2023-05-29 14:38:24 -07:00
ericholguin
3fbf3cc12e eulaview: ui: replace damus white buttons with new gradient style buttons 2023-05-29 14:38:24 -07:00
ericholguin
6682f792d8 eulaview: fix up formatting and use new eula variable 2023-05-29 14:38:24 -07:00
ericholguin
2e53ce3905 eulaview: create eula variable and update headers to bold 2023-05-29 14:38:24 -07:00
ericholguin
c238b6e28f setupview: ui: remove old title, buttons, carousel and use new welcome, views, and lets get started button 2023-05-29 14:38:24 -07:00
ericholguin
e99142a5b8 setupview: remove damus gradient and use new login background 2023-05-29 14:38:24 -07:00
ericholguin
5716578c20 setupview: add why we need nostr view 2023-05-29 14:38:24 -07:00
ericholguin
ec35a413fe setupview: add what is nostr view 2023-05-29 14:38:24 -07:00
ericholguin
724773cb45 setupview: add learn about nostr link view 2023-05-29 14:38:24 -07:00
ericholguin
15acdef912 navigation: remove SetupState and references and update navigation links for onboarding flow 2023-05-29 14:38:24 -07:00
ericholguin
72cc4c1fd7 assets: added nostr logo image used in setup view 2023-05-29 14:38:24 -07:00
ericholguin
65f4227e9b assets: added lightbulb image used in setup view 2023-05-29 14:38:24 -07:00
ericholguin
f0be5a5ad8 assets: added login header background 2023-05-29 14:38:24 -07:00
ericholguin
48855d5d41 assets: added background for the EULA 2023-05-29 14:38:24 -07:00
ericholguin
90c22fdabd components: added reusable gradient button style 2023-05-29 14:38:24 -07:00
ericholguin
dfd1032cd8 gradients: add pink gradient 2023-05-29 14:38:24 -07:00
ericholguin
a0e1e16f17 gradients: add damus logo gradient 2023-05-29 14:38:24 -07:00
Ben Weeks
020a00bf7e Updated UI to use custom font
Changelog-Changed: Updated UI to use custom font
2023-05-29 14:38:24 -07:00
William Casarin
297aaf86c6 carousel: only show blurhash when there is one images
Blurhash interacts strangely with carousel
2023-05-29 14:31:54 -07:00
William Casarin
f6dd060580 fixup! Add new image carousel 2023-05-29 14:31:16 -07:00
William Casarin
12428d01ad preloader: only preload non-animated images to test animation bug fix 2023-05-29 14:22:54 -07:00
Ben Weeks
d091543448 Add new image carousel
Changelog-Added: Added dots under image carousel
Changelof-Fixed: Fixed carousel issues with multiple images of different aspect ratios
Closes: #1004
2023-05-29 14:22:50 -07:00
Ben Weeks
2e596a47a1 Fix event menu capitalization
Closes: #1191
2023-05-29 13:23:24 -07:00
OlegAba
ff15156297 Fix side menu offset
Changelog-Fixed: Fix side menu bug in landscape
Closes: #560
2023-05-29 13:17:05 -07:00
Ben Weeks
0a9fbf5031 Removed hex key to RGB functionality. 2023-05-29 12:42:58 -07:00
Ben Weeks
8eebc2abe5 Minor changes to profile ready for rebrand Key removal from nPub in profile 2023-05-29 12:42:58 -07:00
Ben Weeks
c22199165a Use "Follow me on nostr" text when looking at someone else's QR code
Fixes: #1069
Closes: #1199
Changelog-Fixed: Use "Follow me on nostr" text when looking at someone else's QR code
2023-05-29 12:27:07 -07:00
gladiusKatana
f7a0370824 Add .frame & .position modifiers to TextEntry using ScrollView geometry
Closes: #1201
Changelog-Fixed: Fix issue where cursor dissapears when typing long message
2023-05-29 12:22:44 -07:00
Bryan Montz
fe4277e817 simplified call site usage of NostrFilter 2023-05-29 12:19:27 -07:00
William Casarin
6804fbb607 Attempt fix for randomly broken animated GIFs
Changelog-Fixed: Attempt fix for randomly broken animated gifs
2023-05-29 10:18:52 -07:00
William Casarin
05503024cc Profile Caching
Changelog-Added: Add profile caching
2023-05-26 12:11:43 -07:00
gladius
e4e477a2ac Fix cursor jumping when pressing return
Changelog-Fixed: Fix cursor jumping when pressing return
Closes: #775
2023-05-26 11:20:40 -07:00
59e7a42b5f Fix side menu label size so that translations in longer languages fit without wrapping
Changelog-Fixed: Fix side menu label size so that translations in longer languages fit without wrapping
Closes: #1175
2023-05-26 10:35:25 -07:00
0552c24108 Add mention parsing and fine-grained text selection on description in ProfileView
Changelog-Added: Add mention parsing and fine-grained text selection on description in ProfileView
Closes: #1172
2023-05-26 10:35:18 -07:00
Bryan Montz
61303f49ad fix zap test 2023-05-26 07:32:18 -05:00
Bryan Montz
e10dc93233 add damus_donation to profile data model and update tests 2023-05-26 07:02:14 -05:00
Bryan Montz
ea73c5252d Merge branch 'master' into user-cache 2023-05-26 06:46:47 -05:00
Bryan Montz
7e963c9025 synchronize access to network_pull_date_cache with a queue, writes with barrier flag 2023-05-26 06:38:20 -05:00
Bryan Montz
61ff7da2ae make database write async at the call site and update tests 2023-05-25 09:09:50 -05:00
Bryan Montz
7259641e26 speed optimizations: cache network_pull_date in memory and ensure writes on background queue 2023-05-25 08:20:41 -05:00
gladiusKatana
f9a572faa2 dynamically set .isScrollEnabled in TextViewWrapper (true if UserSearch is present) 2023-05-24 20:41:03 -07:00
gladiusKatana
0f805d7ea7 override .isScrollEnabled in TextViewWrapper (ie, set to false at UITextView creation) 2023-05-24 20:41:03 -07:00
William Casarin
52ca33ef6a script: fetch popular users 2023-05-24 18:26:18 -07:00
1769b08147 Fix reaction notification title to be consistent with ReactionView
Changelog-Fixed: Fix reaction notification title to be consistent with ReactionView
Closes: #1137
2023-05-24 15:20:02 -07:00
Bryan Montz
812213ff2b Add Damus splash screen
Changelog-Updated: Add Damus splash screen
2023-05-24 15:17:08 -07:00
transifex-integration[bot]
d6ecf14b55 Apply translations
Closes: #1134
2023-05-24 15:16:45 -07:00
William Casarin
47a74257c8 nwc debugging 2023-05-24 15:16:45 -07:00
William Casarin
1b161fefd0 nwc: make sure to support nostr+walletconnect scheme
not sure why we have 2 schemes
2023-05-15 12:54:42 -07:00
William Casarin
0b9a274e67 postbox: change initial retry_after from 2 to 10 seconds 2023-05-15 12:54:09 -07:00
William Casarin
2bbbb5db65 Fix a few bugs with donations 2023-05-15 12:53:36 -07:00
William Casarin
bffa42a13a Supporter Badges 2023-05-15 11:57:37 -07:00
Bryan Montz
91113fbc6d allow models to fetch profiles when they get stale 2023-05-15 08:33:35 -05:00
Bryan Montz
d58a1e0ba3 add ability to check the freshness of a PersistedProfile 2023-05-15 08:31:49 -05:00
Bryan Montz
3f7b0a4d6e add network pull date to PersistedProfile model for staleness checking 2023-05-15 08:29:06 -05:00
Bryan Montz
bc315dd571 remove identical function 2023-05-15 07:33:49 -05:00
Bryan Montz
32431096f5 add tests for ProfileDatabase 2023-05-13 09:20:01 -05:00
Bryan Montz
6172347455 add ability to remove all cached Profiles in ProfileDatabase 2023-05-13 09:18:47 -05:00
Bryan Montz
31d327a085 add ability to retrieve count of cached Profiles 2023-05-13 09:18:23 -05:00
Bryan Montz
b5ae7df795 add ability to change ProfileDatabase's storage URL 2023-05-13 09:17:41 -05:00
Bryan Montz
3b0bb48dd4 integrate ProfileDatabase with existing Profiles caching class 2023-05-12 07:21:59 -05:00
Bryan Montz
4646f0e23c add ProfileDatabase class to read and write profiles to disk 2023-05-12 07:21:25 -05:00
Bryan Montz
7027b7016c add two-way translation between existing Profile class and new PersistedProfile CoreData class 2023-05-12 07:19:43 -05:00
Bryan Montz
76c57af548 add managed object class to match new Profile CoreData model 2023-05-12 07:18:12 -05:00
Bryan Montz
7983157c38 add CoreData model for Profile 2023-05-12 07:15:30 -05:00
621 changed files with 78027 additions and 8320 deletions

2
.envrc
View File

@@ -1,4 +1,4 @@
#use nix
use nix
export TODO_FILE=$PWD/TODO

View File

@@ -5,6 +5,7 @@ on:
push:
branches:
- "master"
- "ci"
pull_request:
branches:
- "*"

3
.gitignore vendored
View File

@@ -1,5 +1,8 @@
xcuserdata
/.direnv
damus/TestingPrivate.swift
damus.xcodeproj/xcshareddata/xcbaselines
.DS_Store
TODO.bak
tags
build-git-hash.txt

6
.mailmap Normal file
View File

@@ -0,0 +1,6 @@
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>
Daniel D'Aquino <daniel@daquino.me> <patches@damus.io>

View File

@@ -1,3 +1,332 @@
## [1.6-18] - 2023-09-21
### Added
- Add followed hashtags to your following list (Daniel DAquino)
- Add "Do not show #nsfw tagged posts" setting (Daniel DAquino)
- Hold tap to preview status URL (Jericho Hasselbush)
- Finnish translations (etrikaj)
### Changed
- Switch to nostrdb for @'s and user search (William Casarin)
- Use nostrdb for profiles (William Casarin)
- Updated relay view (ericholguin)
- Increase size of the hitbox on note ellipsis button (Daniel DAquino)
- Make carousel tab dots tappable (Bryan Montz)
- Move the "Follow you" badge into the profile header (Grimless)
### Fixed
- Fix text composer wrapping issue when mentioning npub (Daniel DAquino)
- Make blurred videos viewable by allowing blur to disappear once tapped (Daniel DAquino)
- Fix parsing issue with NIP-47 compliant NWC urls without double-slashes (Daniel DAquino)
- Fix padding of username next to pfp on some views (William Casarin)
- Fixes issue where username with multiple emojis would place cursor in strange position. (Jericho Hasselbush)
- Fixed audio in video playing twice (Bryan Montz)
- Fix crash when long pressing custom reactions (William Casarin)
- Fix random crashom due to old profile database (William Casarin)
[1.6-18]: https://github.com/damus-io/damus/releases/tag/v1.6-18
## [1.6-17] - 2023-08-23
### Added
- Add support for status URLs (William Casarin)
- Click music statuses to display in spotify (William Casarin)
- Add settings for disabling user statuses (William Casarin)
### Changed
- clear statuses if they only contain whitespace (William Casarin)
### Fixed
- Fix long status lines (William Casarin)
- Fix status events not expiring locally (William Casarin)
[1.6-17]: https://github.com/damus-io/damus/releases/tag/v1.6-17
## [1.6-16] - 2023-08-23
### Added
- Added live music statuses (William Casarin)
- Added generic user statuses (William Casarin)
### Fixed
- Avoid notification for zaps from muted profiles (tappu75e@duck.com)
- Fix text editing issues on characters added right after mention link (Daniel DAquino)
- Mute hellthreads everywhere (William Casarin)
[1.6-16]: https://github.com/damus-io/damus/releases/tag/v1.6-16
## [1.6-13] - 2023-08-18
### Fixed
- Fix bug where it would sometimes show -1 in replies (tappu75e@duck.com)
- Fix images and links occasionally appearing with escaped slashes (Daniel DAquino)
- Fixed nostrscript not working on smaller phones (William Casarin)
- Fix zaps sometimes not appearing (William Casarin)
- Fixed issue where reposts would sometimes repost the wrong thing (William Casarin)
- Fixed issue where sometimes there would be empty entries on your profile (William Casarin)
[1.6-13]: https://github.com/damus-io/damus/releases/tag/v1.6-13
## [1.6-11]: "Bugfix Sunday" - 2023-08-07
### Added
- Add close button to custom reactions (Suhail Saqan)
- Add ability to change order of custom reactions (Suhail Saqan)
- Adjustable font size (William Casarin)
### Changed
- Show renotes in Notes timeline (William Casarin)
### Fixed
- Ensure the person you're replying to is the first entry in the reply description (William Casarin)
- Don't cutoff text in notifications (William Casarin)
- Fix wikipedia url detection with parenthesis (William Casarin)
- Fixed old notifications always appearing on first start (William Casarin)
- Fix issue with slashes on relay urls causing relay connection problems (William Casarin)
- Fix rare crash triggered by local notifications (William Casarin)
- Fix crash when long-pressing reactions (William Casarin)
- Fixed nostr reporting decoding (William Casarin)
- Dismiss qr screen on scan (Suhail Saqan)
- Show QRCameraView regardless of same user (Suhail Saqan)
- Fix wiggle when long press reactions (Suhail Saqan)
- Fix reaction button breaking scrolling (Suhail Saqan)
- Fix crash when muting threads (Bryan Montz)
[1.6-11]: https://github.com/damus-io/damus/releases/tag/v1.6-11
## [1.6-8]: "nostrdb prep" 2023-08-03
### Added
- Suggested Users to Follow (Joel Klabo)
- Add support for multiple reactions (Suhail Saqan)
### Changed
- Improved memory usage and performance when processing events (William Casarin)
### Fixed
- Fixed disappearing text on iOS17 (cr0bar)
- Fix UTF support for hashtags (Daniel DAquino)
- Fix compilation error on test target in UserSearchCacheTests (Daniel DAquino)
- Fix nav crashing and buggyness (William Casarin)
- Allow relay logs to be opened in dev mode even if relay (Daniel D'Aquino)
- endless connection attempt loop after user removes relay (Bryan Montz)
[1.6-8]: https://github.com/damus-io/damus/releases/tag/v1.6-8
## 1.6 (7): "Less bad" - 2023-07-16
### Added
- Show nostr address username and support abbreviated _ usernames (William Casarin)
- Re-add nip05 badges to profiles (William Casarin)
- Add space when tagging users in posts if needed (William Casarin)
- Added padding under word count on longform account (William Casarin)
### Fixed
- Don't spam lnurls when validating zaps (William Casarin)
- Eliminate nostr address validation bandwidth on startup (William Casarin)
- Allow user to login to deleted profile (William Casarin)
- Fix issue where typing cc@bob would produce brokenb ccnostr:bob mention (William Casarin)
[1.6-7]: https://github.com/damus-io/damus/releases/tag/v1.6-7
## [1.6-6] - 2023-07-16
### Added
- New markdown renderer (William Casarin)
- Added feedback when user adds a relay that is already on the list (Daniel D'Aquino)
### Changed
- Hide nsec when logging in (cr0bar)
- Remove nip05 on events (William Casarin)
- Rename NIP05 to "nostr address" (William Casarin)
### Fixed
- Fixed issue where hashtags were leaking in DMs (William Casarin)
- Fix issue with emojis next to hashtags and urls (William Casarin)
- relay detail view is not immediately available after adding new relay (Bryan Montz)
- Fix nostr:nostr:... bugs (William Casarin)
[1.6-6]: https://github.com/damus-io/damus/releases/tag/v1.6-6
## [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
### Fixed
- Remove note zaps to fit apples appstore guidelines
- Fix zap sheet popping (William Casarin)
- Fix CustomizeZapView from randomly disappearing (William Casarin)
- Fix "zapped your profile" strings to say "zapped you" (Terry Yiu)
- Fix reconnect loop issues on iOS17 (William Casarin)
- Fix some more thread jankiness (William Casarin)
- Fix spelling of Nostr to use Titlecase instead of lowercase (Terry Yiu)
- Rename all usages of the term Post as a noun to Note to conform to the Nostr spec (Terry Yiu)
- Fix text cutoff on login with npub (gladiusKatana)
- Fix hangs due to video player (William Casarin)
[1.5-5]: https://github.com/damus-io/damus/releases/tag/v1.5-5
## [1.5-2] - 2023-05-30
### Added
- Add new full-bleed video player (William Casarin)
- Add ability to show multiple posts per user in Universe (Ben Weeks)
- Custom iconography added for other areas of the app. (Ben Weeks)
- Custom iconography for the left navigation. (Ben Weeks)
- Custom iconography for the tab buttons. (Ben Weeks)
- Added dots under image carousel (Ben Weeks)
- Add profile caching (Bryan Montz)
- Add mention parsing and fine-grained text selection on description in ProfileView (Terry Yiu)
### Changed
- Redesign phase 1 (text, icons)
- Updated UI to use custom font (Ben Weeks)
### Fixed
- Fix side menu bug in landscape (OlegAba)
- Use "Follow me on nostr" text when looking at someone else's QR code (Ben Weeks)
- Fix issue where cursor dissapears when typing long message (gladiusKatana)
- Attempt fix for randomly broken animated gifs (William Casarin)
- Fix cursor jumping when pressing return (gladius)
- Fix side menu label size so that translations in longer languages fit without wrapping (Terry Yiu)
- Fix reaction notification title to be consistent with ReactionView (Terry Yiu)
- Fix nostr URL scheme to open properly even if there's already a different view open (Terry Yiu)
- Fix crash related to preloading events (Bryan Montz)
## v1.4.3 - 2023-05-08
### Added
@@ -1230,3 +1559,4 @@
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2

13
Makefile Normal file
View File

@@ -0,0 +1,13 @@
all: nostrscript/primal.wasm
nostrscript/%.wasm: nostrscript/%.ts nostrscript/nostr.ts Makefile
asc $< --runtime stub --outFile $@ --optimize
tags:
find damus-c -name '*.c' -or -name '*.h' | xargs ctags
clean:
rm nostrscript/*.wasm
.PHONY: tags

View File

@@ -94,11 +94,23 @@ damus implements the following [Nostr Implementation Possibilities][nips]
Contributors welcome! Start by examining known issues: https://github.com/damus-io/damus/issues.
### Code
### Mailing lists
[Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on GitHub as well.
We have a few mailing lists that anyone can join to get involved in damus development:
[git-send-email]: http://git-send-email.io
- [dev][dev-list] - development discussions
- [patches][patches-list] - code submission and review
- [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
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.

0
TODO Normal file
View File

View File

@@ -35,7 +35,7 @@ typedef struct mention_bech32_block {
struct nostr_bech32 bech32;
} mention_bech32_block_t;
typedef struct block {
typedef struct note_block {
enum block_type type;
union {
struct str_block str;
@@ -45,12 +45,13 @@ typedef struct block {
} block;
} block_t;
typedef struct blocks {
typedef struct note_blocks {
int words;
int num_blocks;
struct block *blocks;
struct note_block *blocks;
} blocks_t;
void blocks_init(struct blocks *blocks);
void blocks_free(struct blocks *blocks);
void blocks_init(struct note_blocks *blocks);
void blocks_free(struct note_blocks *blocks);
#endif /* block_h */

View File

@@ -1,57 +1,629 @@
//
// cursor.h
// damus
//
// Created by William Casarin on 2023-04-09.
//
#ifndef cursor_h
#define cursor_h
#ifndef JB55_CURSOR_H
#define JB55_CURSOR_H
#include "typedefs.h"
#include "varint.h"
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
typedef unsigned char u8;
#define unlikely(x) __builtin_expect((x),0)
#define likely(x) __builtin_expect((x),1)
struct cursor {
const u8 *p;
const u8 *start;
const u8 *end;
unsigned char *start;
unsigned char *p;
unsigned char *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 parse_byte(struct cursor *cursor, u8 *c)
{
if (unlikely(cursor->p >= cursor->end))
return 0;
*c = *cursor->p;
//cursor->p++;
return 1;
}
static inline int parse_char(struct cursor *cur, char c) {
if (cur->p >= cur->end)
return 0;
if (*cur->p == c) {
cur->p++;
return 1;
}
return 0;
}
static inline int peek_char(struct cursor *cur, int ind) {
if ((cur->p + ind < cur->start) || (cur->p + ind >= cur->end))
return -1;
return *(cur->p + ind);
}
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_u32(struct cursor *cursor, uint32_t i) {
return cursor_push(cursor, (unsigned char*)&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) {
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
static inline int is_boundary(char c) {
return !isalnum(c);
static inline int is_underscore(char c) {
return c == '_';
}
static inline int is_invalid_url_ending(char c) {
return c == '!' || c == '?' || c == ')' || c == '.' || c == ',' || c == ';';
static inline int is_utf8_byte(u8 c) {
return c & 0x80;
}
static inline int parse_utf8_char(struct cursor *cursor, unsigned int *code_point, unsigned int *utf8_length)
{
u8 first_byte;
if (!parse_byte(cursor, &first_byte))
return 0; // Not enough data
// Determine the number of bytes in this UTF-8 character
int remaining_bytes = 0;
if (first_byte < 0x80) {
*code_point = first_byte;
return 1;
} else if ((first_byte & 0xE0) == 0xC0) {
remaining_bytes = 1;
*utf8_length = remaining_bytes + 1;
*code_point = first_byte & 0x1F;
} else if ((first_byte & 0xF0) == 0xE0) {
remaining_bytes = 2;
*utf8_length = remaining_bytes + 1;
*code_point = first_byte & 0x0F;
} else if ((first_byte & 0xF8) == 0xF0) {
remaining_bytes = 3;
*utf8_length = remaining_bytes + 1;
*code_point = first_byte & 0x07;
} else {
remaining_bytes = 0;
*utf8_length = 1; // Assume 1 byte length for unrecognized UTF-8 characters
// TODO: We need to gracefully handle unrecognized UTF-8 characters
printf("Invalid UTF-8 byte: %x\n", *code_point);
*code_point = ((first_byte & 0xF0) << 6); // Prevent testing as punctuation
return 0; // Invalid first byte
}
// Peek at remaining bytes
for (int i = 0; i < remaining_bytes; ++i) {
signed char next_byte;
if ((next_byte = peek_char(cursor, i+1)) == -1) {
*utf8_length = 1;
return 0; // Not enough data
}
// Debugging lines
//printf("Cursor: %s\n", cursor->p);
//printf("Codepoint: %x\n", *code_point);
//printf("Codepoint <<6: %x\n", ((*code_point << 6) | (next_byte & 0x3F)));
//printf("Remaining bytes: %x\n", remaining_bytes);
//printf("First byte: %x\n", first_byte);
//printf("Next byte: %x\n", next_byte);
//printf("Bitwise AND result: %x\n", (next_byte & 0xC0));
if ((next_byte & 0xC0) != 0x80) {
*utf8_length = 1;
return 0; // Invalid byte in sequence
}
*code_point = (*code_point << 6) | (next_byte & 0x3F);
}
return 1;
}
/**
* Checks if a given Unicode code point is a punctuation character
*
* @param codepoint The Unicode code point to check. @return true if the
* code point is a punctuation character, false otherwise.
*/
static inline int is_punctuation(unsigned int codepoint) {
// Check for underscore (underscore is not treated as punctuation)
if (is_underscore(codepoint))
return 0;
// Check for ASCII punctuation
if (ispunct(codepoint))
return 1;
// Check for Unicode punctuation exceptions (punctuation allowed in hashtags)
if (codepoint == 0x301C || codepoint == 0xFF5E) // Japanese Wave Dash / Tilde
return 0;
// Check for Unicode punctuation
// NOTE: We may need to adjust the codepoint ranges in the future,
// to include/exclude certain types of Unicode characters in hashtags.
// Unicode Blocks Reference: https://www.compart.com/en/unicode/block
return (
// Latin-1 Supplement No-Break Space (NBSP): U+00A0
(codepoint == 0x00A0) ||
// Latin-1 Supplement Punctuation: U+00A1 to U+00BF
(codepoint >= 0x00A1 && codepoint <= 0x00BF) ||
// General Punctuation: U+2000 to U+206F
(codepoint >= 0x2000 && codepoint <= 0x206F) ||
// Currency Symbols: U+20A0 to U+20CF
(codepoint >= 0x20A0 && codepoint <= 0x20CF) ||
// Supplemental Punctuation: U+2E00 to U+2E7F
(codepoint >= 0x2E00 && codepoint <= 0x2E7F) ||
// CJK Symbols and Punctuation: U+3000 to U+303F
(codepoint >= 0x3000 && codepoint <= 0x303F) ||
// Ideographic Description Characters: U+2FF0 to U+2FFF
(codepoint >= 0x2FF0 && codepoint <= 0x2FFF)
);
}
static inline int is_right_boundary(int c) {
return is_whitespace(c) || is_punctuation(c);
}
static inline int is_left_boundary(char c) {
return is_right_boundary(c) || is_utf8_byte(c);
}
static inline int is_alphanumeric(char c) {
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;
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
}
static inline int consume_until_boundary(struct cursor *cur) {
char c;
unsigned int c;
unsigned int char_length = 1;
unsigned int *utf8_char_length = &char_length;
while (cur->p < cur->end) {
c = *cur->p;
if (is_boundary(c))
*utf8_char_length = 1;
if (is_whitespace(c))
return 1;
cur->p++;
// Need to check for UTF-8 characters, which can be multiple bytes long
if (is_utf8_byte(c)) {
if (!parse_utf8_char(cur, &c, utf8_char_length)) {
if (!is_right_boundary(c)){
// TODO: We should work towards handling all UTF-8 characters.
printf("Invalid UTF-8 code point: %x\n", c);
}
}
}
if (is_right_boundary(c))
return 1;
// Need to use a variable character byte length for UTF-8 (2-4 bytes)
if (cur->p + *utf8_char_length <= cur->end)
cur->p += *utf8_char_length;
else
cur->p++;
}
return 1;
@@ -91,66 +663,4 @@ static inline int consume_until_non_alphanumeric(struct cursor *cur, int or_end)
return or_end;
}
static inline int parse_char(struct cursor *cur, char c) {
if (cur->p >= cur->end)
return 0;
if (*cur->p == c) {
cur->p++;
return 1;
}
return 0;
}
static inline int peek_char(struct cursor *cur, int ind) {
if ((cur->p + ind < cur->start) || (cur->p + ind >= cur->end))
return -1;
return *(cur->p + ind);
}
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 */
#endif

View File

@@ -5,3 +5,9 @@
#include "damus.h"
#include "bolt11.h"
#include "amount.h"
#include "nostr_bech32.h"
#include "wasm.h"
#include "nostrscript.h"
#include "nostrdb.h"
#include "lmdb.h"

View File

@@ -28,9 +28,9 @@ static int parse_digit(struct cursor *cur, int *digit) {
}
static int parse_mention_index(struct cursor *cur, struct block *block) {
static int parse_mention_index(struct cursor *cur, struct note_block *block) {
int d1, d2, d3, ind;
const u8 *start = cur->p;
u8 *start = cur->p;
if (!parse_str(cur, "#["))
return 0;
@@ -59,9 +59,9 @@ static int parse_mention_index(struct cursor *cur, struct block *block) {
return 1;
}
static int parse_hashtag(struct cursor *cur, struct block *block) {
static int parse_hashtag(struct cursor *cur, struct note_block *block) {
int c;
const u8 *start = cur->p;
u8 *start = cur->p;
if (!parse_char(cur, '#'))
return 0;
@@ -81,7 +81,7 @@ static int parse_hashtag(struct cursor *cur, struct block *block) {
return 1;
}
static int add_block(struct blocks *blocks, struct block block)
static int add_block(struct note_blocks *blocks, struct note_block block)
{
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
return 0;
@@ -90,9 +90,9 @@ static int add_block(struct blocks *blocks, struct block block)
return 1;
}
static int add_text_block(struct blocks *blocks, const u8 *start, const u8 *end)
static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8 *end)
{
struct block b;
struct note_block b;
if (start == end)
return 1;
@@ -104,8 +104,71 @@ static int add_text_block(struct blocks *blocks, const u8 *start, const u8 *end)
return add_block(blocks, b);
}
static int parse_url(struct cursor *cur, struct block *block) {
const u8 *start = cur->p;
static int consume_url_fragment(struct cursor *cur)
{
int c;
if ((c = peek_char(cur, 0)) < 0)
return 1;
if (c != '#' && c != '?') {
return 1;
}
cur->p++;
return consume_until_whitespace(cur, 1);
}
static int consume_url_path(struct cursor *cur)
{
int c;
if ((c = peek_char(cur, 0)) < 0)
return 1;
if (c != '/') {
return 1;
}
while (cur->p < cur->end) {
c = *cur->p;
if (c == '?' || c == '#' || is_whitespace(c)) {
return 1;
}
cur->p++;
}
return 1;
}
static int consume_url_host(struct cursor *cur)
{
char c;
int count = 0;
while (cur->p < cur->end) {
c = *cur->p;
// TODO: handle IDNs
if (is_alphanumeric(c) || c == '.' || c == '-')
{
count++;
cur->p++;
continue;
}
return count != 0;
}
// this means the end of the URL hostname is the end of the buffer and we finished
return count != 0;
}
static int parse_url(struct cursor *cur, struct note_block *block) {
u8 *start = cur->p;
if (!parse_str(cur, "http"))
return 0;
@@ -121,15 +184,25 @@ static int parse_url(struct cursor *cur, struct block *block) {
return 0;
}
}
if (!consume_until_whitespace(cur, 1)) {
if (!(consume_url_host(cur) &&
consume_url_path(cur) &&
consume_url_fragment(cur)))
{
cur->p = start;
return 0;
}
// strip any unwanted characters
while(is_invalid_url_ending(peek_char(cur, -1))) cur->p--;
// smart parens
if (start - 1 >= 0 &&
start < cur->end &&
*(start - 1) == '(' &&
(cur->p - 1) < cur->end &&
*(cur->p - 1) == ')')
{
cur->p--;
}
block->type = BLOCK_URL;
block->block.str.start = (const char *)start;
block->block.str.end = (const char *)cur->p;
@@ -137,8 +210,8 @@ static int parse_url(struct cursor *cur, struct block *block) {
return 1;
}
static int parse_invoice(struct cursor *cur, struct block *block) {
const u8 *start, *end;
static int parse_invoice(struct cursor *cur, struct note_block *block) {
u8 *start, *end;
char *fail;
struct bolt11 *bolt11;
// optional
@@ -177,12 +250,12 @@ static int parse_invoice(struct cursor *cur, struct block *block) {
}
static int parse_mention_bech32(struct cursor *cur, struct block *block) {
const u8 *start = cur->p;
if (!parse_str(cur, "nostr:"))
return 0;
static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
u8 *start = cur->p;
parse_char(cur, '@');
parse_str(cur, "nostr:");
block->block.str.start = (const char *)cur->p;
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
@@ -197,7 +270,7 @@ static int parse_mention_bech32(struct cursor *cur, struct block *block) {
return 1;
}
static int add_text_then_block(struct cursor *cur, struct blocks *blocks, struct block block, const u8 **start, const u8 *pre_mention)
static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, struct note_block block, u8 **start, const u8 *pre_mention)
{
if (!add_text_block(blocks, *start, pre_mention))
return 0;
@@ -210,22 +283,28 @@ static int add_text_then_block(struct cursor *cur, struct blocks *blocks, struct
return 1;
}
int damus_parse_content(struct blocks *blocks, const char *content) {
int damus_parse_content(struct note_blocks *blocks, const char *content) {
int cp, c;
struct cursor cur;
struct block block;
const u8 *start, *pre_mention;
struct note_block block;
u8 *start, *pre_mention;
blocks->words = 0;
blocks->num_blocks = 0;
make_cursor(&cur, (const u8*)content, strlen(content));
make_cursor((u8*)content, (u8*)content + strlen(content), &cur);
start = cur.p;
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
cp = peek_char(&cur, -1);
c = peek_char(&cur, 0);
// new word
if (is_whitespace(cp) && !is_whitespace(c)) {
blocks->words++;
}
pre_mention = cur.p;
if (cp == -1 || is_whitespace(cp) || c == '#') {
if (cp == -1 || is_left_boundary(cp) || c == '#') {
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
return 0;
@@ -238,7 +317,7 @@ int damus_parse_content(struct blocks *blocks, const char *content) {
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
return 0;
continue;
} else if (c == 'n' && parse_mention_bech32(&cur, &block)) {
} else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) {
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
return 0;
continue;
@@ -256,12 +335,12 @@ int damus_parse_content(struct blocks *blocks, const char *content) {
return 1;
}
void blocks_init(struct blocks *blocks) {
blocks->blocks = malloc(sizeof(struct block) * MAX_BLOCKS);
void blocks_init(struct note_blocks *blocks) {
blocks->blocks = malloc(sizeof(struct note_block) * MAX_BLOCKS);
blocks->num_blocks = 0;
}
void blocks_free(struct blocks *blocks) {
void blocks_free(struct note_blocks *blocks) {
if (!blocks->blocks) {
return;
}

View File

@@ -9,10 +9,10 @@
#define damus_h
#include <stdio.h>
#include "nostr_bech32.h"
#include "block.h"
typedef unsigned char u8;
int damus_parse_content(struct blocks *blocks, const char *content);
int damus_parse_content(struct note_blocks *blocks, const char *content);
#endif /* damus_h */

15
damus-c/debug.h Normal file
View File

@@ -0,0 +1,15 @@
#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 */

34
damus-c/error.c Normal file
View File

@@ -0,0 +1,34 @@
#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;
}

33
damus-c/error.h Normal file
View File

@@ -0,0 +1,33 @@
#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 */

View File

@@ -39,15 +39,6 @@ bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize)
return slen == 0 && bufsize == 0;
}
static char hexchar(unsigned int val)
{
if (val < 10)
return '0' + val;
if (val < 16)
return 'a' + val - 10;
abort();
}
bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
{
size_t i;

View File

@@ -70,4 +70,15 @@ static inline size_t hex_data_size(size_t strlen)
{
return strlen / 2;
}
static inline char hexchar(unsigned int val)
{
if (val < 10)
return '0' + val;
if (val < 16)
return 'a' + val - 10;
abort();
}
#endif /* CCAN_HEX_H */

View File

@@ -52,9 +52,13 @@
*/
#define unlikely(cond) __builtin_expect(!!(cond), 0)
#else
#ifndef likely
#define likely(cond) (!!(cond))
#endif
#ifndef unlikely
#define unlikely(cond) (!!(cond))
#endif
#endif
#else /* CCAN_LIKELY_DEBUG versions */
#include <ccan/str/str.h>

View File

@@ -91,6 +91,9 @@ static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *t
} else if (strcmp(prefix, "npub") == 0) {
*type = NOSTR_BECH32_NPUB;
return 1;
} else if (strcmp(prefix, "nsec") == 0) {
*type = NOSTR_BECH32_NSEC;
return 1;
} else if (strcmp(prefix, "nprofile") == 0) {
*type = NOSTR_BECH32_NPROFILE;
return 1;
@@ -116,6 +119,10 @@ static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub)
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) {
struct nostr_tlv *tlv;
struct str_block *str;
@@ -218,7 +225,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) {
const u8 *start, *end;
u8 *start, *end;
start = cur->p;
@@ -257,7 +264,7 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
}
struct cursor bcur;
make_cursor(&bcur, obj->buffer, obj->buflen);
make_cursor(obj->buffer, obj->buffer + obj->buflen, &bcur);
switch (obj->type) {
case NOSTR_BECH32_NOTE:
@@ -268,6 +275,10 @@ int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
goto fail;
break;
case NOSTR_BECH32_NSEC:
if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec))
goto fail;
break;
case NOSTR_BECH32_NEVENT:
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
goto fail;

View File

@@ -26,6 +26,7 @@ enum nostr_bech32_type {
NOSTR_BECH32_NEVENT = 4,
NOSTR_BECH32_NRELAY = 5,
NOSTR_BECH32_NADDR = 6,
NOSTR_BECH32_NSEC = 7,
};
struct bech32_note {
@@ -36,6 +37,10 @@ struct bech32_npub {
const u8 *pubkey;
};
struct bech32_nsec {
const u8 *nsec;
};
struct bech32_nevent {
struct relays relays;
const u8 *event_id;
@@ -65,6 +70,7 @@ typedef struct nostr_bech32 {
union {
struct bech32_note note;
struct bech32_npub npub;
struct bech32_nsec nsec;
struct bech32_nevent nevent;
struct bech32_nprofile nprofile;
struct bech32_naddr naddr;

42
damus-c/parser.h Normal file
View File

@@ -0,0 +1,42 @@
#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 */

14
damus-c/typedefs.h Normal file
View File

@@ -0,0 +1,14 @@
#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 */

14
damus-c/varint.h Normal file
View File

@@ -0,0 +1,14 @@
#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 */

7299
damus-c/wasm.c Normal file

File diff suppressed because it is too large Load Diff

850
damus-c/wasm.h Normal file
View File

@@ -0,0 +1,850 @@
#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 */

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,14 @@
{
"pins" : [
{
"identity" : "gsplayer",
"kind" : "remoteSourceControl",
"location" : "https://github.com/wxxsw/GSPlayer",
"state" : {
"revision" : "aa6dad7943d52f5207f7fcc2ad3e4274583443b8",
"version" : "0.2.26"
}
},
{
"identity" : "kingfisher",
"kind" : "remoteSourceControl",
@@ -16,6 +25,14 @@
"state" : {
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
}
},
{
"identity" : "swift-markdown-ui",
"kind" : "remoteSourceControl",
"location" : "https://github.com/damus-io/swift-markdown-ui",
"state" : {
"revision" : "76bb7971da7fbf429de1c84f1244adf657242fee"
}
}
],
"version" : 2

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE3",
"green" : "0xD7",
"red" : "0xF7"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x20",
"green" : "0x13",
"red" : "0x61"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x63",
"green" : "0x11",
"red" : "0xF5"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x6E",
"green" : "0x20",
"red" : "0xF8"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xEE",
"green" : "0xE8",
"red" : "0xF7"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x35",
"green" : "0x04",
"red" : "0x8B"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x3D",
"green" : "0x07",
"red" : "0x9C"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x44",
"green" : "0x06",
"red" : "0xB2"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2D",
"green" : "0x05",
"red" : "0x75"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD8",
"green" : "0xC2",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFA",
"green" : "0xFA",
"red" : "0xF9"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x24",
"green" : "0x22",
"red" : "0x20"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE3",
"green" : "0xE1",
"red" : "0xDD"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2A",
"green" : "0x26",
"red" : "0x23"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x59",
"green" : "0x53",
"red" : "0x4A"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x85",
"green" : "0x7A",
"red" : "0x6A"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE4",
"green" : "0xF1",
"red" : "0xD6"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x38",
"green" : "0x5C",
"red" : "0x12"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x5A",
"green" : "0xAB",
"red" : "0x04"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x64",
"green" : "0xBF",
"red" : "0x03"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF0",
"green" : "0xF7",
"red" : "0xE8"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1F",
"green" : "0x33",
"red" : "0x0A"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x34",
"green" : "0x64",
"red" : "0x02"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x3F",
"green" : "0x79",
"red" : "0x02"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1F",
"green" : "0x3C",
"red" : "0x01"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE4",
"green" : "0xFF",
"red" : "0xAD"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xD1",
"green" : "0xEE",
"red" : "0xFE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x12",
"green" : "0x43",
"red" : "0x5C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1C",
"green" : "0xAD",
"red" : "0xF9"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2C",
"green" : "0xB5",
"red" : "0xFC"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE1",
"green" : "0xF4",
"red" : "0xFE"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x0A",
"green" : "0x25",
"red" : "0x33"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x06",
"green" : "0x85",
"red" : "0xC6"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x03",
"green" : "0x93",
"red" : "0xDD"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x04",
"green" : "0x6A",
"red" : "0x9F"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xCC",
"green" : "0xF5",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -1,15 +1,12 @@
{
"images" : [
{
"filename" : "shaka-full.pdf",
"filename" : "eula-bg.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@@ -0,0 +1,28 @@
<svg width="430" height="813" viewBox="0 0 430 813" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_f_1069_29012)">
<path d="M44.0811 256.851L186.315 111L276.203 223.574L244.02 388.295L69.9751 697.084L100.678 498.338L44.0811 256.851Z" fill="url(#paint0_linear_1069_29012)"/>
</g>
<g filter="url(#filter1_f_1069_29012)">
<path d="M116.509 587.348L206.677 479.401L230.746 273.265L266.666 231.183L367.424 396.975L281.292 659.008L266.665 801.413L66.889 763.694L116.509 587.348Z" fill="url(#paint1_linear_1069_29012)"/>
</g>
<defs>
<filter id="filter0_f_1069_29012" x="-66.6248" y="0.294121" width="453.534" height="807.496" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="55.3529" result="effect1_foregroundBlur_1069_29012"/>
</filter>
<filter id="filter1_f_1069_29012" x="-43.8172" y="120.477" width="521.947" height="791.642" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="55.3529" result="effect1_foregroundBlur_1069_29012"/>
</filter>
<linearGradient id="paint0_linear_1069_29012" x1="230.179" y1="166.577" x2="-67.7956" y2="310.108" gradientUnits="userSpaceOnUse">
<stop stop-color="#D34CD9"/>
<stop offset="1" stop-color="#4E4DF4"/>
</linearGradient>
<linearGradient id="paint1_linear_1069_29012" x1="139.483" y1="462.902" x2="377.854" y2="565.47" gradientUnits="userSpaceOnUse">
<stop stop-color="#0DE8FF"/>
<stop offset="1" stop-color="#641AAE"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,15 +1,12 @@
{
"images" : [
{
"filename" : "shaka-line.pdf",
"filename" : "gradient.jpg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "lightbulb.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,14 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.2" width="48" height="48" rx="24" fill="url(#paint0_linear_1843_42349)"/>
<path d="M19.9993 36.0001H27.9993M28.8095 28.0001C31.5199 26.3669 33.3327 23.3952 33.3327 20.0001C33.3327 14.8454 29.154 10.6667 23.9993 10.6667C18.8447 10.6667 14.666 14.8454 14.666 20.0001C14.666 23.3952 16.4788 26.3669 19.1892 28.0001M28.8095 28.0001C28.5475 28.1579 28.2772 28.3032 27.9993 28.4352V31.3334C27.9993 31.7016 27.7009 32.0001 27.3327 32.0001H20.666C20.2978 32.0001 19.9993 31.7016 19.9993 31.3334V28.4352C19.7215 28.3032 19.4512 28.1579 19.1892 28.0001M28.8095 28.0001H19.1892" stroke="url(#paint1_linear_1843_42349)" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
<defs>
<linearGradient id="paint0_linear_1843_42349" x1="5.41935" y1="0.774194" x2="37.9355" y2="47.2258" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9AD1C"/>
<stop offset="1" stop-color="#DF7E0C"/>
</linearGradient>
<linearGradient id="paint1_linear_1843_42349" x1="16.7735" y1="11.0754" x2="35.0141" y2="30.2759" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9AD1C"/>
<stop offset="1" stop-color="#DF7E0C"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "header.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "nostr-logo.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,88 +0,0 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.073975 cm
1.000000 1.000000 1.000000 scn
1.295334 8.661732 m
3.613694 8.367855 l
4.475733 8.733568 5.268113 9.771931 5.474915 10.327032 c
6.083156 11.959681 5.507567 14.604573 5.474915 15.061715 c
5.448792 15.427428 6.008246 15.693006 6.291239 15.780080 c
7.571236 15.858447 8.508359 14.876789 8.642253 13.984165 c
8.740212 13.331103 8.576948 11.752880 8.381030 10.849482 c
8.979668 10.936556 10.980525 10.901726 11.868687 10.849482 c
12.756847 10.797236 13.474895 10.196423 14.193260 9.412750 c
14.767952 8.237244 13.953805 7.725680 13.474895 7.616838 c
13.834077 7.257654 l
14.781013 5.918882 13.649043 5.178749 13.115711 5.004600 c
13.474895 4.743376 l
14.487136 3.763786 13.246323 2.751544 13.017752 2.882155 c
11.058574 3.176033 l
15.499378 1.673996 l
16.054478 0.400530 15.074889 0.073999 14.781013 0.073999 c
8.576947 1.673996 l
6.291239 1.673996 5.311650 1.869914 4.299407 2.163791 c
4.157911 2.131138 3.659409 1.987464 2.797370 1.673996 c
1.935332 1.360527 1.219143 2.087601 0.968804 2.490320 c
-0.285071 4.083785 -0.467927 7.257655 1.295334 8.661732 c
h
f
n
Q
endstream
endobj
3 0 obj
1149
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 15.666626 15.710510 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001239 00000 n
0000001262 00000 n
0000001435 00000 n
0000001509 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1568
%%EOF

View File

@@ -1,323 +0,0 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.474731 -0.563965 cm
1.000000 1.000000 1.000000 scn
3.613694 9.332577 m
3.553993 8.861599 l
3.637261 8.851044 3.721838 8.862753 3.799107 8.895533 c
3.613694 9.332577 l
h
1.295334 9.626453 m
1.355035 10.097433 l
1.227973 10.113539 1.099794 10.077623 0.999601 9.997839 c
1.295334 9.626453 l
h
0.968804 3.455042 m
1.372000 3.705677 l
1.362764 3.720535 1.352713 3.734872 1.341894 3.748621 c
0.968804 3.455042 l
h
4.299407 3.128512 m
4.431771 3.584435 l
4.353942 3.607030 4.271623 3.609325 4.192656 3.591103 c
4.299407 3.128512 l
h
8.576947 2.638718 m
8.695503 3.098424 l
8.656776 3.108411 8.616942 3.113465 8.576947 3.113465 c
8.576947 2.638718 l
h
14.781013 1.038721 m
14.662457 0.579016 l
14.701184 0.569027 14.741018 0.563974 14.781013 0.563974 c
14.781013 1.038721 l
h
15.499378 2.638718 m
15.934578 2.828420 l
15.881091 2.951125 15.778289 3.045548 15.651489 3.088437 c
15.499378 2.638718 l
h
11.058574 4.140755 m
11.128998 4.610250 l
10.885809 4.646729 10.655017 4.491467 10.597156 4.252461 c
10.539293 4.013455 10.673516 3.769826 10.906463 3.691035 c
11.058574 4.140755 l
h
13.017752 3.846877 m
13.253292 4.259073 l
13.202273 4.288227 13.146286 4.307655 13.088176 4.316372 c
13.017752 3.846877 l
h
13.474895 5.708097 m
13.805044 6.049252 l
13.789093 6.064689 13.772079 6.078987 13.754128 6.092043 c
13.474895 5.708097 l
h
13.115711 5.969321 m
12.968349 6.420619 l
12.798800 6.365256 12.674588 6.219535 12.646772 6.043359 c
12.618958 5.867183 12.692234 5.690281 12.836478 5.585376 c
13.115711 5.969321 l
h
13.834077 8.222376 m
14.221668 8.496526 l
14.206144 8.518474 14.188784 8.539063 14.169774 8.558073 c
13.834077 8.222376 l
h
13.474895 8.581559 m
13.369680 9.044500 l
13.201114 9.006190 13.066693 8.879284 13.018762 8.713197 c
12.970830 8.547110 13.016963 8.368095 13.139197 8.245862 c
13.474895 8.581559 l
h
14.193260 10.377472 m
14.619765 10.585986 l
14.599768 10.626891 14.573989 10.664707 14.543221 10.698271 c
14.193260 10.377472 l
h
8.381030 11.814203 m
7.917068 11.914822 l
7.884080 11.762714 7.927746 11.604099 8.033934 11.490305 c
8.140121 11.376513 8.295343 11.321997 8.449365 11.344399 c
8.381030 11.814203 l
h
8.642253 14.948887 m
9.111748 15.019311 l
8.642253 14.948887 l
h
6.291239 16.744801 m
6.262227 17.218662 l
6.224693 17.216364 6.187564 17.209614 6.151623 17.198555 c
6.291239 16.744801 l
h
5.474915 16.026436 m
5.948456 16.060261 l
5.474915 16.026436 l
h
5.474915 11.291754 m
5.030037 11.457493 l
5.474915 11.291754 l
h
3.673396 9.803555 m
1.355035 10.097433 l
1.235632 9.155476 l
3.553993 8.861599 l
3.673396 9.803555 l
h
0.999601 9.997839 m
-0.029049 9.178730 -0.454726 7.875908 -0.474048 6.619066 c
-0.493367 5.362488 -0.110331 4.058727 0.595713 3.161463 c
1.341894 3.748621 l
0.794064 4.444821 0.458734 5.524729 0.475334 6.604470 c
0.491930 7.683949 0.856455 8.670100 1.591066 9.255068 c
0.999601 9.997839 l
h
0.565608 3.204407 m
0.721970 2.952868 1.013515 2.611341 1.407507 2.372385 c
1.811404 2.127421 2.357187 1.973489 2.959612 2.192553 c
2.635129 3.084882 l
2.375515 2.990478 2.132184 3.043347 1.899893 3.184233 c
1.657696 3.331126 1.465977 3.554496 1.372000 3.705677 c
0.565608 3.204407 l
h
2.959612 2.192553 m
3.816493 2.504146 4.293336 2.639887 4.406158 2.665923 c
4.192656 3.591103 l
4.022485 3.551832 3.502325 3.400227 2.635129 3.084882 c
2.959612 2.192553 l
h
4.167043 2.672591 m
5.229115 2.364247 6.254152 2.163970 8.576947 2.163970 c
8.576947 3.113465 l
6.328326 3.113465 5.394184 3.305025 4.431771 3.584435 c
4.167043 2.672591 l
h
8.458392 2.179011 m
14.662457 0.579016 l
14.899569 1.498427 l
8.695503 3.098424 l
8.458392 2.179011 l
h
14.781013 0.563974 m
15.036198 0.563974 15.495326 0.684875 15.814721 1.047266 c
16.180891 1.462728 16.264221 2.072176 15.934578 2.828420 c
15.064179 2.449016 l
15.289635 1.931793 15.160722 1.741243 15.102402 1.675073 c
15.055794 1.622190 14.990156 1.579316 14.916806 1.549556 c
14.881134 1.535082 14.847747 1.525430 14.820526 1.519657 c
14.791491 1.513498 14.777695 1.513469 14.781013 1.513469 c
14.781013 0.563974 l
h
15.651489 3.088437 m
11.210685 4.590474 l
10.906463 3.691035 l
15.347267 2.188998 l
15.651489 3.088437 l
h
10.988150 3.671260 m
12.947328 3.377382 l
13.088176 4.316372 l
11.128998 4.610250 l
10.988150 3.671260 l
h
12.782211 3.434681 m
12.991495 3.315090 13.204453 3.370091 13.288217 3.396689 c
13.400116 3.432221 13.506123 3.490767 13.598186 3.554502 c
13.783985 3.683133 13.977411 3.877748 14.120350 4.119644 c
14.264680 4.363894 14.369576 4.678114 14.335162 5.031647 c
14.300108 5.391746 14.125634 5.739002 13.805044 6.049252 c
13.144745 5.366943 l
13.330275 5.187398 13.380290 5.040778 13.390134 4.939653 c
13.400617 4.831963 13.370820 4.717613 13.302905 4.602680 c
13.233600 4.485394 13.137231 4.390213 13.057724 4.335170 c
13.017135 4.307070 12.996612 4.300308 13.000857 4.301657 c
13.003194 4.302399 13.024761 4.309311 13.061064 4.310122 c
13.095938 4.310902 13.170414 4.306433 13.253292 4.259073 c
12.782211 3.434681 l
h
13.754128 6.092043 m
13.394944 6.353267 l
12.836478 5.585376 l
13.195662 5.324152 l
13.754128 6.092043 l
h
13.263074 5.518023 m
13.593105 5.625790 14.123367 5.907292 14.433812 6.409482 c
14.595931 6.671733 14.696482 6.993351 14.669847 7.364054 c
14.643518 7.730516 14.495621 8.109214 14.221668 8.496526 c
13.446486 7.948226 l
13.646002 7.666152 13.711709 7.450294 13.722795 7.296009 c
13.733575 7.145966 13.695351 7.020646 13.626177 6.908748 c
13.474038 6.662641 13.171650 6.487002 12.968349 6.420619 c
13.263074 5.518023 l
h
14.169774 8.558073 m
13.810592 8.917255 l
13.139197 8.245862 l
13.498380 7.886679 l
14.169774 8.558073 l
h
13.580109 8.118617 m
13.896242 8.190466 14.344993 8.395787 14.624650 8.816864 c
14.929440 9.275781 14.963785 9.882310 14.619765 10.585986 c
13.766754 10.168959 l
13.997427 9.697128 13.912044 9.460121 13.833706 9.342171 c
13.730235 9.186377 13.532457 9.081495 13.369680 9.044500 c
13.580109 8.118617 l
h
14.543221 10.698271 m
13.820906 11.486253 12.989320 12.223852 11.896564 12.288132 c
11.840808 11.340275 l
12.524374 11.300065 13.128883 10.836036 13.843298 10.056674 c
14.543221 10.698271 l
h
11.896564 12.288132 m
11.441970 12.314873 10.711069 12.336796 10.019300 12.341186 c
9.341933 12.345484 8.654247 12.333687 8.312695 12.284006 c
8.449365 11.344399 l
8.706450 11.381794 9.318512 11.396118 10.013274 11.391710 c
10.693633 11.387392 11.407242 11.365778 11.840808 11.340275 c
11.896564 12.288132 l
h
8.844993 11.713585 m
8.948084 12.188952 9.040332 12.829445 9.094679 13.432834 c
9.147870 14.023395 9.169946 14.631327 9.111748 15.019311 c
8.172758 14.878462 l
8.212520 14.613384 8.201942 14.105675 8.149012 13.518009 c
8.097237 12.943172 8.009893 12.342852 7.917068 11.914822 c
8.844993 11.713585 l
h
9.111748 15.019311 m
8.944062 16.137217 7.805658 17.313158 6.262227 17.218662 c
6.320251 16.270941 l
7.336813 16.333179 8.072657 15.545805 8.172758 14.878462 c
9.111748 15.019311 l
h
6.151623 17.198555 m
5.976391 17.144638 5.715709 17.036982 5.490986 16.876261 c
5.292936 16.734617 4.969444 16.439627 5.001374 15.992612 c
5.948456 16.060261 l
5.951383 16.019283 5.934667 15.999795 5.943361 16.012491 c
5.954769 16.029152 5.984430 16.061831 6.043331 16.103956 c
6.162553 16.189222 6.323094 16.257891 6.430855 16.291048 c
6.151623 17.198555 l
h
5.001374 15.992612 m
5.011176 15.855374 5.059216 15.566318 5.104405 15.255149 c
5.152757 14.922197 5.207128 14.509316 5.241940 14.062993 c
5.312967 13.152368 5.295928 12.171200 5.030037 11.457493 c
5.919792 11.126015 l
6.262142 12.044956 6.261431 13.202559 6.188560 14.136827 c
6.151423 14.612950 6.093790 15.049047 6.044043 15.391605 c
5.991133 15.755945 5.954979 15.968927 5.948456 16.060261 c
5.001374 15.992612 l
h
5.030037 11.457493 m
4.953650 11.252455 4.742510 10.903708 4.434547 10.555828 c
4.127778 10.209298 3.769400 9.914337 3.428282 9.769621 c
3.799107 8.895533 l
4.320028 9.116529 4.788858 9.523607 5.145489 9.926461 c
5.500926 10.327968 5.789377 10.775953 5.919792 11.126015 c
5.030037 11.457493 l
h
f
n
Q
endstream
endobj
3 0 obj
7995
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 16.615845 16.660034 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000008085 00000 n
0000008108 00000 n
0000008281 00000 n
0000008355 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
8414
%%EOF

View File

@@ -22,5 +22,23 @@ class DamusColors {
static let purple = Color("DamusPurple")
static let deepPurple = Color("DamusDeepPurple")
static let blue = Color("DamusBlue")
static let success = Color("DamusSuccessPrimary")
static let successSecondary = Color("DamusSuccessSecondary")
static let successTertiary = Color("DamusSuccessTertiary")
static let successQuaternary = Color("DamusSuccessQuaternary")
static let successBorder = Color("DamusSuccessBorder")
static let warning = Color("DamusWarningPrimary")
static let warningSecondary = Color("DamusWarningSecondary")
static let warningTertiary = Color("DamusWarningTertiary")
static let warningQuaternary = Color("DamusWarningQuaternary")
static let warningBorder = Color("DamusWarningBorder")
static let danger = Color("DamusDangerPrimary")
static let dangerSecondary = Color("DamusDangerSecondary")
static let dangerTertiary = Color("DamusDangerTertiary")
static let dangerQuaternary = Color("DamusDangerQuaternary")
static let dangerBorder = Color("DamusDangerBorder")
static let neutral1 = Color("DamusNeutral1")
static let neutral3 = Color("DamusNeutral3")
static let neutral6 = Color("DamusNeutral6")
}

View File

@@ -10,11 +10,7 @@ import SwiftUI
struct EndBlock: View {
let height: CGFloat
init () {
self.height = 10.0
}
init (height: Float) {
init(height: Float = 10) {
self.height = CGFloat(height)
}

View File

@@ -0,0 +1,53 @@
//
// GradientButtonStyle.swift
// damus
//
// Created by eric on 5/20/23.
//
import SwiftUI
struct GradientButtonStyle: ButtonStyle {
let padding: CGFloat
init(padding: CGFloat = 16.0) {
self.padding = padding
}
func makeBody(configuration: Self.Configuration) -> some View {
return configuration.label
.padding(padding)
.foregroundColor(Color.white)
.background {
RoundedRectangle(cornerRadius: 12)
.fill(PinkGradient)
}
.scaleEffect(configuration.isPressed ? 0.95 : 1)
}
}
struct GradientButtonStyle_Previews: PreviewProvider {
static var previews: some View {
VStack {
Button(action: {
print("dynamic size")
}) {
Text(verbatim: "Dynamic Size")
}
.buttonStyle(GradientButtonStyle())
Button(action: {
print("infinite width")
}) {
HStack {
Text(verbatim: "Infinite Width")
}
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
}
.buttonStyle(GradientButtonStyle())
.padding()
}
}
}

View File

@@ -0,0 +1,30 @@
//
// 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()
}
}

View File

@@ -0,0 +1,30 @@
//
// DamusLightGradient.swift
// damus
//
// Created by eric on 9/8/23.
//
import SwiftUI
fileprivate let damus_grad_c1 = hex_col(r: 0xd3, g: 0x2d, b: 0xc3)
fileprivate let damus_grad_c2 = hex_col(r: 0x33, g: 0xc5, b: 0xbc)
fileprivate let damus_grad = [damus_grad_c1, damus_grad_c2]
struct DamusLightGradient: View {
var body: some View {
DamusLightGradient.gradient
.opacity(0.5)
.edgesIgnoringSafeArea([.top,.bottom])
}
static var gradient: LinearGradient {
LinearGradient(colors: damus_grad, startPoint: .topLeading, endPoint: .bottomTrailing)
}
}
struct DamusLightGradient_Previews: PreviewProvider {
static var previews: some View {
DamusLightGradient()
}
}

View File

@@ -0,0 +1,29 @@
//
// DamusLogoGradient.swift
// damus
//
// Created by eric on 5/24/23.
//
import SwiftUI
fileprivate let damus_logo_grad_c1 = hex_col(r: 0x30, g: 0xb3, b: 0xf1)
fileprivate let damus_logo_grad_c2 = hex_col(r: 0xc5, g: 0x39, b: 0xf9)
fileprivate let damus_logo_grad = [damus_logo_grad_c1, damus_logo_grad_c2]
struct DamusLogoGradient: View {
var body: some View {
DamusLogoGradient.gradient
.edgesIgnoringSafeArea([.top,.bottom])
}
static var gradient: LinearGradient {
LinearGradient(colors: damus_logo_grad, startPoint: .leading, endPoint: .trailing)
}
}
struct DamusLogoGradient_Previews: PreviewProvider {
static var previews: some View {
DamusLogoGradient()
}
}

View File

@@ -0,0 +1,29 @@
//
// GoldSupportGradient.swift
// damus
//
// Created by William Casarin on 2023-05-15.
//
import SwiftUI
fileprivate let gold_grad_c1 = hex_col(r: 226, g: 168, b: 0)
fileprivate let gold_grad_c2 = hex_col(r: 249, g: 243, b: 100)
fileprivate let gold_grad = [gold_grad_c2, gold_grad_c1]
let GoldGradient: LinearGradient =
LinearGradient(colors: gold_grad, startPoint: .bottomLeading, endPoint: .topTrailing)
struct GoldGradientView: View {
var body: some View {
GoldGradient
.edgesIgnoringSafeArea([.top,.bottom])
}
}
struct GoldGradientView_Previews: PreviewProvider {
static var previews: some View {
GoldGradientView()
}
}

View File

@@ -0,0 +1,26 @@
//
// GrayGradient.swift
// damus
//
// Created by klabo on 7/20/23.
//
import SwiftUI
let GrayGradient = LinearGradient(gradient:
Gradient(colors: [Color(#colorLiteral(red: 0.9764705882, green: 0.9803921569, blue: 0.9803921569, alpha: 1))]),
startPoint: .leading,
endPoint: .trailing)
struct GrayGradientView: View {
var body: some View {
GrayGradient
.edgesIgnoringSafeArea([.top, .bottom])
}
}
struct GrayGradient_Previews: PreviewProvider {
static var previews: some View {
GrayGradientView()
}
}

View File

@@ -0,0 +1,28 @@
//
// PinkGradient.swift
// damus
//
// Created by eric on 5/20/23.
//
import SwiftUI
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 pink_grad = [damus_grad_c1, damus_grad_c2]
let PinkGradient = LinearGradient(colors: pink_grad, startPoint: .topTrailing, endPoint: .bottom)
struct PinkGradientView: View {
var body: some View {
PinkGradient
.edgesIgnoringSafeArea([.top,.bottom])
}
}
struct PinkGradientView_Previews: PreviewProvider {
static var previews: some View {
PinkGradientView()
}
}

View File

@@ -21,7 +21,7 @@ struct IconLabel: View {
var body: some View {
HStack(spacing: 0) {
Image(systemName: img_name)
Image(img_name)
.foregroundColor(img_color)
.frame(width: 20)
.padding([.trailing], 20)

View File

@@ -52,24 +52,31 @@ enum ImageShape {
}
}
// MARK: - Image Carousel
@MainActor
struct ImageCarousel: View {
var urls: [URL]
var urls: [MediaUrl]
let evid: String
let evid: NoteId
let state: DamusState
@State private var open_sheet: Bool = false
@State private var current_url: URL? = nil
@State private var image_fill: ImageFill? = nil
@State private var fillHeight: CGFloat = 350
@State private var maxHeight: CGFloat = UIScreen.main.bounds.height * 1.2 // 1.2
@State private var firstImageHeight: CGFloat? = nil
@State private var currentImageHeight: CGFloat?
@State private var selectedIndex = 0
@State private var video_size: CGSize? = nil
let fillHeight: CGFloat = 350
let maxHeight: CGFloat = UIScreen.main.bounds.height * 1.2
init(state: DamusState, evid: String, urls: [URL]) {
init(state: DamusState, evid: NoteId, urls: [MediaUrl]) {
_open_sheet = State(initialValue: false)
_current_url = State(initialValue: nil)
_image_fill = State(initialValue: state.previews.lookup_image_meta(evid))
let media_model = state.events.get_cache_data(evid).media_metadata_model
_image_fill = State(initialValue: media_model.fill)
self.urls = urls
self.evid = evid
self.state = state
@@ -80,75 +87,150 @@ struct ImageCarousel: View {
}
var height: CGFloat {
image_fill?.height ?? fillHeight
firstImageHeight ?? image_fill?.height ?? fillHeight
}
func Placeholder(url: URL, geo_size: CGSize) -> some View {
func Placeholder(url: URL, geo_size: CGSize, num_urls: Int) -> some View {
Group {
if let meta = state.events.lookup_img_metadata(url: url),
if num_urls > 1 {
// jb55: quick hack since carousel with multiple images looks horrible with blurhash background
Color.clear
} else if let meta = state.events.lookup_img_metadata(url: url),
case .processed(let blurhash) = meta.state {
Image(uiImage: blurhash)
.resizable()
.frame(width: geo_size.width * UIScreen.main.scale, height: self.height * UIScreen.main.scale)
} else {
EmptyView()
Color.clear
}
}
.onAppear {
if self.image_fill == nil,
let meta = state.events.lookup_img_metadata(url: url),
let size = meta.meta.dim?.size
{
if self.image_fill == nil, let size = state.video.size_for_url(url) {
let fill = ImageFill.calculate_image_fill(geo_size: geo_size, img_size: size, maxHeight: maxHeight, fillHeight: fillHeight)
self.image_fill = fill
}
}
}
var body: some View {
TabView {
ForEach(urls, id: \.absoluteString) { url in
func Media(geo: GeometryProxy, url: MediaUrl, index: Int) -> some View {
Group {
switch url {
case .image(let url):
Img(geo: geo, url: url, index: index)
.onTapGesture {
open_sheet = true
}
case .video(let url):
DamusVideoPlayer(url: url, video_size: $video_size, controller: state.video)
.onChange(of: video_size) { size in
guard let size else { return }
let fill = ImageFill.calculate_image_fill(geo_size: geo.size, img_size: size, maxHeight: maxHeight, fillHeight: fillHeight)
print("video_size changed \(size)")
if self.image_fill == nil {
print("video_size firstImageHeight \(fill.height)")
firstImageHeight = fill.height
state.events.get_cache_data(evid).media_metadata_model.fill = fill
}
self.image_fill = fill
}
}
}
}
func Img(geo: GeometryProxy, url: URL, index: Int) -> some View {
KFAnimatedImage(url)
.callbackQueue(.dispatch(.global(qos:.background)))
.backgroundDecode(true)
.imageContext(.note, disable_animation: state.settings.disable_animation)
.image_fade(duration: 0.25)
.cancelOnDisappear(true)
.configure { view in
view.framePreloadCount = 3
}
.imageFill(for: geo.size, max: maxHeight, fill: fillHeight) { fill in
state.events.get_cache_data(evid).media_metadata_model.fill = fill
// blur hash can be discarded when we have the url
// NOTE: this is the wrong place for this... we need to remove
// it when the image is loaded in memory. This may happen
// earlier than this (by the preloader, etc)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
state.events.lookup_img_metadata(url: url)?.state = .not_needed
}
image_fill = fill
if index == 0 {
firstImageHeight = fill.height
//maxHeight = firstImageHeight ?? maxHeight
} else {
//maxHeight = firstImageHeight ?? fill.height
}
}
.background {
Placeholder(url: url, geo_size: geo.size, num_urls: urls.count)
}
.aspectRatio(contentMode: filling ? .fill : .fit)
.position(x: geo.size.width / 2, y: geo.size.height / 2)
.tabItem {
Text(url.absoluteString)
}
.id(url.absoluteString)
.padding(0)
}
var Medias: some View {
TabView(selection: $selectedIndex) {
ForEach(urls.indices, id: \.self) { index in
GeometryReader { geo in
KFAnimatedImage(url)
.callbackQueue(.dispatch(.global(qos:.background)))
.backgroundDecode(true)
.imageContext(.note, disable_animation: state.settings.disable_animation)
.image_fade(duration: 0.25)
.cancelOnDisappear(true)
.configure { view in
view.framePreloadCount = 3
}
.imageFill(for: geo.size, max: maxHeight, fill: fillHeight) { fill in
state.previews.cache_image_meta(evid: evid, image_fill: fill)
// blur hash can be discarded when we have the url
// NOTE: this is the wrong place for this... we need to remove
// it when the image is loaded in memory. This may happen
// earlier than this (by the preloader, etc)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
state.events.lookup_img_metadata(url: url)?.state = .not_needed
}
image_fill = fill
}
.background {
Placeholder(url: url, geo_size: geo.size)
}
.aspectRatio(contentMode: filling ? .fill : .fit)
.tabItem {
Text(url.absoluteString)
}
.id(url.absoluteString)
Media(geo: geo, url: urls[index], index: index)
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.fullScreenCover(isPresented: $open_sheet) {
ImageView(urls: urls, disable_animation: state.settings.disable_animation)
ImageView(video_controller: state.video, urls: urls, disable_animation: state.settings.disable_animation)
}
.frame(height: self.height)
.onTapGesture {
open_sheet = true
.frame(height: height)
.onChange(of: selectedIndex) { value in
selectedIndex = value
}
.tabViewStyle(PageTabViewStyle())
}
var body: some View {
VStack {
Medias
.onTapGesture { }
// This is our custom carousel image indicator
CarouselDotsView(urls: urls, 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())
}
}
}
// MARK: - Image Modifier
@@ -199,9 +281,11 @@ public struct ImageFill {
}
}
// MARK: - Preview Provider
struct ImageCarousel_Previews: PreviewProvider {
static var previews: some View {
ImageCarousel(state: test_damus_state(), evid: "evid", urls: [URL(string: "https://jb55.com/red-me.jpg")!,URL(string: "https://jb55.com/red-me.jpg")!])
let url: MediaUrl = .image(URL(string: "https://jb55.com/red-me.jpg")!)
ImageCarousel(state: test_damus_state, evid: test_note.id, urls: [url, url])
}
}

View File

@@ -9,7 +9,7 @@ import SwiftUI
struct InvoiceView: View {
@Environment(\.colorScheme) var colorScheme
let our_pubkey: String
let our_pubkey: Pubkey
let invoice: Invoice
@State var showing_select_wallet: Bool = false
@State var copied = false
@@ -25,10 +25,10 @@ struct InvoiceView: View {
UIPasteboard.general.string = invoice.string
} label: {
if !copied {
Image(systemName: "doc.on.clipboard")
Image("copy2")
.foregroundColor(.gray)
} else {
Image(systemName: "checkmark.circle")
Image("check-circle")
.foregroundColor(DamusColors.green)
}
}
@@ -37,7 +37,7 @@ struct InvoiceView: View {
var PayButton: some View {
Button {
if settings.show_wallet_selector {
showing_select_wallet = true
present_sheet(.select_wallet(invoice: invoice.string))
} else {
open_with_wallet(wallet: settings.default_wallet.model, invoice: invoice.string)
}
@@ -63,7 +63,7 @@ struct InvoiceView: View {
VStack(alignment: .leading, spacing: 12) {
HStack {
Label("", systemImage: "bolt.fill")
Label("", image: "zap.fill")
.foregroundColor(.orange)
Text("Lightning Invoice", comment: "Indicates that the view is for paying a Lightning invoice.")
Spacer()
@@ -79,9 +79,6 @@ struct InvoiceView: View {
}
.padding(30)
}
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
SelectWalletView(default_wallet: settings.default_wallet, showingSelectWallet: $showing_select_wallet, our_pubkey: our_pubkey, invoice: invoice.string)
}
}
}
@@ -111,8 +108,12 @@ let test_invoice = Invoice(description: .description("this is a description"), a
struct InvoiceView_Previews: PreviewProvider {
static var previews: some View {
InvoiceView(our_pubkey: "", invoice: test_invoice, settings: test_damus_state().settings)
InvoiceView(our_pubkey: .empty, invoice: test_invoice, settings: test_damus_state.settings)
.frame(width: 300, height: 200)
}
}
func present_sheet(_ sheet: Sheets) {
notify(.present_sheet(sheet))
}

View File

@@ -8,7 +8,7 @@
import SwiftUI
struct InvoicesView: View {
let our_pubkey: String
let our_pubkey: Pubkey
var invoices: [Invoice]
let settings: UserSettingsStore
@@ -29,7 +29,7 @@ struct InvoicesView: View {
struct InvoicesView_Previews: PreviewProvider {
static var previews: some View {
InvoicesView(our_pubkey: "", invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)], settings: test_damus_state().settings)
InvoicesView(our_pubkey: test_note.pubkey, invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)], settings: test_damus_state.settings)
.frame(width: 300)
}
}

View File

@@ -9,19 +9,19 @@ import SwiftUI
struct NIP05Badge: View {
let nip05: NIP05
let pubkey: String
let pubkey: Pubkey
let contacts: Contacts
let show_domain: Bool
let clickable: Bool
let profiles: Profiles
@Environment(\.openURL) var openURL
init (nip05: NIP05, pubkey: String, contacts: Contacts, show_domain: Bool, clickable: Bool) {
init(nip05: NIP05, pubkey: Pubkey, contacts: Contacts, show_domain: Bool, profiles: Profiles) {
self.nip05 = nip05
self.pubkey = pubkey
self.contacts = contacts
self.show_domain = show_domain
self.clickable = clickable
self.profiles = profiles
}
var nip05_color: Bool {
@@ -32,34 +32,47 @@ struct NIP05Badge: View {
Group {
if nip05_color {
LINEAR_GRADIENT
.mask(Image(systemName: "checkmark.seal.fill")
.mask(Image("verified.fill")
.resizable()
).frame(width: 14, height: 14)
).frame(width: 18, height: 18)
} else if show_domain {
Image(systemName: "checkmark.seal.fill")
.font(.footnote)
Image("verified")
.resizable()
.frame(width: 18, height: 18)
.nip05_colorized(gradient: nip05_color)
}
}
}
var username_matches_nip05: Bool {
guard let name = profiles.lookup(id: pubkey).map({ p in p?.name }).value
else {
return false
}
return name.lowercased() == nip05.username.lowercased()
}
var nip05_string: String {
if nip05.username == "_" || username_matches_nip05 {
return nip05.host
} else {
return "\(nip05.username)@\(nip05.host)"
}
}
var body: some View {
HStack(spacing: 2) {
Seal
if show_domain {
if clickable {
Text(nip05.host)
.nip05_colorized(gradient: nip05_color)
.onTapGesture {
if let nip5url = nip05.siteUrl {
openURL(nip5url)
}
Text(nip05_string)
.nip05_colorized(gradient: nip05_color)
.onTapGesture {
if let nip5url = nip05.siteUrl {
openURL(nip5url)
}
} else {
Text(nip05.host)
.foregroundColor(.gray)
}
}
}
}
@@ -77,14 +90,22 @@ extension View {
}
}
func use_nip05_color(pubkey: String, contacts: Contacts) -> Bool {
func use_nip05_color(pubkey: Pubkey, contacts: Contacts) -> Bool {
return contacts.is_friend_or_self(pubkey) ? true : false
}
struct NIP05Badge_Previews: PreviewProvider {
static var previews: some View {
let test_state = test_damus_state()
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, clickable: false)
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, profiles: test_state.profiles)
NIP05Badge(nip05: NIP05(username: "_", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: Contacts(our_pubkey: test_pubkey), show_domain: true, profiles: test_state.profiles)
}
}
}

View File

@@ -0,0 +1,49 @@
//
// NeutralButtonStyle.swift
// damus
//
// Created by eric on 9/1/23.
//
import SwiftUI
struct NeutralButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
return configuration.label
.background(DamusColors.neutral1)
.cornerRadius(12)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(DamusColors.neutral3, lineWidth: 1)
)
.scaleEffect(configuration.isPressed ? 0.95 : 1)
}
}
struct NeutralButtonStyle_Previews: PreviewProvider {
static var previews: some View {
VStack {
Button(action: {
print("dynamic size")
}) {
Text(verbatim: "Dynamic Size")
.padding()
}
.buttonStyle(NeutralButtonStyle())
Button(action: {
print("infinite width")
}) {
HStack {
Text(verbatim: "Infinite Width")
.padding()
}
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
}
.buttonStyle(NeutralButtonStyle())
.padding()
}
}
}

View File

@@ -9,16 +9,15 @@ import SwiftUI
struct Reposted: View {
let damus: DamusState
let pubkey: String
let profile: Profile?
let pubkey: Pubkey
var body: some View {
HStack(alignment: .center) {
Image(systemName: "arrow.2.squarepath")
Image("repost")
.foregroundColor(Color.gray)
ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_nip5_domain: false)
ProfileName(pubkey: pubkey, damus: damus, show_nip5_domain: false)
.foregroundColor(Color.gray)
Text("Reposted", comment: "Text indicating that the post was reposted (i.e. re-shared).")
Text("Reposted", comment: "Text indicating that the note was reposted (i.e. re-shared).")
.foregroundColor(Color.gray)
}
}
@@ -26,7 +25,7 @@ struct Reposted: View {
struct Reposted_Previews: PreviewProvider {
static var previews: some View {
let test_state = test_damus_state()
Reposted(damus: test_state, pubkey: test_state.pubkey, profile: make_test_profile())
let test_state = test_damus_state
Reposted(damus: test_state, pubkey: test_state.pubkey)
}
}

View File

@@ -0,0 +1,176 @@
//
// 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 {
switch described {
case .hashtag:
SingleCharacterAvatar(character: "#")
case .unknown:
SystemIconAvatar(system_name: "magnifyingglass")
}
}
}
var SearchText: Text {
Text(described.description)
}
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 {
HashtagUnfollowButton(damus_state: state, hashtag: ht, is_following: $is_following)
} else {
HashtagFollowButton(damus_state: state, hashtag: ht, is_following: $is_following)
}
}
}
}
.onReceive(handle_notify(.followed)) { ref in
guard hashtag_matches_search(desc: self.described, ref: ref) else { return }
self.is_following = true
}
.onReceive(handle_notify(.unfollowed)) { ref in
guard hashtag_matches_search(desc: self.described, ref: ref) else { return }
self.is_following = false
}
}
}
struct SystemIconAvatar: View {
let system_name: String
var body: some View {
NonImageAvatar {
Image(systemName: system_name)
.font(.title.bold())
}
}
}
struct SingleCharacterAvatar: View {
let character: String
var body: some View {
NonImageAvatar {
Text(verbatim: character)
.font(.largeTitle.bold())
.mask(Text(verbatim: character)
.font(.largeTitle.bold()))
}
}
}
struct NonImageAvatar<Content: View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
ZStack {
Circle()
.fill(Color(red: 0xF8/255.0, green: 0xE7/255.0, blue: 0xF8/255.0))
.frame(width: 54, height: 54)
content
.foregroundStyle(PinkGradient)
}
}
}
struct HashtagUnfollowButton: View {
let damus_state: DamusState
let hashtag: String
@Binding var is_following: Bool
var body: some View {
return Button(action: { unfollow(hashtag) }) {
Text("Unfollow hashtag", comment: "Button to unfollow a given hashtag.")
.font(.footnote.bold())
}
.buttonStyle(GradientButtonStyle(padding: 10))
}
func unfollow(_ hashtag: String) {
is_following = false
handle_unfollow(state: damus_state, unfollow: FollowRef.hashtag(hashtag))
}
}
struct HashtagFollowButton: View {
let damus_state: DamusState
let hashtag: String
@Binding var is_following: Bool
var body: some View {
return Button(action: { follow(hashtag) }) {
Text("Follow hashtag", comment: "Button to follow a given hashtag.")
.font(.footnote.bold())
}
.buttonStyle(GradientButtonStyle(padding: 10))
}
func follow(_ hashtag: String) {
is_following = true
handle_follow(state: damus_state, follow: .hashtag(hashtag))
}
}
func hashtag_matches_search(desc: DescribedSearch, ref: FollowRef) -> Bool {
guard case .hashtag(let follow_ht) = ref,
case .hashtag(let search_ht) = desc,
follow_ht == search_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: .hashtag(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)
}
}
}

View File

@@ -28,7 +28,11 @@ struct SelectableText: View {
)
.padding([.leading, .trailing], -1.0)
.onAppear {
self.selectedTextWidth = geo.size.width
if geo.size.width == .zero {
self.selectedTextHeight = 1000.0
} else {
self.selectedTextWidth = geo.size.width
}
}
.onChange(of: geo.size) { newSize in
self.selectedTextWidth = newSize.width

View File

@@ -0,0 +1,48 @@
//
// MusicController.swift
// damus
//
// Created by William Casarin on 2023-08-21.
//
import SwiftUI
import MediaPlayer
enum MusicState {
case playback_state(MPMusicPlaybackState)
case song(MPMediaItem?)
}
class MusicController {
let player: MPMusicPlayerController
let onChange: (MusicState) -> ()
init(onChange: @escaping (MusicState) -> ()) {
player = .systemMusicPlayer
player.beginGeneratingPlaybackNotifications()
self.onChange = onChange
print("Playback State: \(player.playbackState)")
print("Now Playing Item: \(player.nowPlayingItem?.title ?? "None")")
NotificationCenter.default.addObserver(self, selector: #selector(self.songChanged(notification:)), name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: player)
NotificationCenter.default.addObserver(self, selector: #selector(self.playbackStatusChanged(notification:)), name: .MPMusicPlayerControllerPlaybackStateDidChange, object: player)
}
deinit {
print("deinit musiccontroller")
}
@objc
func songChanged(notification: Notification) {
onChange(.song(player.nowPlayingItem))
}
@objc
func playbackStatusChanged(notification: Notification) {
onChange(.playback_state(player.playbackState))
}
}

View File

@@ -0,0 +1,183 @@
//
// UserStatus.swift
// damus
//
// Created by William Casarin on 2023-08-22.
//
import Foundation
import MediaPlayer
struct Song {
let started_playing: Date
let content: String
}
struct UserStatus {
let type: UserStatusType
let expires_at: Date?
var content: String
let created_at: UInt32
var url: URL?
func to_note(keypair: FullKeypair) -> NostrEvent? {
return make_user_status_note(status: self, keypair: keypair)
}
init(type: UserStatusType, expires_at: Date?, content: String, created_at: UInt32, url: URL? = nil) {
self.type = type
self.expires_at = expires_at
self.content = content
self.created_at = created_at
self.url = url
}
func expired() -> Bool {
guard let expires_at else { return false }
return Date.now >= expires_at
}
init?(ev: NostrEvent) {
guard let tag = ev.referenced_params.just_one() else {
return nil
}
let str = tag.param.string()
if str == "general" {
self.type = .general
} else if str == "music" {
self.type = .music
} else {
return nil
}
if let tag = ev.tags.first(where: { t in t.count >= 2 && t[0].matches_char("r") }),
tag.count >= 2,
let url = URL(string: tag[1].string())
{
self.url = url
} else {
self.url = nil
}
if let tag = ev.tags.first(where: { t in t.count >= 2 && t[0].matches_str("expiration") }),
tag.count == 2,
let expires = UInt32(tag[1].string())
{
self.expires_at = Date(timeIntervalSince1970: TimeInterval(expires))
} else {
self.expires_at = nil
}
self.content = ev.content
self.created_at = ev.created_at
}
}
enum UserStatusType: String {
case music
case general
}
class UserStatusModel: ObservableObject {
@Published var general: UserStatus?
@Published var music: UserStatus?
func update_status(_ s: UserStatus) {
// whitespace = delete
let del = s.content.allSatisfy({ c in c.isWhitespace })
switch s.type {
case .music:
if del {
self.music = nil
} else {
self.music = s
}
case .general:
if del {
self.general = nil
} else {
self.general = s
}
}
}
func try_expire() {
if let general, general.expired() {
self.general = nil
}
if let music, music.expired() {
self.music = nil
}
}
var _playing_enabled: Bool
var playing_enabled: Bool {
set {
var new_val = newValue
if newValue {
MPMediaLibrary.requestAuthorization { astatus in
switch astatus {
case .notDetermined: new_val = false
case .denied: new_val = false
case .restricted: new_val = false
case .authorized: new_val = true
@unknown default:
new_val = false
}
}
}
if new_val != playing_enabled {
_playing_enabled = new_val
self.objectWillChange.send()
}
}
get {
return _playing_enabled
}
}
init(playing: UserStatus? = nil, status: UserStatus? = nil) {
self.general = status
self.music = playing
self._playing_enabled = false
self.playing_enabled = false
}
static var current_track: String? {
let player = MPMusicPlayerController.systemMusicPlayer
guard let nowPlayingItem = player.nowPlayingItem else { return nil }
return nowPlayingItem.title
}
}
func make_user_status_note(status: UserStatus, keypair: FullKeypair, expiry: Date? = nil) -> NostrEvent?
{
var tags: [[String]] = [ ["d", status.type.rawValue] ]
if let expiry {
tags.append(["expiration", String(UInt32(expiry.timeIntervalSince1970))])
} else if let expiry = status.expires_at {
tags.append(["expiration", String(UInt32(expiry.timeIntervalSince1970))])
}
if let url = status.url {
tags.append(["r", url.absoluteString])
}
let kind = NostrKind.status.rawValue
guard let ev = NostrEvent(content: status.content, keypair: keypair.to_keypair(), kind: kind, tags: tags) else {
return nil
}
return ev
}

View File

@@ -0,0 +1,160 @@
//
// UserStatusSheet.swift
// damus
//
// Created by William Casarin on 2023-08-23.
//
import SwiftUI
enum StatusDuration: CustomStringConvertible, CaseIterable {
case never
case thirty_mins
case hour
case four_hours
case day
case week
var timeInterval: TimeInterval? {
switch self {
case .never:
return nil
case .thirty_mins:
return 60 * 30
case .hour:
return 60 * 60
case .four_hours:
return 60 * 60 * 4
case .day:
return 60 * 60 * 24
case .week:
return 60 * 60 * 24 * 7
}
}
var expiration: Date? {
guard let timeInterval else {
return nil
}
return Date.now.addingTimeInterval(timeInterval)
}
var description: String {
guard let timeInterval else {
return NSLocalizedString("Never", comment: "Profile status duration setting of never expiring.")
}
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
formatter.allowedUnits = [.minute, .hour, .day, .weekOfMonth]
return formatter.string(from: timeInterval) ?? "\(timeInterval) seconds"
}
}
struct UserStatusSheet: View {
let postbox: PostBox
let keypair: Keypair
@State var duration: StatusDuration = .never
@ObservedObject var status: UserStatusModel
@Environment(\.dismiss) var dismiss
var status_binding: Binding<String> {
Binding(get: {
status.general?.content ?? ""
}, set: { v in
if let general = status.general {
status.general = UserStatus(type: .general, expires_at: duration.expiration, content: v, created_at: UInt32(Date.now.timeIntervalSince1970), url: general.url)
} else {
status.general = UserStatus(type: .general, expires_at: duration.expiration, content: v, created_at: UInt32(Date.now.timeIntervalSince1970), url: nil)
}
})
}
var url_binding: Binding<String> {
Binding(get: {
status.general?.url?.absoluteString ?? ""
}, set: { v in
if let general = status.general {
status.general = UserStatus(type: .general, expires_at: duration.expiration, content: general.content, created_at: UInt32(Date.now.timeIntervalSince1970), url: URL(string: v))
} else {
status.general = UserStatus(type: .general, expires_at: duration.expiration, content: "", created_at: UInt32(Date.now.timeIntervalSince1970), url: URL(string: v))
}
})
}
var body: some View {
VStack(alignment: .leading, spacing: 20) {
Text("Set Status", comment: "Title of view that allows the user to set their profile status (e.g. working, studying, coding)")
.font(.largeTitle)
TextField(text: status_binding, label: {
Text("📋 Working", comment: "Placeholder as an example of what the user could set as their profile status.")
})
HStack {
Image("link")
TextField(text: url_binding, label: {
Text("https://example.com", comment: "Placeholder as an example of what the user could set so that the link is opened when the status is tapped.")
})
}
HStack {
Text("Clear status", comment: "Label to prompt user to select an expiration time for the profile status to clear.")
Spacer()
Picker(NSLocalizedString("Duration", comment: "Label for profile status expiration duration picker."), selection: $duration) {
ForEach(StatusDuration.allCases, id: \.self) { d in
Text(verbatim: d.description)
.tag(d)
}
}
}
Toggle(isOn: $status.playing_enabled, label: {
Text("Broadcast music playing on Apple Music", comment: "Toggle to enable or disable broadcasting what music is being played on Apple Music in their profile status.")
})
HStack(alignment: .center) {
Button(action: {
dismiss()
}, label: {
Text("Cancel", comment: "Cancel button text for dismissing profile status settings view.")
})
Spacer()
Button(action: {
guard let status = self.status.general,
let kp = keypair.to_full(),
let ev = make_user_status_note(status: status, keypair: kp, expiry: duration.expiration)
else {
return
}
postbox.send(ev)
dismiss()
}, label: {
Text("Save", comment: "Save button text for saving profile status settings.")
})
.buttonStyle(GradientButtonStyle())
}
.padding([.top], 30)
Spacer()
}
.padding(30)
}
}
struct UserStatusSheet_Previews: PreviewProvider {
static var previews: some View {
UserStatusSheet(postbox: test_damus_state.postbox, keypair: test_keypair, status: .init())
}
}

View File

@@ -0,0 +1,83 @@
//
// UserStatus.swift
// damus
//
// Created by William Casarin on 2023-08-21.
//
import SwiftUI
import MediaPlayer
import WebKit
struct UserStatusView: View {
@ObservedObject var status: UserStatusModel
var show_general: Bool
var show_music: Bool
@Environment(\.openURL) var openURL
func Status(st: UserStatus, prefix: String = "") -> some View {
HStack {
Text(verbatim: "\(prefix)\(st.content)")
.lineLimit(1)
.foregroundColor(.gray)
.font(.callout.italic())
if st.url != nil {
Image("link")
.resizable()
.frame(width: 16, height: 16)
.foregroundColor(.gray)
}
}
.onTapGesture {
if let url = st.url {
openURL(url)
}
}
.contextMenu(
menuItems: {
if let url = st.url {
Button(url.absoluteString, action: { openURL(url) }) }
}, preview: {
if let url = st.url {
URLPreview(url: url)
}
})
}
var body: some View {
VStack(alignment: .leading, spacing: 2) {
if show_general, let general = status.general {
Status(st: general)
}
if show_music, let playing = status.music {
Status(st: playing, prefix: "🎵")
}
}
}
struct URLPreview: UIViewRepresentable {
var url: URL
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ wkView: WKWebView, context: Context) {
let request = URLRequest(url: url)
wkView.load(request)
}
}
}
/*
struct UserStatusView_Previews: PreviewProvider {
static var previews: some View {
UserStatusView(status: UserStatus(type: .music, expires_at: nil, content: "Track - Artist", created_at: 0, url: URL(string: "spotify:search:abc")), show_general: true, show_music: true)
}
}
*/

View File

@@ -0,0 +1,73 @@
//
// SupporterBadge.swift
// damus
//
// Created by William Casarin on 2023-05-15.
//
import SwiftUI
struct SupporterBadge: View {
let percent: Int
let size: CGFloat = 17
var body: some View {
if percent < 100 {
Image("star.fill")
.resizable()
.frame(width:size, height:size)
.foregroundColor(support_level_color(percent))
} else {
Image("star.fill")
.resizable()
.frame(width:size, height:size)
.foregroundStyle(GoldGradient)
}
}
}
func support_level_color(_ percent: Int) -> Color {
if percent == 0 {
return .gray
}
let percent_f = Double(percent) / 100.0
let cutoff = 0.5
let h = cutoff + (percent_f * cutoff); // Hue (note 0.2 = Green, see huge chart below)
let s = 0.9; // Saturation
let b = 0.9; // Brightness
return Color(hue: h, saturation: s, brightness: b)
}
struct SupporterBadge_Previews: PreviewProvider {
static func Level(_ p: Int) -> some View {
HStack(alignment: .center) {
SupporterBadge(percent: p)
.frame(width: 50)
Text(verbatim: p.formatted())
.frame(width: 50)
}
}
static var previews: some View {
VStack(spacing: 0) {
VStack(spacing: 0) {
Level(1)
Level(10)
Level(20)
Level(30)
Level(40)
Level(50)
}
Level(60)
Level(70)
Level(80)
Level(90)
Level(100)
}
}
}

View File

@@ -10,7 +10,7 @@ import NaturalLanguage
struct Translated: Equatable {
let artifacts: NoteArtifacts
let artifacts: NoteArtifactsSeparated
let language: String
}
@@ -42,9 +42,10 @@ struct TranslateView: View {
.translate_button_style()
}
func TranslatedView(lang: String?, artifacts: NoteArtifacts) -> some View {
func TranslatedView(lang: String?, artifacts: NoteArtifactsSeparated, font_size: Double) -> some View {
return VStack(alignment: .leading) {
Text(String(format: NSLocalizedString("Translated from %@", comment: "Button to indicate that the note has been translated from a different language."), lang ?? "ja"))
let translatedFromLanguageString = 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)
.font(.footnote)
.padding([.top, .bottom], 10)
@@ -53,7 +54,7 @@ struct TranslateView: View {
SelectableText(attributedString: artifacts.content.attributed, size: self.size)
} else {
artifacts.content.text
.font(eventviewsize_to_font(self.size))
.font(eventviewsize_to_font(self.size, font_size: font_size))
}
}
}
@@ -63,7 +64,7 @@ struct TranslateView: View {
guard let note_language = translations_model.note_language else {
return
}
let res = await translate_note(profiles: damus_state.profiles, privkey: damus_state.keypair.privkey, event: event, settings: damus_state.settings, note_lang: note_language)
let res = await translate_note(profiles: damus_state.profiles, keypair: damus_state.keypair, event: event, settings: damus_state.settings, note_lang: note_language)
DispatchQueue.main.async {
self.translations_model.state = res
}
@@ -97,7 +98,7 @@ struct TranslateView: View {
Text("")
case .translated(let translated):
let languageName = Locale.current.localizedString(forLanguageCode: translated.language)
TranslatedView(lang: languageName, artifacts: translated.artifacts)
TranslatedView(lang: languageName, artifacts: translated.artifacts, font_size: damus_state.settings.font_size)
case .not_needed:
Text("")
}
@@ -119,16 +120,16 @@ extension View {
struct TranslateView_Previews: PreviewProvider {
static var previews: some View {
let ds = test_damus_state()
TranslateView(damus_state: ds, event: test_event, size: .normal)
let ds = test_damus_state
TranslateView(damus_state: ds, event: test_note, size: .normal)
}
}
func translate_note(profiles: Profiles, privkey: String?, event: NostrEvent, settings: UserSettingsStore, note_lang: String) async -> TranslateStatus {
func translate_note(profiles: Profiles, keypair: Keypair, event: NostrEvent, settings: UserSettingsStore, note_lang: String) async -> TranslateStatus {
// If the note language is different from our preferred languages, send a translation request.
let translator = Translator(settings)
let originalContent = event.get_content(privkey)
let originalContent = event.get_content(keypair)
let translated_note = try? await translator.translate(originalContent, from: note_lang, to: current_language())
guard let translated_note else {
@@ -142,7 +143,7 @@ func translate_note(profiles: Profiles, privkey: String?, event: NostrEvent, set
}
// Render translated note
let translated_blocks = event.get_blocks(content: translated_note)
let translated_blocks = parse_note_content(content: .content(translated_note, event.tags))
let artifacts = render_blocks(blocks: translated_blocks, profiles: profiles)
// and cache it

View File

@@ -12,7 +12,7 @@ struct TruncatedText: View {
let maxChars: Int = 280
var body: some View {
let truncatedAttributedString: AttributedString? = getTruncatedString()
let truncatedAttributedString: AttributedString? = text.attributed.truncateOrNil(maxLength: maxChars)
if let truncatedAttributedString {
Text(truncatedAttributedString)
@@ -28,16 +28,6 @@ struct TruncatedText: View {
.allowsHitTesting(false)
}
}
func getTruncatedString() -> AttributedString? {
let nsAttributedString = NSAttributedString(text.attributed)
if nsAttributedString.length < maxChars { return nil }
let range = NSRange(location: 0, length: maxChars)
let truncatedAttributedString = nsAttributedString.attributedSubstring(from: range)
return AttributedString(truncatedAttributedString) + "..."
}
}
struct TruncatedText_Previews: PreviewProvider {

View File

@@ -9,47 +9,45 @@ import SwiftUI
struct UserViewRow: View {
let damus_state: DamusState
let pubkey: String
@State var navigating: Bool = false
let pubkey: Pubkey
var body: some View {
let dest = ProfileView(damus_state: damus_state, pubkey: pubkey)
UserView(damus_state: damus_state, pubkey: pubkey)
.contentShape(Rectangle())
.background(
NavigationLink(destination: dest, isActive: $navigating) {
EmptyView()
}
)
.onTapGesture {
navigating = true
}
.background(.clear)
}
}
struct UserView: View {
let damus_state: DamusState
let pubkey: String
let pubkey: Pubkey
let spacer: Bool
@State var about_text: Text? = nil
init(damus_state: DamusState, pubkey: Pubkey, spacer: Bool = true) {
self.damus_state = damus_state
self.pubkey = pubkey
self.spacer = spacer
}
var body: some View {
VStack {
HStack {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation)
VStack(alignment: .leading) {
let profile = damus_state.profiles.lookup(id: pubkey)
ProfileName(pubkey: pubkey, profile: profile, damus: damus_state, show_nip5_domain: false)
if let about = profile?.about {
Text(about)
ProfileName(pubkey: pubkey, damus: damus_state, show_nip5_domain: false)
if let about_text {
about_text
.lineLimit(3)
.font(.footnote)
}
}
Spacer()
if spacer {
Spacer()
}
}
}
}
@@ -57,6 +55,6 @@ struct UserView: View {
struct UserView_Previews: PreviewProvider {
static var previews: some View {
UserView(damus_state: test_damus_state(), pubkey: "pk")
UserView(damus_state: test_damus_state, pubkey: test_note.pubkey)
}
}

View File

@@ -13,7 +13,7 @@ struct WebsiteLink: View {
var body: some View {
HStack {
Image(systemName: "link")
Image("link")
.foregroundColor(.gray)
.font(.footnote)
@@ -23,6 +23,8 @@ struct WebsiteLink: View {
Text(link_text)
.font(.footnote)
.foregroundColor(.accentColor)
.truncationMode(.tail)
.lineLimit(1)
})
}
}

View File

@@ -10,46 +10,41 @@ import SwiftUI
enum ZappingEventType {
case failed(ZappingError)
case got_zap_invoice(String)
case sent_from_nwc
}
enum ZappingError {
case fetching_invoice
case bad_lnurl
case canceled
case send_failed
}
struct ZappingEvent {
let is_custom: Bool
let type: ZappingEventType
let event: NostrEvent
}
class ZapButtonModel: ObservableObject {
var invoice: String? = nil
@Published var zapping: String = ""
@Published var showing_select_wallet: Bool = false
@Published var showing_zap_customizer: Bool = false
let target: ZapTarget
}
struct ZapButton: View {
let damus_state: DamusState
let event: NostrEvent
let target: ZapTarget
let lnurl: String
@ObservedObject var zaps: ZapsDataModel
@StateObject var button: ZapButtonModel = ZapButtonModel()
var our_zap: Zapping? {
zaps.zaps.first(where: { z in z.request.pubkey == damus_state.pubkey })
zaps.zaps.first(where: { z in z.request.ev.pubkey == damus_state.pubkey })
}
var zap_img: String {
switch our_zap {
case .none:
return "bolt"
return "zap"
case .zap:
return "bolt.fill"
return "zap.fill"
case .pending:
return "bolt.fill"
return "zap.fill"
}
}
@@ -60,18 +55,11 @@ struct ZapButton: View {
// always orange !
return Color.orange
/*
if our_zap.is_paid {
return Color.orange
} else {
return Color.yellow
}
*/
}
func tap() {
guard let our_zap else {
send_zap(damus_state: damus_state, event: event, lnurl: lnurl, is_custom: false, comment: nil, amount_sats: nil, zap_type: damus_state.settings.default_zap_type)
send_zap(damus_state: damus_state, target: target, lnurl: lnurl, is_custom: false, comment: nil, amount_sats: nil, zap_type: damus_state.settings.default_zap_type)
return
}
@@ -118,12 +106,17 @@ struct ZapButton: View {
var body: some View {
HStack(spacing: 4) {
Button(action: {
}, label: {
Image(systemName: zap_img)
.foregroundColor(zap_color)
.font(.footnote.weight(.medium))
})
if !damus_state.settings.nozaps || zaps.zap_total > 0 {
Button(action: {
}, label: {
Image(zap_img)
.resizable()
.foregroundColor(zap_color)
.font(.footnote.weight(.medium))
.aspectRatio(contentMode: .fit)
.frame(width:20, height: 20)
})
}
if zaps.zap_total > 0 {
Text(verbatim: format_msats_abbrev(zaps.zap_total))
@@ -133,51 +126,25 @@ struct ZapButton: View {
}
.accessibilityLabel(NSLocalizedString("Zap", comment: "Accessibility label for zap button"))
.simultaneousGesture(LongPressGesture().onEnded {_ in
button.showing_zap_customizer = true
guard !damus_state.settings.nozaps else { return }
present_sheet(.zap(target: target, lnurl: lnurl))
})
.highPriorityGesture(TapGesture().onEnded {
guard !damus_state.settings.nozaps else { return }
tap()
})
.sheet(isPresented: $button.showing_zap_customizer) {
CustomizeZapView(state: damus_state, event: event, lnurl: lnurl)
}
.sheet(isPresented: $button.showing_select_wallet, onDismiss: {button.showing_select_wallet = false}) {
SelectWalletView(default_wallet: damus_state.settings.default_wallet, showingSelectWallet: $button.showing_select_wallet, our_pubkey: damus_state.pubkey, invoice: button.invoice ?? "")
}
.onReceive(handle_notify(.zapping)) { notif in
let zap_ev = notif.object as! ZappingEvent
guard zap_ev.event.id == self.event.id else {
return
}
guard !zap_ev.is_custom else {
return
}
switch zap_ev.type {
case .failed:
break
case .got_zap_invoice(let inv):
if damus_state.settings.show_wallet_selector {
self.button.invoice = inv
self.button.showing_select_wallet = true
} else {
let wallet = damus_state.settings.default_wallet.model
open_with_wallet(wallet: wallet, invoice: inv)
}
}
}
}
}
struct ZapButton_Previews: PreviewProvider {
static var previews: some View {
let pending_zap = PendingZap(amount_msat: 1000, target: ZapTarget.note(id: "noteid", author: "author"), request: .normal(test_zap_request), type: .pub, state: .external(.init(state: .fetching_invoice)))
let pending_zap = PendingZap(amount_msat: 1000, target: ZapTarget.note(id: test_note.id, author: test_note.pubkey), request: .normal(test_zap_request), type: .pub, state: .external(.init(state: .fetching_invoice)))
let zaps = ZapsDataModel([.pending(pending_zap)])
ZapButton(damus_state: test_damus_state(), event: test_event, lnurl: "lnurl", zaps: zaps)
ZapButton(damus_state: test_damus_state, target: ZapTarget.note(id: test_note.id, author: test_note.pubkey), lnurl: "lnurl", zaps: zaps)
}
}
@@ -193,14 +160,13 @@ func initial_pending_zap_state(settings: UserSettingsStore) -> PendingZapState {
return .external(ExtPendingZapState(state: .fetching_invoice))
}
func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_custom: Bool, comment: String?, amount_sats: Int?, zap_type: ZapType) {
func send_zap(damus_state: DamusState, target: ZapTarget, lnurl: String, is_custom: Bool, comment: String?, amount_sats: Int?, zap_type: ZapType) {
guard let keypair = damus_state.keypair.to_full() else {
return
}
// Only take the first 10 because reasons
let relays = Array(damus_state.pool.our_descriptors.prefix(10))
let target = ZapTarget.note(id: event.id, author: event.pubkey)
let content = comment ?? ""
guard let mzapreq = make_zap_request_event(keypair: keypair, content: content, relays: relays, target: target, zap_type: zap_type) else {
@@ -217,68 +183,74 @@ func send_zap(damus_state: DamusState, event: NostrEvent, lnurl: String, is_cust
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
damus_state.add_zap(zap: .pending(pending_zap))
Task {
var mpayreq = damus_state.lnurls.lookup(target.pubkey)
if mpayreq == nil {
mpayreq = await fetch_static_payreq(lnurl)
}
guard let payreq = mpayreq else {
Task { @MainActor in
guard let payreq = await damus_state.lnurls.lookup_or_fetch(pubkey: target.pubkey, lnurl: lnurl) else {
// TODO: show error
DispatchQueue.main.async {
remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events)
let typ = ZappingEventType.failed(.bad_lnurl)
let ev = ZappingEvent(is_custom: is_custom, type: typ, event: event)
notify(.zapping, ev)
}
remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events)
let typ = ZappingEventType.failed(.bad_lnurl)
let ev = ZappingEvent(is_custom: is_custom, type: typ, target: target)
notify(.zapping(ev))
return
}
DispatchQueue.main.async {
damus_state.lnurls.endpoints[target.pubkey] = payreq
}
guard let inv = await fetch_zap_invoice(payreq, zapreq: zapreq, msats: amount_msat, zap_type: zap_type, comment: comment) else {
DispatchQueue.main.async {
remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events)
let typ = ZappingEventType.failed(.fetching_invoice)
let ev = ZappingEvent(is_custom: is_custom, type: typ, event: event)
notify(.zapping, ev)
}
remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events)
let typ = ZappingEventType.failed(.fetching_invoice)
let ev = ZappingEvent(is_custom: is_custom, type: typ, target: target)
notify(.zapping(ev))
return
}
DispatchQueue.main.async {
switch pending_zap_state {
case .nwc(let nwc_state):
// don't both continuing, user has canceled
if case .cancel_fetching_invoice = nwc_state.state {
remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events)
return
}
let nwc_req = nwc_pay(url: nwc_state.url, pool: damus_state.pool, post: damus_state.postbox, invoice: inv, on_flush: .once({ pe in
switch pending_zap_state {
case .nwc(let nwc_state):
// don't both continuing, user has canceled
if case .cancel_fetching_invoice = nwc_state.state {
remove_zap(reqid: reqid, zapcache: damus_state.zaps, evcache: damus_state.events)
let typ = ZappingEventType.failed(.canceled)
let ev = ZappingEvent(is_custom: is_custom, type: typ, target: target)
notify(.zapping(ev))
return
}
var flusher: OnFlush? = nil
// donations are only enabled on one-tap zaps and off appstore
if !damus_state.settings.nozaps && !is_custom && damus_state.settings.donation_percent > 0 {
flusher = .once({ pe in
// send donation zap when the pending zap is flushed, this allows user to cancel and not send a donation
Task.init { @MainActor in
Task { @MainActor in
await send_donation_zap(pool: damus_state.pool, postbox: damus_state.postbox, nwc: nwc_state.url, percent: damus_state.settings.donation_percent, base_msats: amount_msat)
}
}))
guard let nwc_req, case .nwc(let pzap_state) = pending_zap_state else {
return
}
if pzap_state.update_state(state: .postbox_pending(nwc_req)) {
// we don't need to trigger a ZapsDataModel update here
}
case .external(let pending_ext):
pending_ext.state = .done
let ev = ZappingEvent(is_custom: is_custom, type: .got_zap_invoice(inv), event: event)
notify(.zapping, ev)
})
}
// we don't have a delay on one-tap nozaps (since this will be from customize zap view)
let delay = damus_state.settings.nozaps ? nil : 5.0
let nwc_req = nwc_pay(url: nwc_state.url, pool: damus_state.pool, post: damus_state.postbox, invoice: inv, delay: delay, on_flush: flusher)
guard let nwc_req, case .nwc(let pzap_state) = pending_zap_state else {
print("nwc: failed to send nwc request for zapreq \(reqid.reqid)")
let typ = ZappingEventType.failed(.send_failed)
let ev = ZappingEvent(is_custom: is_custom, type: typ, target: target)
notify(.zapping(ev))
return
}
print("nwc: sending request \(nwc_req.id) zap_req_id \(reqid.reqid)")
if pzap_state.update_state(state: .postbox_pending(nwc_req)) {
// we don't need to trigger a ZapsDataModel update here
}
let ev = ZappingEvent(is_custom: is_custom, type: .sent_from_nwc, target: target)
notify(.zapping(ev))
case .external(let pending_ext):
pending_ext.state = .done
let ev = ZappingEvent(is_custom: is_custom, type: .got_zap_invoice(inv), target: target)
notify(.zapping(ev))
}
}

124
damus/ContentParsing.swift Normal file
View File

@@ -0,0 +1,124 @@
//
// ContentParsing.swift
// damus
//
// Created by William Casarin on 2023-07-22.
//
import Foundation
enum NoteContent {
case note(NostrEvent)
case content(String, TagsSequence?)
init(note: NostrEvent, keypair: Keypair) {
if note.known_kind == .dm {
self = .content(note.get_content(keypair), note.tags)
} else {
self = .note(note)
}
}
}
func parsed_blocks_finish(bs: inout note_blocks, tags: TagsSequence?) -> Blocks {
var out: [Block] = []
var i = 0
while (i < bs.num_blocks) {
let block = bs.blocks[i]
if let converted = Block(block, tags: tags) {
out.append(converted)
}
i += 1
}
let words = Int(bs.words)
blocks_free(&bs)
return Blocks(words: words, blocks: out)
}
func parse_note_content(content: NoteContent) -> Blocks {
var bs = note_blocks()
bs.num_blocks = 0;
blocks_init(&bs)
switch content {
case .content(let s, let tags):
return s.withCString { cptr in
damus_parse_content(&bs, cptr)
return parsed_blocks_finish(bs: &bs, tags: tags)
}
case .note(let note):
damus_parse_content(&bs, note.content_raw)
return parsed_blocks_finish(bs: &bs, tags: note.tags)
}
}
func interpret_event_refs_ndb(blocks: [Block], tags: TagsSequence) -> [EventRef] {
if tags.count == 0 {
return []
}
/// build a set of indices for each event mention
let mention_indices = build_mention_indices(blocks, type: .e)
/// simpler case with no mentions
if mention_indices.count == 0 {
return interp_event_refs_without_mentions_ndb(tags.note.referenced_noterefs)
}
return interp_event_refs_with_mentions_ndb(tags: tags, mention_indices: mention_indices)
}
func interp_event_refs_without_mentions_ndb(_ ev_tags: References<NoteRef>) -> [EventRef] {
var count = 0
var evrefs: [EventRef] = []
var first: Bool = true
var first_ref: NoteRef? = nil
for ref in ev_tags {
if first {
first_ref = ref
evrefs.append(.thread_id(ref))
first = false
} else {
evrefs.append(.reply(ref))
}
count += 1
}
if let first_ref, count == 1 {
let r = first_ref
return [.reply_to_root(r)]
}
return evrefs
}
func interp_event_refs_with_mentions_ndb(tags: TagsSequence, mention_indices: Set<Int>) -> [EventRef] {
var mentions: [EventRef] = []
var ev_refs: [NoteRef] = []
var i: Int = 0
for tag in tags {
if let note_id = NoteRef.from_tag(tag: tag) {
if mention_indices.contains(i) {
mentions.append(.mention(.noteref(note_id, index: i)))
} else {
ev_refs.append(note_id)
}
}
i += 1
}
var replies = interp_event_refs_without_mentions(ev_refs)
replies.append(contentsOf: mentions)
return replies
}

File diff suppressed because it is too large Load Diff

View File

@@ -68,6 +68,8 @@
</dict>
<key>NSCameraUsageDescription</key>
<string>Damus needs access to your camera if you want to upload photos from it</string>
<key>NSAppleMusicUsageDescription</key>
<string>Damus needs access to your media library for playback statuses</string>
<key>NSMicrophoneUsageDescription</key>
<string>Damus needs access to your microphone if you want to upload recorded videos from it</string>
</dict>

View File

@@ -20,7 +20,7 @@ class ActionBarModel: ObservableObject {
@Published var our_zap: Zapping?
@Published var likes: Int
@Published var boosts: Int
@Published var zaps: Int
@Published private(set) var zaps: Int
@Published var zap_total: Int64
@Published var replies: Int
@@ -28,19 +28,7 @@ class ActionBarModel: ObservableObject {
return ActionBarModel(likes: 0, boosts: 0, zaps: 0, zap_total: 0, replies: 0, our_like: nil, our_boost: nil, our_zap: nil, our_reply: nil)
}
init() {
self.our_like = nil
self.our_boost = nil
self.our_reply = nil
self.our_zap = nil
self.likes = 0
self.boosts = 0
self.zaps = 0
self.zap_total = 0
self.replies = 0
}
init(likes: Int, boosts: Int, zaps: Int, zap_total: Int64, replies: Int, our_like: NostrEvent?, our_boost: NostrEvent?, our_zap: Zapping?, our_reply: NostrEvent?) {
init(likes: Int = 0, boosts: Int = 0, zaps: Int = 0, zap_total: Int64 = 0, replies: Int = 0, our_like: NostrEvent? = nil, our_boost: NostrEvent? = nil, our_zap: Zapping? = nil, our_reply: NostrEvent? = nil) {
self.likes = likes
self.boosts = boosts
self.zaps = zaps
@@ -52,7 +40,7 @@ class ActionBarModel: ObservableObject {
self.our_reply = our_reply
}
func update(damus: DamusState, evid: String) {
func update(damus: DamusState, evid: NoteId) {
self.likes = damus.likes.counts[evid] ?? 0
self.boosts = damus.boosts.counts[evid] ?? 0
self.zaps = damus.zaps.event_counts[evid] ?? 0

View File

@@ -7,18 +7,18 @@
import Foundation
fileprivate func get_bookmarks_key(pubkey: String) -> String {
fileprivate func get_bookmarks_key(pubkey: Pubkey) -> String {
pk_setting_key(pubkey, key: "bookmarks")
}
func load_bookmarks(pubkey: String) -> [NostrEvent] {
func load_bookmarks(pubkey: Pubkey) -> [NostrEvent] {
let key = get_bookmarks_key(pubkey: pubkey)
return (UserDefaults.standard.stringArray(forKey: key) ?? []).compactMap {
event_from_json(dat: $0)
}
}
func save_bookmarks(pubkey: String, current_value: [NostrEvent], value: [NostrEvent]) -> Bool {
func save_bookmarks(pubkey: Pubkey, current_value: [NostrEvent], value: [NostrEvent]) -> Bool {
let uniq_bookmarks = uniq(value)
if uniq_bookmarks != current_value {
@@ -32,8 +32,8 @@ func save_bookmarks(pubkey: String, current_value: [NostrEvent], value: [NostrEv
class BookmarksManager: ObservableObject {
private let pubkey: String
private let pubkey: Pubkey
private var _bookmarks: [NostrEvent]
var bookmarks: [NostrEvent] {
get {
@@ -47,7 +47,7 @@ class BookmarksManager: ObservableObject {
}
}
init(pubkey: String) {
init(pubkey: Pubkey) {
self._bookmarks = load_bookmarks(pubkey: pubkey)
self.pubkey = pubkey
}

View File

@@ -9,136 +9,162 @@ import Foundation
class Contacts {
private var friends: Set<String> = Set()
private var friend_of_friends: Set<String> = Set()
private var muted: Set<String> = Set()
let our_pubkey: String
private var friends: Set<Pubkey> = Set()
private var friend_of_friends: Set<Pubkey> = Set()
/// Tracks which friends are friends of a given pubkey.
private var pubkey_to_our_friends = [Pubkey : Set<Pubkey>]()
private var muted: Set<Pubkey> = Set()
let our_pubkey: Pubkey
var event: NostrEvent?
var mutelist: NostrEvent?
init(our_pubkey: String) {
init(our_pubkey: Pubkey) {
self.our_pubkey = our_pubkey
}
func is_muted(_ pk: String) -> Bool {
func is_muted(_ pk: Pubkey) -> Bool {
return muted.contains(pk)
}
func set_mutelist(_ ev: NostrEvent) {
let oldlist = self.mutelist
self.mutelist = ev
let old = Set(oldlist?.referenced_pubkeys.map({ $0.ref_id }) ?? [])
let new = Set(ev.referenced_pubkeys.map({ $0.ref_id }))
let old = oldlist.map({ ev in Set(ev.referenced_pubkeys) }) ?? Set<Pubkey>()
let new = Set(ev.referenced_pubkeys)
let diff = old.symmetricDifference(new)
var new_mutes = Array<String>()
var new_unmutes = Array<String>()
var new_mutes = Set<Pubkey>()
var new_unmutes = Set<Pubkey>()
for d in diff {
if new.contains(d) {
new_mutes.append(d)
new_mutes.insert(d)
} else {
new_unmutes.append(d)
new_unmutes.insert(d)
}
}
// TODO: set local mutelist here
self.muted = Set(ev.referenced_pubkeys.map({ $0.ref_id }))
self.muted = Set(ev.referenced_pubkeys)
if new_mutes.count > 0 {
notify(.new_mutes, new_mutes)
notify(.new_mutes(new_mutes))
}
if new_unmutes.count > 0 {
notify(.new_unmutes, new_unmutes)
notify(.new_unmutes(new_unmutes))
}
}
func remove_friend(_ pubkey: String) {
func remove_friend(_ pubkey: Pubkey) {
friends.remove(pubkey)
pubkey_to_our_friends.forEach {
pubkey_to_our_friends[$0.key]?.remove(pubkey)
}
}
func get_friend_list() -> [String] {
return Array(friends)
func get_friend_list() -> Set<Pubkey> {
return friends
}
func get_followed_hashtags() -> Set<String> {
guard let ev = self.event else { return Set() }
return Set(ev.referenced_hashtags.map({ $0.hashtag }))
}
func add_friend_pubkey(_ pubkey: String) {
func follows(hashtag: Hashtag) -> Bool {
guard let ev = self.event else { return false }
return ev.referenced_hashtags.first(where: { $0 == hashtag }) != nil
}
func add_friend_pubkey(_ pubkey: Pubkey) {
friends.insert(pubkey)
}
func add_friend_contact(_ contact: NostrEvent) {
friends.insert(contact.pubkey)
for tag in contact.tags {
if tag.count >= 2 && tag[0] == "p" {
friend_of_friends.insert(tag[1])
for pk in contact.referenced_pubkeys {
friend_of_friends.insert(pk)
// Exclude themself and us.
if contact.pubkey != our_pubkey && contact.pubkey != pk {
if pubkey_to_our_friends[pk] == nil {
pubkey_to_our_friends[pk] = Set<Pubkey>()
}
pubkey_to_our_friends[pk]?.insert(contact.pubkey)
}
}
}
func is_friend_of_friend(_ pubkey: String) -> Bool {
func is_friend_of_friend(_ pubkey: Pubkey) -> Bool {
return friend_of_friends.contains(pubkey)
}
func is_in_friendosphere(_ pubkey: String) -> Bool {
func is_in_friendosphere(_ pubkey: Pubkey) -> Bool {
return friends.contains(pubkey) || friend_of_friends.contains(pubkey)
}
func is_friend(_ pubkey: String) -> Bool {
func is_friend(_ pubkey: Pubkey) -> Bool {
return friends.contains(pubkey)
}
func is_friend_or_self(_ pubkey: String) -> Bool {
func is_friend_or_self(_ pubkey: Pubkey) -> Bool {
return pubkey == our_pubkey || is_friend(pubkey)
}
func follow_state(_ pubkey: String) -> FollowState {
func follow_state(_ pubkey: Pubkey) -> FollowState {
return is_friend(pubkey) ? .follows : .unfollows
}
/// Gets the list of pubkeys of our friends who follow the given pubkey.
func get_friended_followers(_ pubkey: Pubkey) -> [Pubkey] {
return Array((pubkey_to_our_friends[pubkey] ?? Set()))
}
}
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: pubkey, follow: follow) else {
func follow_reference(box: PostBox, our_contacts: NostrEvent?, keypair: FullKeypair, follow: FollowRef) -> NostrEvent? {
guard let ev = follow_user_event(our_contacts: our_contacts, keypair: keypair, follow: follow) else {
return nil
}
ev.calculate_id()
ev.sign(privkey: privkey)
pool.send(.event(ev))
box.send(ev)
return ev
}
func unfollow_user(postbox: PostBox, our_contacts: NostrEvent?, pubkey: String, privkey: String, unfollow: String) -> NostrEvent? {
func unfollow_reference(postbox: PostBox, our_contacts: NostrEvent?, keypair: FullKeypair, unfollow: FollowRef) -> NostrEvent? {
guard let cs = our_contacts else {
return nil
}
let ev = unfollow_user_event(our_contacts: cs, our_pubkey: pubkey, unfollow: unfollow)
ev.calculate_id()
ev.sign(privkey: privkey)
guard let ev = unfollow_reference_event(our_contacts: cs, keypair: keypair, unfollow: unfollow) else {
return nil
}
postbox.send(ev)
return ev
}
func unfollow_user_event(our_contacts: NostrEvent, our_pubkey: String, unfollow: String) -> NostrEvent {
let tags = our_contacts.tags.filter { tag in
if tag.count >= 2 && tag[0] == "p" && tag[1] == unfollow {
return false
func unfollow_reference_event(our_contacts: NostrEvent, keypair: FullKeypair, unfollow: FollowRef) -> NostrEvent? {
let tags = our_contacts.tags.reduce(into: [[String]]()) { ts, tag in
if let tag = FollowRef.from_tag(tag: tag), tag == unfollow {
return
}
return true
ts.append(tag.strings())
}
let kind = NostrKind.contacts.rawValue
return NostrEvent(content: our_contacts.content, pubkey: our_pubkey, kind: kind, tags: tags)
return NostrEvent(content: our_contacts.content, keypair: keypair.to_keypair(), kind: kind, tags: Array(tags))
}
func follow_user_event(our_contacts: NostrEvent?, our_pubkey: String, follow: ReferencedId) -> NostrEvent? {
func follow_user_event(our_contacts: NostrEvent?, keypair: FullKeypair, follow: FollowRef) -> NostrEvent? {
guard let cs = our_contacts else {
// don't create contacts for now so we don't nuke our contact list due to connectivity issues
// we should only create contacts during profile creation
@@ -146,7 +172,7 @@ func follow_user_event(our_contacts: NostrEvent?, our_pubkey: String, follow: Re
return nil
}
guard let ev = follow_with_existing_contacts(our_pubkey: our_pubkey, our_contacts: cs, follow: follow) else {
guard let ev = follow_with_existing_contacts(keypair: keypair, our_contacts: cs, follow: follow) else {
return nil
}
@@ -158,23 +184,18 @@ func decode_json_relays(_ content: String) -> [String: RelayInfo]? {
return decode_json(content)
}
func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: String, relay: String) -> NostrEvent? {
func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], keypair: FullKeypair, relay: String) -> NostrEvent?{
var relays = ensure_relay_info(relays: current_relays, content: ev.content)
relays.removeValue(forKey: relay)
print("remove_relay \(relays)")
guard let content = encode_json(relays) else {
return nil
}
let new_ev = NostrEvent(content: content, pubkey: ev.pubkey, kind: 3, tags: ev.tags)
new_ev.calculate_id()
new_ev.sign(privkey: privkey)
return new_ev
return NostrEvent(content: content, keypair: keypair.to_keypair(), kind: 3, tags: ev.tags.strings())
}
func add_relay(ev: NostrEvent, privkey: String, current_relays: [RelayDescriptor], relay: String, info: RelayInfo) -> NostrEvent? {
func add_relay(ev: NostrEvent, keypair: FullKeypair, current_relays: [RelayDescriptor], relay: String, info: RelayInfo) -> NostrEvent? {
var relays = ensure_relay_info(relays: current_relays, content: ev.content)
guard relays.index(forKey: relay) == nil else {
@@ -187,10 +208,7 @@ func add_relay(ev: NostrEvent, privkey: String, current_relays: [RelayDescriptor
return nil
}
let new_ev = NostrEvent(content: content, pubkey: ev.pubkey, kind: 3, tags: ev.tags)
new_ev.calculate_id()
new_ev.sign(privkey: privkey)
return new_ev
return NostrEvent(content: content, keypair: keypair.to_keypair(), kind: 3, tags: ev.tags.strings())
}
func ensure_relay_info(relays: [RelayDescriptor], content: String) -> [String: RelayInfo] {
@@ -200,16 +218,31 @@ func ensure_relay_info(relays: [RelayDescriptor], content: String) -> [String: R
return relay_info
}
func follow_with_existing_contacts(our_pubkey: String, our_contacts: NostrEvent, follow: ReferencedId) -> NostrEvent? {
func is_already_following(contacts: NostrEvent, follow: FollowRef) -> Bool {
return contacts.references.contains { ref in
switch (ref, follow) {
case let (.hashtag(ht), .hashtag(follow_ht)):
return ht.string() == follow_ht
case let (.pubkey(pk), .pubkey(follow_pk)):
return pk == follow_pk
case (.hashtag, .pubkey), (.pubkey, .hashtag),
(.event, _), (.quote, _), (.param, _):
return false
}
}
}
func follow_with_existing_contacts(keypair: FullKeypair, our_contacts: NostrEvent, follow: FollowRef) -> NostrEvent? {
// don't update if we're already following
if our_contacts.references(id: follow.ref_id, key: "p") {
if is_already_following(contacts: our_contacts, follow: follow) {
return nil
}
let kind = NostrKind.contacts.rawValue
var tags = our_contacts.tags
tags.append(refid_to_tag(follow))
return NostrEvent(content: our_contacts.content, pubkey: our_pubkey, kind: kind, tags: tags)
var tags = our_contacts.tags.strings()
tags.append(follow.tag)
return NostrEvent(content: our_contacts.content, keypair: keypair.to_keypair(), kind: kind, tags: tags)
}
func make_contact_relays(_ relays: [RelayDescriptor]) -> [String: RelayInfo] {

View File

@@ -0,0 +1,54 @@
//
// ContentFilters.swift
// damus
//
// Created by Daniel DAquino on 2023-09-18.
//
import Foundation
/// Simple filter to determine whether to show posts or all posts and replies.
enum FilterState : Int {
case posts_and_replies = 1
case posts = 0
func filter(ev: NostrEvent) -> Bool {
switch self {
case .posts:
return ev.known_kind == .boost || !ev.is_reply(.empty)
case .posts_and_replies:
return true
}
}
}
/// Simple filter to determine whether to show posts with #nsfw tags
func nsfw_tag_filter(ev: NostrEvent) -> Bool {
return ev.referenced_hashtags.first(where: { t in t.hashtag == "nsfw" }) == nil
}
/// Generic filter with various tweakable settings
struct ContentFilters {
var filters: [(NostrEvent) -> Bool]
func filter(ev: NostrEvent) -> Bool {
for filter in filters {
if !filter(ev) {
return false
}
}
return true
}
}
extension ContentFilters {
static func defaults(_ settings: UserSettingsStore) -> [(NostrEvent) -> Bool] {
var filters = Array<(NostrEvent) -> Bool>()
if settings.hide_nsfw_tagged_content {
filters.append(nsfw_tag_filter)
}
return filters
}
}

View File

@@ -12,18 +12,10 @@ class CreateAccountModel: ObservableObject {
@Published var real_name: String = ""
@Published var nick_name: String = ""
@Published var about: String = ""
@Published var pubkey: String = ""
@Published var privkey: String = ""
@Published var profile_image: String? = nil
var pubkey_bech32: String {
return bech32_pubkey(self.pubkey) ?? ""
}
var privkey_bech32: String {
return bech32_privkey(self.privkey) ?? ""
}
@Published var pubkey: Pubkey = .empty
@Published var privkey: Privkey = .empty
@Published var profile_image: URL? = nil
var rendered_name: String {
if real_name.isEmpty {
return nick_name
@@ -35,17 +27,11 @@ class CreateAccountModel: ObservableObject {
return Keypair(pubkey: self.pubkey, privkey: self.privkey)
}
init() {
init(real: String = "", nick: String = "", about: String = "") {
let keypair = generate_new_keypair()
self.pubkey = keypair.pubkey
self.privkey = keypair.privkey!
}
init(real: String, nick: String, about: String) {
let keypair = generate_new_keypair()
self.pubkey = keypair.pubkey
self.privkey = keypair.privkey!
self.privkey = keypair.privkey
self.real_name = real
self.nick_name = nick
self.about = about

Some files were not shown because too many files have changed in this diff Show More