Compare commits
256 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
b8d9e34b84
|
|||
| 8b9958a4ad | |||
| 87a0bdac94 | |||
| 37b964c296 | |||
| b1a2b47116 | |||
| af6f88ab17 | |||
| 647495dbc0 | |||
| 826fd1ef33 | |||
| 54dd2035a1 | |||
| 587819c8eb | |||
| 8954c1c245 | |||
| 19a421604c | |||
| 68b57d8b99 | |||
| f3056653db | |||
| 6196279d2b | |||
| f213420b41 | |||
| b4140dc5f2 | |||
|
1b27e9041f
|
|||
| 795577a0a1 | |||
| d5c45dc8ba | |||
| 603a5a1814 | |||
| 06a1a9aba6 | |||
| ff1815cce0 | |||
| 0bdec912f8 | |||
| 6d8312fa57 | |||
| f6d56179eb | |||
| 193e922c9c | |||
| a1a89dc98e | |||
| 3e764e75e4 | |||
| 7c563cb0ae | |||
|
a328b0d1a8
|
|||
| 5018b9aa1e | |||
| 1f6657e471 | |||
| 062b5dc040 | |||
| 390c9162ae | |||
| 94f66adf8d | |||
| d547dade04 | |||
| 94a67adff9 | |||
| 29f192c377 | |||
|
4e67c88607
|
|||
| 42200c347b | |||
|
36f05ccaed
|
|||
|
98a1b95d12
|
|||
| 4cdef502e9 | |||
| ae2e70ba7d | |||
| 1b4e54582f | |||
| 909148f0be | |||
| c100c6db47 | |||
| 8d3fb397f7 | |||
| f8742a609c | |||
| d55d0d61ed | |||
| cf90480501 | |||
| f0075904c2 | |||
|
a41acc12e7
|
|||
| 1e22984d52 | |||
| 9080e4efae | |||
|
6488634eda
|
|||
|
355cd1283c
|
|||
| 6ed9c408f9 | |||
| 5f52e6f62f | |||
| 59211bb4fd | |||
| 6d634763c5 | |||
| 49cf56f4c2 | |||
| 98c7bf5afc | |||
| 70a7239cfd | |||
| 0e83632896 | |||
| f0df4aa218 | |||
| bb9fc6f905 | |||
| f69e0c660a | |||
| 9089246b6b | |||
| 237c939639 | |||
| 4f86361b63 | |||
| 209ad71ff3 | |||
| c728850524 | |||
| bc638f79f6 | |||
| 47a6f7ff38 | |||
| 6653798d27 | |||
| e9ea96ffb6 | |||
|
59ccde9c38
|
|||
|
f9be7b166c
|
|||
| 2366089896 | |||
| 4f2bacfaab | |||
| 5a8b29b5cc | |||
| 679c0ac424 | |||
| 48050f5e69 | |||
| b5c967e161 | |||
| 715d4aa35d | |||
| 4b54278378 | |||
| 6e700e5726 | |||
| 18ad113198 | |||
| 7415671900 | |||
| d5b6d935e8 | |||
| 543fd67f35 | |||
| c24689d3ef | |||
| bf7120dc08 | |||
| dbe938ad9b | |||
| 289d55b918 | |||
| 771fa845e3 | |||
| 5f1545b86a | |||
| 9a95967a81 | |||
| fe444228e6 | |||
| 504108da75 | |||
| d43a2ff92d | |||
| 10596ddb09 | |||
| 989684cd37 | |||
| 9ab03034a2 | |||
| c602c754f8 | |||
|
c5db887ab1
|
|||
| 0563ec8bf8 | |||
| 29c4170833 | |||
| 3c629621eb | |||
| 09f12845c0 | |||
| b882a96206 | |||
| fb82cc0531 | |||
| 90cd48ead7 | |||
| f71b67f036 | |||
|
4406e44424
|
|||
| ae6608cf7d | |||
| 04759107a2 | |||
| 552402f2b5 | |||
| 852609ee30 | |||
| 1e44d97a97 | |||
| 567303e680 | |||
| 7d1bac4028 | |||
| eae844e081 | |||
| 140b0e4fc4 | |||
| 0b476faff7 | |||
| 53ec89551b | |||
| 638052492d | |||
| 45f8c37498 | |||
| f96ad99790 | |||
| 1f79c20973 | |||
| e8b23daa3d | |||
| a2eb77a5e9 | |||
| 29a8206586 | |||
| 07676a1f95 | |||
| 79ca3b2262 | |||
| ba8425dedb | |||
| 4faf63f29d | |||
| 84ad0e03d0 | |||
| 7d3d23def3 | |||
| ac1a5d237e | |||
| cfcd799d63 | |||
| 351b32308f | |||
| 5a4299edaa | |||
| 99b619e011 | |||
| d5ee9e4780 | |||
| ced5b4974f | |||
| 9be55b08fd | |||
| ac5f39a922 | |||
| 0e9691ae7a | |||
| 1441d339a7 | |||
| 2517132041 | |||
| 71acb16387 | |||
| 9e2e8595e8 | |||
| 1a2e9464af | |||
| 63dd39c7e4 | |||
| 40be9885c5 | |||
| 331d7e9792 | |||
| d21613a765 | |||
| 7780120504 | |||
| 1696e0365e | |||
| 006f8d79e0 | |||
| 135432e03c | |||
| 1fd4d4d950 | |||
| 7d406fd75f | |||
| 0902548336 | |||
| 09547529ad | |||
| 6bd7e7563c | |||
| 5ec77bf8d2 | |||
| 33368c3ac4 | |||
| 99d282ee20 | |||
| a9009049c9 | |||
| e64abca1f0 | |||
| e90408027b | |||
| 58a74af25b | |||
| 0a33f4ca1c | |||
| 960ed8158c | |||
|
0cff4dc194
|
|||
| 03822418c7 | |||
|
de510423f6
|
|||
| 264fbac16c | |||
|
2cd508c4c2
|
|||
|
5e0b4583c0
|
|||
|
4d2a670c72
|
|||
|
73d17ac708
|
|||
|
c2e955faa5
|
|||
|
58d95a0c15
|
|||
|
d86a6a9e16
|
|||
|
1269c00485
|
|||
| 98183cb4a8 | |||
| 537100d923 | |||
| ca3c65496a | |||
| 9b2fb867b4 | |||
| 52f6dff4e9 | |||
| 94811b3737 | |||
| 921b5a2a31 | |||
| 116825b556 | |||
| e40cc9a50a | |||
| 43f6053429 | |||
| 1e8d8120ac | |||
| dfb681cc02 | |||
| 889c584487 | |||
| 72f00fb413 | |||
| d6694fac40 | |||
| d4068f8d52 | |||
| 7d410bff34 | |||
| b25e2ff6c0 | |||
| eddff1a579 | |||
| 387e1bcf22 | |||
| 4da002e1b4 | |||
| 139a2455a5 | |||
| e058f7e8e1 | |||
| ec3f0b3c5d | |||
| 20b1697e40 | |||
| 159f00e466 | |||
| 57635b3c17 | |||
|
900094fae4
|
|||
|
4fbc9882ce
|
|||
| e1578c0337 | |||
| 9fa11118d3 | |||
|
3aac4e2f7f
|
|||
|
133c237105
|
|||
|
f59d267863
|
|||
| 78b4035d51 | |||
| dcc4b7b5e4 | |||
| 1af12e5e81 | |||
| 2eeeb081fd | |||
| 7affc5ae4b | |||
| f283519a0d | |||
| 3317f23618 | |||
| 2ed17a2509 | |||
| 08ca484d54 | |||
| 2feaa207d7 | |||
| bb6a09179e | |||
| 49f64e7f49 | |||
|
a65a6966ac
|
|||
|
6f15746b8a
|
|||
| 13066a8fa2 | |||
| c647daf9b9 | |||
| 7bcc345038 | |||
| bf0f879d66 | |||
| 3af9131afe | |||
| b6b6d033a8 | |||
| 819d7496b2 | |||
| 4c58e73e18 | |||
| 6e38707aaa | |||
| 0f08612b79 | |||
| ef89c4b33b | |||
| 5c9bc02ac6 | |||
| b57d2a3a6e | |||
| 0e8c94b668 | |||
| 3e6c8c47a7 | |||
| e4beb872a5 | |||
| 552bd9cae5 | |||
| 059a16a8dc |
@@ -1,10 +1,156 @@
|
|||||||
|
## [1.1.0-3] - 2023-02-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add a "load more" button instead of always inserting events in timelines (William Casarin)
|
||||||
|
- Added the ability to select text on posts (OlegAba)
|
||||||
|
- Added Posts or Post & Replies selector to Profile (ericholguin)
|
||||||
|
- Improved profile navbar (OlegAba)
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Rename global feed to universe (William Casarin)
|
||||||
|
- Improve look of post view (ericholguin)
|
||||||
|
- Added a 20MB content length limit for all image files (OlegAba)
|
||||||
|
- Improved EventActionBar button spacing (Bryan Montz)
|
||||||
|
- Polished profile key copy buttons, added animation (Bryan Montz)
|
||||||
|
- Format large numbers of action bar actions (Joel Klabo)
|
||||||
|
- Improved blur on images, especially in dark mode (Bryan Montz)
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Remove trailing slash when adding a relay (middlingphys)
|
||||||
|
- Scroll to top of events instead of the bottom (OlegAba)
|
||||||
|
- Fix lag on startup when you have lots of DMs (William Casarin)
|
||||||
|
- Fix an issues where dm notifications appear without any new events (William Casarin)
|
||||||
|
- Fix some hangs when scrolling by images (OlegAba)
|
||||||
|
- Force default zap amount text field to accept only numbers (Terry Yiu)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1.1.0-3]: https://github.com/damus-io/damus/releases/tag/v1.1.0-3
|
||||||
|
|
||||||
|
## [1.1.0-2] - 2023-02-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Save drafts to posts, replies and DMs (Terry Yiu)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Ensure stats get updated in realtime on action bars (William Casarin)
|
||||||
|
- Fix reposts not getting counted properly (William Casarin)
|
||||||
|
- Fix a bug where zaps on other people's posts weren't showing (William Casarin)
|
||||||
|
- Fix punctuation getting included in some urls (Gert Goet)
|
||||||
|
- Improve language detection (Terry Yiu)
|
||||||
|
- Fix some animated image crashes (William Casarin)
|
||||||
|
|
||||||
|
|
||||||
|
[1.1.0-2]: https://github.com/damus-io/damus/releases/tag/v1.1.0-2
|
||||||
|
## [1.0.0-15] - 2023-02-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Relay Filtering (William Casarin)
|
||||||
|
- Japanese translations (Terry Yiu)
|
||||||
|
- Add password autofill on account login and creation (Terry Yiu)
|
||||||
|
- Show if relay is paid (William Casarin)
|
||||||
|
- Add "Follows You" indicator on profile (William Casarin)
|
||||||
|
- Add screen to select individual relays when posting/broadcasting (Andrii Sievrikov)
|
||||||
|
- Relay Detail View (Joel Klabo)
|
||||||
|
- Warn when attempting to post an nsec key (Terry Yiu)
|
||||||
|
- DeepL translation integration (Terry Yiu)
|
||||||
|
- Use local authentication (faceid) to access private key (Andrii Sievrikov)
|
||||||
|
- Add accessibility labels to action bar (Bryan Montz)
|
||||||
|
- Copy invoice button (Joel Klabo)
|
||||||
|
- Receive Lightning Zaps (William Casarin)
|
||||||
|
- Allow text selection in bio (Suhail Saqan)
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Show "Follow Back" button on profile page (William Casarin)
|
||||||
|
- When on your profile page, open relay view instead for your own relays (Terry Yiu)
|
||||||
|
- Updated QR code view, include profile image, etc (ericholguin)
|
||||||
|
- Clicking relay numbers now goes to relay config (radixrat)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Load zaps, likes and reposts when you open a thread (William Casarin)
|
||||||
|
- Fix bug where sidebar navigation fails to pop when switching timelines (William Casarin)
|
||||||
|
- Use lnaddress before lnurl for tip addresses to avoid Anigma scamming (William Casarin)
|
||||||
|
- Fix sidebar navigation bugs (OlegAba)
|
||||||
|
- Fix issue where navigation fails pop to root when switching timelines (William Casarin)
|
||||||
|
- Make @ mentions case insensitive (William Casarin)
|
||||||
|
- Fix some lnurls not getting decoded properly (William Casarin)
|
||||||
|
- Hide incoming DMs from blocked users (William Casarin)
|
||||||
|
- Hide blocked users from search results (William Casarin)
|
||||||
|
- Fix Cash App invoice payments (Rob Seward)
|
||||||
|
- DM Padding (OlegAba)
|
||||||
|
- Check for broken lnurls (William Casarin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1.0.0-15]: https://github.com/damus-io/damus/releases/tag/v1.0.0-15
|
||||||
|
## [1.0.0-13] - 2023-01-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- LibreTranslate note translations (Terry Yiu)
|
||||||
|
- Added support for account deletion (William Casarin)
|
||||||
|
- User tagging and autocompletion in posts (Swift)
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Remove redundant logout button from settings (Jonathan Milligan)
|
||||||
|
- Moved relay config to its own sidebar entry (William Casarin)
|
||||||
|
- New stylized tabs (ericholguin)
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix hidden profile action sheet when clicking ... (William Casarin)
|
||||||
|
- Fixed height of DM input (Terry Yiu)
|
||||||
|
- Fixed bug where copying pubkey from context menu only copied your own pubkey (Terry Yiu)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1.0.0-13]: https://github.com/damus-io/damus/releases/tag/v1.0.0-13
|
||||||
|
## [1.0.0-12] - 2023-01-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added Arabic and Portuguese translations (Barodane, Antonio Chagas)
|
||||||
|
- Add QRCode view for sharing your pubkey (ericholguin)
|
||||||
|
- Added nostr: uri handling (William Casarin)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Remove markdown link support from posts (Joel Klabo)
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed crash on some SVG profile pictures (OlegAba)
|
||||||
|
- Localization fixes
|
||||||
|
- Don't allow blocking yourself (Terry)
|
||||||
|
- Hide muted users from global (William Casarin)
|
||||||
|
- Fixed profiles sometimes not loading from other clients (William Casarin)
|
||||||
|
- Fixed bug where `spam` was always the report type (William Casarin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1.0.0-12]: https://github.com/damus-io/damus/releases/tag/v1.0.0-12
|
||||||
|
|
||||||
## [1.0.0-11] - 2023-01-25
|
## [1.0.0-11] - 2023-01-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Reposts view (Terry Yiu)
|
- Reposts view (Terry Yiu)
|
||||||
- Translations for it_IT, it_CH, fr_FR, de_DE, de_AT and lv_LV (William Casarin)
|
- Translations for it_IT, it_CH, fr_FR, de_DE, de_AT and lv_LV (Nicolò Carcagnì, Solobalbo, Gregor, Peter Gerstbach, SYX)
|
||||||
- Added ability to block users (William Casarin)
|
- Added ability to block users (William Casarin)
|
||||||
- Added a way to report content (William Casarin)
|
- Added a way to report content (William Casarin)
|
||||||
- Stretchable profile cover header (Swift)
|
- Stretchable profile cover header (Swift)
|
||||||
@@ -31,7 +177,7 @@
|
|||||||
|
|
||||||
- Show website on profiles (William Casarin)
|
- Show website on profiles (William Casarin)
|
||||||
- Add the ability to choose participants when replying (Joel Klabo)
|
- Add the ability to choose participants when replying (Joel Klabo)
|
||||||
- Translations for de_AT, de_DE, tr_TR, fr_FR (William Casarin)
|
- Translations for de_AT, de_DE, tr_TR, fr_FR (Gregor, Peter Gerstbach, Taylan Benli, Solobalbo)
|
||||||
- Add DM Message Requests (William Casarin)
|
- Add DM Message Requests (William Casarin)
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +197,7 @@
|
|||||||
|
|
||||||
- Drastically improved image viewer (OlegAba)
|
- Drastically improved image viewer (OlegAba)
|
||||||
- Added pinch to zoom on images (Swift)
|
- Added pinch to zoom on images (Swift)
|
||||||
- Add Latin American Spanish translations (William Casarin)
|
- Add Latin American Spanish translations (Nicolás Valencia)
|
||||||
- Added SVG profile picture support (OlegAba)
|
- Added SVG profile picture support (OlegAba)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
[](https://github.com/damus-io/damus/actions/workflows/run-tests.yaml)
|
||||||
|
|
||||||
# damus
|
# damus
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
|||||||
## Getting Started on Damus
|
## Getting Started on Damus
|
||||||
|
|
||||||
### Damus iOS
|
### Damus iOS
|
||||||
1) Get the Damus app on TestFlight: https://testflight.apple.com/join/CLwjLxWl
|
1) Get the Damus app on the iOS App Store: https://apps.apple.com/ca/app/damus/id1628663131
|
||||||
|
|
||||||
#### ⚙️ Settings (gear icon, top right)
|
#### ⚙️ Settings (gear icon, top right)
|
||||||
- Relays: You can add more relays to send your notes to by tapping the "+".
|
- Relays: You can add more relays to send your notes to by tapping the "+".
|
||||||
@@ -48,7 +49,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
|||||||
4. Add @ direcly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
|
4. Add @ direcly followed by the pubkey (e.g., `@npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s`)
|
||||||
- You can also long-press a Note to grab their User ID aka pubkey or Note ID to link directly to a Note.
|
- You can also long-press a Note to grab their User ID aka pubkey or Note ID to link directly to a Note.
|
||||||
- Currently you can't delete your Notes in the iOS app
|
- Currently you can't delete your Notes in the iOS app
|
||||||
- Share images by pasting the image url which you can grab from imgbb, imgur, etc. (i.e., `(https://i.ibb.co/2SHZbwm/alpha60.jpg)`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
|
- Share images by pasting the image url which you can grab from imgbb, imgur, etc. (i.e., `https://i.ibb.co/2SHZbwm/alpha60.jpg`). Currently images only load for people you follow in the 🏠 Personal Feed. Images are not automatically loaded in 🔍 Global Feed
|
||||||
- Engaging with Notes
|
- Engaging with Notes
|
||||||
- 💬 Replying to a Note: Tap the chat icon underneath the note. This will show up in the users’ notifications and in your 🏠 Personal and 🔍 Global Feeds
|
- 💬 Replying to a Note: Tap the chat icon underneath the note. This will show up in the users’ notifications and in your 🏠 Personal and 🔍 Global Feeds
|
||||||
- ♺ Reposts: Tap the repost icon which will show up in your 🏠 Personal and 🔍 Global Feeds
|
- ♺ Reposts: Tap the repost icon which will show up in your 🏠 Personal and 🔍 Global Feeds
|
||||||
@@ -91,15 +92,23 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributors welcome! [Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on github as well.
|
Contributors welcome!
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
[Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on GitHub as well.
|
||||||
|
|
||||||
[git-send-email]: http://git-send-email.io
|
[git-send-email]: http://git-send-email.io
|
||||||
|
|
||||||
## git log bot
|
### Translations
|
||||||
|
|
||||||
npub1fjtdwclt9lspjy8huu3qklr7eklp5uq90u6yh8mec290pqxraccqlufnas
|
Translators welcome! Join the [Transifex][transifex] project.
|
||||||
|
|
||||||
### Awards
|
All user-facing strings must have a comment in order to provide context to translators. If a SwiftUI component has a `comment` parameter, use that. Otherwise, wrap your string with `NSLocalizedString` with the `comment` field populated.
|
||||||
|
|
||||||
|
[transifex]: https://explore.transifex.com/damus/damus-ios/
|
||||||
|
|
||||||
|
### Awards
|
||||||
|
|
||||||
There may be nostr badges awarded for contributors in the future... :)
|
There may be nostr badges awarded for contributors in the future... :)
|
||||||
|
|
||||||
@@ -107,3 +116,7 @@ First contributors:
|
|||||||
|
|
||||||
1. @randymcmillan
|
1. @randymcmillan
|
||||||
2. @jcarucci27
|
2. @jcarucci27
|
||||||
|
|
||||||
|
### git log bot
|
||||||
|
|
||||||
|
npub1fjtdwclt9lspjy8huu3qklr7eklp5uq90u6yh8mec290pqxraccqlufnas
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ static inline int is_boundary(char c) {
|
|||||||
return !isalnum(c);
|
return !isalnum(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_invalid_url_ending(char c) {
|
||||||
|
return c == '!' || c == '?' || c == ')' || c == '.' || c == ',' || c == ';';
|
||||||
|
}
|
||||||
|
|
||||||
static void make_cursor(struct cursor *c, const u8 *content, size_t len)
|
static void make_cursor(struct cursor *c, const u8 *content, size_t len)
|
||||||
{
|
{
|
||||||
c->start = content;
|
c->start = content;
|
||||||
@@ -55,8 +59,8 @@ static int consume_until_whitespace(struct cursor *cur, int or_end) {
|
|||||||
while (cur->p < cur->end) {
|
while (cur->p < cur->end) {
|
||||||
c = *cur->p;
|
c = *cur->p;
|
||||||
|
|
||||||
if (is_whitespace(c) && consumedAtLeastOne)
|
if (is_whitespace(c))
|
||||||
return 1;
|
return consumedAtLeastOne;
|
||||||
|
|
||||||
cur->p++;
|
cur->p++;
|
||||||
consumedAtLeastOne = true;
|
consumedAtLeastOne = true;
|
||||||
@@ -221,6 +225,9 @@ static int parse_url(struct cursor *cur, struct block *block) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// strip any unwanted characters
|
||||||
|
while(is_invalid_url_ending(peek_char(cur, -1))) cur->p--;
|
||||||
|
|
||||||
block->type = BLOCK_URL;
|
block->type = BLOCK_URL;
|
||||||
block->block.str.start = (const char *)start;
|
block->block.str.start = (const char *)start;
|
||||||
block->block.str.end = (const char *)cur->p;
|
block->block.str.end = (const char *)cur->p;
|
||||||
|
|||||||
@@ -11,13 +11,22 @@
|
|||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
|
||||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
|
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
|
||||||
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||||
|
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */; };
|
||||||
|
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040EE29A8FEE9008A0F29 /* EventDetailBarTests.swift */; };
|
||||||
|
3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */; };
|
||||||
|
3A3040F329A91366008A0F29 /* ProfileViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3040F229A91366008A0F29 /* ProfileViewTests.swift */; };
|
||||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
||||||
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */; };
|
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FC297E3CFF0090C62D /* RepostsModel.swift */; };
|
||||||
3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FE297E3D900090C62D /* RepostsView.swift */; };
|
3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA247FE297E3D900090C62D /* RepostsView.swift */; };
|
||||||
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA24801297E3DC20090C62D /* RepostView.swift */; };
|
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA24801297E3DC20090C62D /* RepostView.swift */; };
|
||||||
|
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA59D1C2999B0400061C48E /* DraftsModel.swift */; };
|
||||||
|
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */; };
|
||||||
|
3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */; };
|
||||||
|
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB72AB8298ECF30004BB58C /* Translator.swift */; };
|
||||||
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; };
|
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; };
|
||||||
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
||||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
||||||
|
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
|
||||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
||||||
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
|
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
|
||||||
@@ -27,8 +36,6 @@
|
|||||||
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
|
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
|
||||||
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; };
|
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; };
|
||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
||||||
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */; };
|
|
||||||
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; };
|
|
||||||
4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; };
|
4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; };
|
||||||
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
|
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
|
||||||
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; };
|
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; };
|
||||||
@@ -39,6 +46,7 @@
|
|||||||
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */; };
|
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */; };
|
||||||
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8B28398BC6008A31F1 /* Keys.swift */; };
|
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8B28398BC6008A31F1 /* Keys.swift */; };
|
||||||
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */; };
|
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */; };
|
||||||
|
4C2CDDF7299D4A5E00879FD5 /* Debouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2CDDF6299D4A5E00879FD5 /* Debouncer.swift */; };
|
||||||
4C363A8428233689006E126D /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8328233689006E126D /* Parser.swift */; };
|
4C363A8428233689006E126D /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8328233689006E126D /* Parser.swift */; };
|
||||||
4C363A8828236948006E126D /* BlocksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8728236948006E126D /* BlocksView.swift */; };
|
4C363A8828236948006E126D /* BlocksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8728236948006E126D /* BlocksView.swift */; };
|
||||||
4C363A8A28236B57006E126D /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8928236B57006E126D /* MentionView.swift */; };
|
4C363A8A28236B57006E126D /* MentionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8928236B57006E126D /* MentionView.swift */; };
|
||||||
@@ -69,6 +77,8 @@
|
|||||||
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */; };
|
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */; };
|
||||||
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDB281DCE6100B3DE84 /* Liked.swift */; };
|
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDB281DCE6100B3DE84 /* Liked.swift */; };
|
||||||
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */; };
|
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */; };
|
||||||
|
4C3D52B6298DB4E6001C5831 /* ZapEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */; };
|
||||||
|
4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3D52B7298DB5C6001C5831 /* TextEvent.swift */; };
|
||||||
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA63C28FF52D600C48A62 /* bolt11.c */; };
|
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA63C28FF52D600C48A62 /* bolt11.c */; };
|
||||||
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA64028FF553900C48A62 /* hash_u5.c */; };
|
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA64028FF553900C48A62 /* hash_u5.c */; };
|
||||||
4C3EA64428FF558100C48A62 /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA64328FF558100C48A62 /* sha256.c */; };
|
4C3EA64428FF558100C48A62 /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA64328FF558100C48A62 /* sha256.c */; };
|
||||||
@@ -85,6 +95,7 @@
|
|||||||
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */; };
|
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */; };
|
||||||
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */; };
|
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */; };
|
||||||
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */; };
|
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */; };
|
||||||
|
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C42812B298C848200DBF26F /* TranslateView.swift */; };
|
||||||
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C477C9D282C3A4800033AA3 /* TipCounter.swift */; };
|
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C477C9D282C3A4800033AA3 /* TipCounter.swift */; };
|
||||||
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */; };
|
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */; };
|
||||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */; };
|
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */; };
|
||||||
@@ -116,6 +127,8 @@
|
|||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C987B56283FD07F0042CE38 /* FollowersModel.swift */; };
|
||||||
4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */; };
|
4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */; };
|
||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
||||||
|
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
|
||||||
|
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; };
|
||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
||||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
|
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
|
||||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; };
|
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; };
|
||||||
@@ -128,7 +141,16 @@
|
|||||||
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88392296F798300DC99E7 /* ReactionsModel.swift */; };
|
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88392296F798300DC99E7 /* ReactionsModel.swift */; };
|
||||||
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88395296F7F8B00DC99E7 /* ReactionView.swift */; };
|
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88395296F7F8B00DC99E7 /* ReactionView.swift */; };
|
||||||
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88399297322D200DC99E7 /* DMTests.swift */; };
|
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88399297322D200DC99E7 /* DMTests.swift */; };
|
||||||
|
4CB883A62975F83C00DC99E7 /* LNUrlPayRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883A52975F83C00DC99E7 /* LNUrlPayRequest.swift */; };
|
||||||
|
4CB883A82975FC1800DC99E7 /* Zaps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883A72975FC1800DC99E7 /* Zaps.swift */; };
|
||||||
|
4CB883AA297612FF00DC99E7 /* ZapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883A9297612FF00DC99E7 /* ZapTests.swift */; };
|
||||||
|
4CB883AE2976FA9300DC99E7 /* FormatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883AD2976FA9300DC99E7 /* FormatTests.swift */; };
|
||||||
|
4CB883B0297705DD00DC99E7 /* ZapButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883AF297705DD00DC99E7 /* ZapButton.swift */; };
|
||||||
|
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB883B5297730E400DC99E7 /* LNUrls.swift */; };
|
||||||
|
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */; };
|
||||||
|
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB9D4A82992D2F400A9A7E4 /* FollowsYou.swift */; };
|
||||||
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */; };
|
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */; };
|
||||||
|
4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAE6297EFA7B00430951 /* Zap.swift */; };
|
||||||
4CC7AAEB297F0AEC00430951 /* BuilderEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */; };
|
4CC7AAEB297F0AEC00430951 /* BuilderEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */; };
|
||||||
4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEC297F0B9E00430951 /* Highlight.swift */; };
|
4CC7AAED297F0B9E00430951 /* Highlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEC297F0B9E00430951 /* Highlight.swift */; };
|
||||||
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEF297F11C700430951 /* SelectedEventView.swift */; };
|
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAEF297F11C700430951 /* SelectedEventView.swift */; };
|
||||||
@@ -138,6 +160,9 @@
|
|||||||
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; };
|
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; };
|
||||||
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; };
|
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; };
|
||||||
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
||||||
|
4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */; };
|
||||||
|
4CE0E2B229A3DF6900DB4CA2 /* LoadMoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2B129A3DF6900DB4CA2 /* LoadMoreButton.swift */; };
|
||||||
|
4CE0E2B629A3ED5500DB4CA2 /* InnerTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */; };
|
||||||
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
|
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
|
||||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
|
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
|
||||||
4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */; };
|
4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */; };
|
||||||
@@ -151,6 +176,14 @@
|
|||||||
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
|
||||||
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; };
|
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; };
|
||||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
|
||||||
|
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
|
||||||
|
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */; };
|
||||||
|
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794D2996B16A00F758CC /* RelayToggle.swift */; };
|
||||||
|
4CE879502996B2BD00F758CC /* RelayStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794F2996B2BD00F758CC /* RelayStatus.swift */; };
|
||||||
|
4CE879522996B68900F758CC /* RelayType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879512996B68900F758CC /* RelayType.swift */; };
|
||||||
|
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879542996BAB900F758CC /* RelayPaidDetail.swift */; };
|
||||||
|
4CE879582996C45300F758CC /* ZapsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE879572996C45300F758CC /* ZapsView.swift */; };
|
||||||
|
4CE8795B2996C47A00F758CC /* ZapsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8795A2996C47A00F758CC /* ZapsModel.swift */; };
|
||||||
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; };
|
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; };
|
||||||
4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; };
|
4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; };
|
||||||
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; };
|
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; };
|
||||||
@@ -170,21 +203,31 @@
|
|||||||
4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE829844AF100D66079 /* AnyCodable.swift */; };
|
4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABE829844AF100D66079 /* AnyCodable.swift */; };
|
||||||
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */; };
|
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */; };
|
||||||
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
4CF0ABEE29844B5500D66079 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABED29844B5500D66079 /* AnyEncodable.swift */; };
|
||||||
|
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
|
||||||
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
|
||||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||||
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
|
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
|
||||||
|
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FCB2984ACA60072348F /* QRCodeView.swift */; };
|
||||||
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; };
|
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; };
|
||||||
|
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643EA5C7296B764E005081BB /* RelayFilterView.swift */; };
|
||||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
|
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
|
||||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
||||||
7C45AE6D297352F90031D7BC /* SVGKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7C45AE6C297352F90031D7BC /* SVGKit */; };
|
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C60CAEE298471A1009C80D6 /* CoreSVG.swift */; };
|
||||||
7C45AE6F297352F90031D7BC /* SVGKitSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7C45AE6E297352F90031D7BC /* SVGKitSwift */; };
|
|
||||||
7C45AE71297353390031D7BC /* KFImageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C45AE70297353390031D7BC /* KFImageModel.swift */; };
|
|
||||||
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */; };
|
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */; };
|
||||||
|
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C95CAED299DCEF1009DCB67 /* KFOptionSetter+.swift */; };
|
||||||
|
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CFF6316299FEFE5005D382A /* SelectableText.swift */; };
|
||||||
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9609F057296E220800069BF3 /* BannerImageView.swift */; };
|
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9609F057296E220800069BF3 /* BannerImageView.swift */; };
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
||||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||||
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
||||||
|
F75BA12D29A1855400E10810 /* BookmarksManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BA12C29A1855400E10810 /* BookmarksManager.swift */; };
|
||||||
|
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75BA12E29A18EF500E10810 /* BookmarksView.swift */; };
|
||||||
|
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E91298B0F0700AB113A /* RelayDetailView.swift */; };
|
||||||
|
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */; };
|
||||||
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
|
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
|
||||||
F7F0BA272978E54D009531F3 /* ParicipantsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA262978E54D009531F3 /* ParicipantsView.swift */; };
|
F7F0BA272978E54D009531F3 /* ParicipantsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA262978E54D009531F3 /* ParicipantsView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -214,29 +257,71 @@
|
|||||||
3A185A04297F2C3800F4BDC0 /* lv-LV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "lv-LV"; path = "lv-LV.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A185A04297F2C3800F4BDC0 /* lv-LV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "lv-LV"; path = "lv-LV.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3A185A05297F2C3800F4BDC0 /* lv-LV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "lv-LV"; path = "lv-LV.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3A185A05297F2C3800F4BDC0 /* lv-LV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "lv-LV"; path = "lv-LV.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3A185A06297F2C3800F4BDC0 /* lv-LV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "lv-LV"; path = "lv-LV.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A185A06297F2C3800F4BDC0 /* lv-LV */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "lv-LV"; path = "lv-LV.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A25EF132992DA5D008ABE69 /* el-GR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "el-GR"; path = "el-GR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
|
3A25EF142992DA5D008ABE69 /* el-GR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "el-GR"; path = "el-GR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
|
3A25EF152992DA5D008ABE69 /* el-GR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "el-GR"; path = "el-GR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
3A2B8B0A296A8982009CC16D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "en-US"; path = "en-US.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A2B8B0A296A8982009CC16D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "en-US"; path = "en-US.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyDescriptionTests.swift; sourceTree = "<group>"; };
|
||||||
|
3A3040EE29A8FEE9008A0F29 /* EventDetailBarTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailBarTests.swift; sourceTree = "<group>"; };
|
||||||
|
3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationUtil.swift; sourceTree = "<group>"; };
|
||||||
|
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewTests.swift; sourceTree = "<group>"; };
|
||||||
|
3A3040F929A91ED6008A0F29 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
|
3A3040FA29A91EFC008A0F29 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
|
3A3040FB29A91F03008A0F29 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-HK"; path = "zh-HK.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A3040FC29A91F31008A0F29 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
|
3A3040FD29A91F31008A0F29 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-TW"; path = "zh-TW.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A3040FE29A91F31008A0F29 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
|
3A41E559299D52BE001FA465 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3A41E55A299D52BE001FA465 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3A41E55B299D52BE001FA465 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3A4F3320297CCFEE004B5F72 /* fr-FR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-FR"; path = "fr-FR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A4F3320297CCFEE004B5F72 /* fr-FR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-FR"; path = "fr-FR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3A4F3321297CCFEE004B5F72 /* fr-FR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-FR"; path = "fr-FR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3A4F3321297CCFEE004B5F72 /* fr-FR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-FR"; path = "fr-FR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3A4F3322297CCFEE004B5F72 /* fr-FR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "fr-FR"; path = "fr-FR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A4F3322297CCFEE004B5F72 /* fr-FR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "fr-FR"; path = "fr-FR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
3A5EA10F297CCF6C00569477 /* de-AT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-AT"; path = "de-AT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3A5EA110297CCF6C00569477 /* de-AT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-AT"; path = "de-AT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3A5CAE1E298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3A5EA111297CCF6C00569477 /* de-AT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "de-AT"; path = "de-AT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A5CAE1F298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-CN"; path = "zh-CN.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A66D927299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3A66D928299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3A66D929299472FA008B44F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
3A827A18299FC69D00C4D171 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3A827A19299FC69D00C4D171 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3A827A1A299FC69D00C4D171 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = ru.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
3A8624D9299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3A8624DA299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3A8624DB299E82BE00BD8BE9 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3A929C20297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3A929C20297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3A929C21297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3A929C21297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "it-IT"; path = "it-IT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3A929C22297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "it-IT"; path = "it-IT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3A929C22297F2CF80090925E /* it-IT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "it-IT"; path = "it-IT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A93342929884CA600D6A8F3 /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pl-PL"; path = "pl-PL.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
|
3A93342A29884CA600D6A8F3 /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pl-PL"; path = "pl-PL.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
|
3A93342B29884CA600D6A8F3 /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pl-PL"; path = "pl-PL.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3A96D41A298DA94500388A2A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3A96D41B298DA94500388A2A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3A96D41C298DA94500388A2A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3AA247FC297E3CFF0090C62D /* RepostsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepostsModel.swift; sourceTree = "<group>"; };
|
3AA247FC297E3CFF0090C62D /* RepostsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepostsModel.swift; sourceTree = "<group>"; };
|
||||||
3AA247FE297E3D900090C62D /* RepostsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostsView.swift; sourceTree = "<group>"; };
|
3AA247FE297E3D900090C62D /* RepostsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostsView.swift; sourceTree = "<group>"; };
|
||||||
3AA24801297E3DC20090C62D /* RepostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostView.swift; sourceTree = "<group>"; };
|
3AA24801297E3DC20090C62D /* RepostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepostView.swift; sourceTree = "<group>"; };
|
||||||
|
3AA59D1C2999B0400061C48E /* DraftsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraftsModel.swift; sourceTree = "<group>"; };
|
||||||
|
3AAA95C9298DF87B00F3D526 /* TranslationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationService.swift; sourceTree = "<group>"; };
|
||||||
|
3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLPlan.swift; sourceTree = "<group>"; };
|
||||||
|
3AB5B86A2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3AB5B86B2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3AB5B86C2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
3AB72AB8298ECF30004BB58C /* Translator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Translator.swift; sourceTree = "<group>"; };
|
||||||
|
3AC524EE298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
3AC524EF298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
|
3AC524F0298C000B00693EBF /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
3ACB685B297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3ACB685B297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3ACB685E297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3ACB685E297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.swift; sourceTree = "<group>"; };
|
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.swift; sourceTree = "<group>"; };
|
||||||
3AEABD20297CCFA8003F2975 /* de-DE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-DE"; path = "de-DE.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibreTranslateServer.swift; sourceTree = "<group>"; };
|
||||||
3AEABD21297CCFA8003F2975 /* de-DE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-DE"; path = "de-DE.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
|
||||||
3AEABD22297CCFA8003F2975 /* de-DE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "de-DE"; path = "de-DE.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
|
||||||
3AEB8003297CCEA800713A25 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tr-TR"; path = "tr-TR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
3AEB8003297CCEA800713A25 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tr-TR"; path = "tr-TR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
3AEB8004297CCEA800713A25 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tr-TR"; path = "tr-TR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3AEB8004297CCEA800713A25 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "tr-TR"; path = "tr-TR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3AEB8005297CCEA900713A25 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "tr-TR"; path = "tr-TR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
3AEB8005297CCEA900713A25 /* tr-TR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "tr-TR"; path = "tr-TR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
|
3AF6336829884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
|
3AF6336929884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
|
3AF6336A29884C6B0005672A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; };
|
4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; };
|
||||||
4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; };
|
4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; };
|
||||||
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; };
|
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
@@ -248,8 +333,6 @@
|
|||||||
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
||||||
4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
|
4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
|
||||||
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
||||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyQuoteView.swift; sourceTree = "<group>"; };
|
|
||||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
|
|
||||||
4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; };
|
4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; };
|
||||||
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
|
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
|
||||||
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; };
|
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; };
|
||||||
@@ -260,6 +343,7 @@
|
|||||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureSelector.swift; sourceTree = "<group>"; };
|
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureSelector.swift; sourceTree = "<group>"; };
|
||||||
4C285C8B28398BC6008A31F1 /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
|
4C285C8B28398BC6008A31F1 /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
|
||||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveKeysView.swift; sourceTree = "<group>"; };
|
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveKeysView.swift; sourceTree = "<group>"; };
|
||||||
|
4C2CDDF6299D4A5E00879FD5 /* Debouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debouncer.swift; sourceTree = "<group>"; };
|
||||||
4C363A8328233689006E126D /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
|
4C363A8328233689006E126D /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
|
||||||
4C363A8728236948006E126D /* BlocksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlocksView.swift; sourceTree = "<group>"; };
|
4C363A8728236948006E126D /* BlocksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlocksView.swift; sourceTree = "<group>"; };
|
||||||
4C363A8928236B57006E126D /* MentionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionView.swift; sourceTree = "<group>"; };
|
4C363A8928236B57006E126D /* MentionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionView.swift; sourceTree = "<group>"; };
|
||||||
@@ -290,6 +374,8 @@
|
|||||||
4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeCounter.swift; sourceTree = "<group>"; };
|
4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeCounter.swift; sourceTree = "<group>"; };
|
||||||
4C3BEFDB281DCE6100B3DE84 /* Liked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Liked.swift; sourceTree = "<group>"; };
|
4C3BEFDB281DCE6100B3DE84 /* Liked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Liked.swift; sourceTree = "<group>"; };
|
||||||
4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusState.swift; sourceTree = "<group>"; };
|
4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusState.swift; sourceTree = "<group>"; };
|
||||||
|
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapEvent.swift; sourceTree = "<group>"; };
|
||||||
|
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEvent.swift; sourceTree = "<group>"; };
|
||||||
4C3EA63B28FF52D600C48A62 /* bolt11.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bolt11.h; sourceTree = "<group>"; };
|
4C3EA63B28FF52D600C48A62 /* bolt11.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bolt11.h; sourceTree = "<group>"; };
|
||||||
4C3EA63C28FF52D600C48A62 /* bolt11.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = bolt11.c; sourceTree = "<group>"; };
|
4C3EA63C28FF52D600C48A62 /* bolt11.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = bolt11.c; sourceTree = "<group>"; };
|
||||||
4C3EA63E28FF54BD00C48A62 /* short_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = short_types.h; sourceTree = "<group>"; };
|
4C3EA63E28FF54BD00C48A62 /* short_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = short_types.h; sourceTree = "<group>"; };
|
||||||
@@ -335,6 +421,7 @@
|
|||||||
4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceTests.swift; sourceTree = "<group>"; };
|
4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceTests.swift; sourceTree = "<group>"; };
|
||||||
4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoicesView.swift; sourceTree = "<group>"; };
|
4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoicesView.swift; sourceTree = "<group>"; };
|
||||||
4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceView.swift; sourceTree = "<group>"; };
|
4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvoiceView.swift; sourceTree = "<group>"; };
|
||||||
|
4C42812B298C848200DBF26F /* TranslateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslateView.swift; sourceTree = "<group>"; };
|
||||||
4C477C9D282C3A4800033AA3 /* TipCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipCounter.swift; sourceTree = "<group>"; };
|
4C477C9D282C3A4800033AA3 /* TipCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TipCounter.swift; sourceTree = "<group>"; };
|
||||||
4C4A3A5A288A1B2200453788 /* damus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = damus.entitlements; sourceTree = "<group>"; };
|
4C4A3A5A288A1B2200453788 /* damus.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = damus.entitlements; sourceTree = "<group>"; };
|
||||||
4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHomeModel.swift; sourceTree = "<group>"; };
|
4C5C7E67284ED36500A22DF5 /* SearchHomeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHomeModel.swift; sourceTree = "<group>"; };
|
||||||
@@ -367,6 +454,8 @@
|
|||||||
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
|
4C987B56283FD07F0042CE38 /* FollowersModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersModel.swift; sourceTree = "<group>"; };
|
||||||
4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatroomMetadata.swift; sourceTree = "<group>"; };
|
4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatroomMetadata.swift; sourceTree = "<group>"; };
|
||||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
||||||
|
4CAAD8AC298851D000060CEA /* AccountDeletion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletion.swift; sourceTree = "<group>"; };
|
||||||
|
4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConfigView.swift; sourceTree = "<group>"; };
|
||||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
||||||
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
|
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
|
||||||
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = "<group>"; };
|
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = "<group>"; };
|
||||||
@@ -379,7 +468,16 @@
|
|||||||
4CB88392296F798300DC99E7 /* ReactionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsModel.swift; sourceTree = "<group>"; };
|
4CB88392296F798300DC99E7 /* ReactionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsModel.swift; sourceTree = "<group>"; };
|
||||||
4CB88395296F7F8B00DC99E7 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = "<group>"; };
|
4CB88395296F7F8B00DC99E7 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = "<group>"; };
|
||||||
4CB88399297322D200DC99E7 /* DMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMTests.swift; sourceTree = "<group>"; };
|
4CB88399297322D200DC99E7 /* DMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMTests.swift; sourceTree = "<group>"; };
|
||||||
|
4CB883A52975F83C00DC99E7 /* LNUrlPayRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LNUrlPayRequest.swift; sourceTree = "<group>"; };
|
||||||
|
4CB883A72975FC1800DC99E7 /* Zaps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Zaps.swift; sourceTree = "<group>"; };
|
||||||
|
4CB883A9297612FF00DC99E7 /* ZapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapTests.swift; sourceTree = "<group>"; };
|
||||||
|
4CB883AD2976FA9300DC99E7 /* FormatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormatTests.swift; sourceTree = "<group>"; };
|
||||||
|
4CB883AF297705DD00DC99E7 /* ZapButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapButton.swift; sourceTree = "<group>"; };
|
||||||
|
4CB883B5297730E400DC99E7 /* LNUrls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LNUrls.swift; sourceTree = "<group>"; };
|
||||||
|
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileNameView.swift; sourceTree = "<group>"; };
|
||||||
|
4CB9D4A82992D2F400A9A7E4 /* FollowsYou.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowsYou.swift; sourceTree = "<group>"; };
|
||||||
4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteLink.swift; sourceTree = "<group>"; };
|
4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteLink.swift; sourceTree = "<group>"; };
|
||||||
|
4CC7AAE6297EFA7B00430951 /* Zap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zap.swift; sourceTree = "<group>"; };
|
||||||
4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuilderEventView.swift; sourceTree = "<group>"; };
|
4CC7AAEA297F0AEC00430951 /* BuilderEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuilderEventView.swift; sourceTree = "<group>"; };
|
||||||
4CC7AAEC297F0B9E00430951 /* Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Highlight.swift; sourceTree = "<group>"; };
|
4CC7AAEC297F0B9E00430951 /* Highlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Highlight.swift; sourceTree = "<group>"; };
|
||||||
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedEventView.swift; sourceTree = "<group>"; };
|
4CC7AAEF297F11C700430951 /* SelectedEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedEventView.swift; sourceTree = "<group>"; };
|
||||||
@@ -389,6 +487,9 @@
|
|||||||
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfile.swift; sourceTree = "<group>"; };
|
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfile.swift; sourceTree = "<group>"; };
|
||||||
4CC7AAF9297F64AC00430951 /* EventMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMenu.swift; sourceTree = "<group>"; };
|
4CC7AAF9297F64AC00430951 /* EventMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMenu.swift; sourceTree = "<group>"; };
|
||||||
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
|
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
|
||||||
|
4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventHolder.swift; sourceTree = "<group>"; };
|
||||||
|
4CE0E2B129A3DF6900DB4CA2 /* LoadMoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreButton.swift; sourceTree = "<group>"; };
|
||||||
|
4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InnerTimelineView.swift; sourceTree = "<group>"; };
|
||||||
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
|
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
|
||||||
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
|
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
|
||||||
4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldAlert.swift; sourceTree = "<group>"; };
|
4CE4F9E0285287B800C00DD9 /* TextFieldAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldAlert.swift; sourceTree = "<group>"; };
|
||||||
@@ -404,6 +505,14 @@
|
|||||||
4CE6DF0127F7A08200C66700 /* damusUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITests.swift; sourceTree = "<group>"; };
|
4CE6DF0127F7A08200C66700 /* damusUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITests.swift; sourceTree = "<group>"; };
|
||||||
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||||
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; };
|
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; };
|
||||||
|
4CE8794729941DA700F758CC /* RelayFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilters.swift; sourceTree = "<group>"; };
|
||||||
|
4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayMetadatas.swift; sourceTree = "<group>"; };
|
||||||
|
4CE8794D2996B16A00F758CC /* RelayToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayToggle.swift; sourceTree = "<group>"; };
|
||||||
|
4CE8794F2996B2BD00F758CC /* RelayStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayStatus.swift; sourceTree = "<group>"; };
|
||||||
|
4CE879512996B68900F758CC /* RelayType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayType.swift; sourceTree = "<group>"; };
|
||||||
|
4CE879542996BAB900F758CC /* RelayPaidDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPaidDetail.swift; sourceTree = "<group>"; };
|
||||||
|
4CE879572996C45300F758CC /* ZapsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapsView.swift; sourceTree = "<group>"; };
|
||||||
|
4CE8795A2996C47A00F758CC /* ZapsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapsModel.swift; sourceTree = "<group>"; };
|
||||||
4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; };
|
4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; };
|
||||||
4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; };
|
4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; };
|
||||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; };
|
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; };
|
||||||
@@ -424,18 +533,30 @@
|
|||||||
4CF0ABE829844AF100D66079 /* AnyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = "<group>"; };
|
4CF0ABE829844AF100D66079 /* AnyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = "<group>"; };
|
4CF0ABEB29844B4700D66079 /* AnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = "<group>"; };
|
||||||
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
4CF0ABED29844B5500D66079 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
|
||||||
|
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
|
||||||
|
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
|
||||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
||||||
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
|
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
|
||||||
|
5C513FCB2984ACA60072348F /* QRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeView.swift; sourceTree = "<group>"; };
|
||||||
6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; };
|
6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; };
|
||||||
|
643EA5C7296B764E005081BB /* RelayFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterView.swift; sourceTree = "<group>"; };
|
||||||
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
|
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
|
||||||
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||||
7C45AE70297353390031D7BC /* KFImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFImageModel.swift; sourceTree = "<group>"; };
|
7C60CAEE298471A1009C80D6 /* CoreSVG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreSVG.swift; sourceTree = "<group>"; };
|
||||||
7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomableScrollView.swift; sourceTree = "<group>"; };
|
7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomableScrollView.swift; sourceTree = "<group>"; };
|
||||||
|
7C95CAED299DCEF1009DCB67 /* KFOptionSetter+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KFOptionSetter+.swift"; sourceTree = "<group>"; };
|
||||||
|
7CFF6316299FEFE5005D382A /* SelectableText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableText.swift; sourceTree = "<group>"; };
|
||||||
9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; };
|
9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; };
|
||||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
||||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
||||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
||||||
|
F75BA12C29A1855400E10810 /* BookmarksManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksManager.swift; sourceTree = "<group>"; };
|
||||||
|
F75BA12E29A18EF500E10810 /* BookmarksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksView.swift; sourceTree = "<group>"; };
|
||||||
|
F7908E91298B0F0700AB113A /* RelayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayDetailView.swift; sourceTree = "<group>"; };
|
||||||
|
F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIPURLBuilder.swift; sourceTree = "<group>"; };
|
||||||
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
|
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
|
||||||
F7F0BA262978E54D009531F3 /* ParicipantsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParicipantsView.swift; sourceTree = "<group>"; };
|
F7F0BA262978E54D009531F3 /* ParicipantsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParicipantsView.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -445,8 +566,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
7C45AE6F297352F90031D7BC /* SVGKitSwift in Frameworks */,
|
|
||||||
7C45AE6D297352F90031D7BC /* SVGKit in Frameworks */,
|
|
||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
||||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */,
|
6C7DE41F2955169800E66263 /* Vault in Frameworks */,
|
||||||
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */,
|
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */,
|
||||||
@@ -564,6 +683,7 @@
|
|||||||
4C63334F283D40E500B1C9C3 /* HomeModel.swift */,
|
4C63334F283D40E500B1C9C3 /* HomeModel.swift */,
|
||||||
4C633351283D419F00B1C9C3 /* SignalModel.swift */,
|
4C633351283D419F00B1C9C3 /* SignalModel.swift */,
|
||||||
4C5F9113283D694D0052CD1C /* FollowTarget.swift */,
|
4C5F9113283D694D0052CD1C /* FollowTarget.swift */,
|
||||||
|
F75BA12C29A1855400E10810 /* BookmarksManager.swift */,
|
||||||
4C5F9115283D855D0052CD1C /* EventsModel.swift */,
|
4C5F9115283D855D0052CD1C /* EventsModel.swift */,
|
||||||
4C5F9117283D88E40052CD1C /* FollowingModel.swift */,
|
4C5F9117283D88E40052CD1C /* FollowingModel.swift */,
|
||||||
4C987B56283FD07F0042CE38 /* FollowersModel.swift */,
|
4C987B56283FD07F0042CE38 /* FollowersModel.swift */,
|
||||||
@@ -575,9 +695,13 @@
|
|||||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */,
|
BA693073295D649800ADDB87 /* UserSettingsStore.swift */,
|
||||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */,
|
4FE60CDC295E1C5E00105A1F /* Wallet.swift */,
|
||||||
4CB88392296F798300DC99E7 /* ReactionsModel.swift */,
|
4CB88392296F798300DC99E7 /* ReactionsModel.swift */,
|
||||||
7C45AE70297353390031D7BC /* KFImageModel.swift */,
|
|
||||||
4CF0ABD32980996B00D66079 /* Report.swift */,
|
4CF0ABD32980996B00D66079 /* Report.swift */,
|
||||||
4CF0ABDD2981A69500D66079 /* MutelistModel.swift */,
|
4CF0ABDD2981A69500D66079 /* MutelistModel.swift */,
|
||||||
|
3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */,
|
||||||
|
3AAA95C9298DF87B00F3D526 /* TranslationService.swift */,
|
||||||
|
3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */,
|
||||||
|
4CE8795A2996C47A00F758CC /* ZapsModel.swift */,
|
||||||
|
3AA59D1C2999B0400061C48E /* DraftsModel.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -585,6 +709,11 @@
|
|||||||
4C75EFA227FA576C0006080F /* Views */ = {
|
4C75EFA227FA576C0006080F /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4CE0E2B029A3DF4700DB4CA2 /* Timeline */,
|
||||||
|
4CE879562996C44A00F758CC /* Zaps */,
|
||||||
|
4CB9D4A52992D01900A9A7E4 /* Profile */,
|
||||||
|
4CAAD8AE29888A9B00060CEA /* Relays */,
|
||||||
|
4CF0ABF42985CD4200D66079 /* Posting */,
|
||||||
4CF0ABDF2981A83000D66079 /* Muting */,
|
4CF0ABDF2981A83000D66079 /* Muting */,
|
||||||
4CC7AAEE297F11B300430951 /* Events */,
|
4CC7AAEE297F11B300430951 /* Events */,
|
||||||
3AA24800297E3DAE0090C62D /* Reposts */,
|
3AA24800297E3DAE0090C62D /* Reposts */,
|
||||||
@@ -592,6 +721,7 @@
|
|||||||
4CB88387296AF97C00DC99E7 /* ActionBar */,
|
4CB88387296AF97C00DC99E7 /* ActionBar */,
|
||||||
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
|
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
|
||||||
4C363A8728236948006E126D /* BlocksView.swift */,
|
4C363A8728236948006E126D /* BlocksView.swift */,
|
||||||
|
F75BA12E29A18EF500E10810 /* BookmarksView.swift */,
|
||||||
4C285C8128385570008A31F1 /* CarouselView.swift */,
|
4C285C8128385570008A31F1 /* CarouselView.swift */,
|
||||||
4C0A3F8B280F5FCA000448DE /* ChatroomView.swift */,
|
4C0A3F8B280F5FCA000448DE /* ChatroomView.swift */,
|
||||||
4C0A3F90280F6528000448DE /* ChatView.swift */,
|
4C0A3F90280F6528000448DE /* ChatView.swift */,
|
||||||
@@ -618,9 +748,6 @@
|
|||||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||||
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
||||||
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
||||||
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
|
|
||||||
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
|
||||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
|
||||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
||||||
F7F0BA262978E54D009531F3 /* ParicipantsView.swift */,
|
F7F0BA262978E54D009531F3 /* ParicipantsView.swift */,
|
||||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
|
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
|
||||||
@@ -630,7 +757,6 @@
|
|||||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
||||||
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
|
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
|
||||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
||||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
|
||||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
|
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
|
||||||
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
|
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
|
||||||
647D9A8C2968520300A295DE /* SideMenuView.swift */,
|
647D9A8C2968520300A295DE /* SideMenuView.swift */,
|
||||||
@@ -640,6 +766,8 @@
|
|||||||
4CF0ABD529817F5B00D66079 /* ReportView.swift */,
|
4CF0ABD529817F5B00D66079 /* ReportView.swift */,
|
||||||
4CF0ABE42981EE0C00D66079 /* EULAView.swift */,
|
4CF0ABE42981EE0C00D66079 /* EULAView.swift */,
|
||||||
3AA247FE297E3D900090C62D /* RepostsView.swift */,
|
3AA247FE297E3D900090C62D /* RepostsView.swift */,
|
||||||
|
5C513FCB2984ACA60072348F /* QRCodeView.swift */,
|
||||||
|
643EA5C7296B764E005081BB /* RelayFilterView.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -667,8 +795,11 @@
|
|||||||
4C7FF7D628233637009601DB /* Util */ = {
|
4C7FF7D628233637009601DB /* Util */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4CE879492995B58700F758CC /* Relays */,
|
||||||
4CF0ABEA29844B2F00D66079 /* AnyCodable */,
|
4CF0ABEA29844B2F00D66079 /* AnyCodable */,
|
||||||
|
4CC7AAE6297EFA7B00430951 /* Zap.swift */,
|
||||||
4C3A1D322960DB0500558C0F /* Markdown.swift */,
|
4C3A1D322960DB0500558C0F /* Markdown.swift */,
|
||||||
|
F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */,
|
||||||
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
|
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
|
||||||
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
|
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
|
||||||
4C363A8328233689006E126D /* Parser.swift */,
|
4C363A8328233689006E126D /* Parser.swift */,
|
||||||
@@ -683,10 +814,36 @@
|
|||||||
64FBD06E296255C400D9D3B2 /* Theme.swift */,
|
64FBD06E296255C400D9D3B2 /* Theme.swift */,
|
||||||
4CB8838529656C8B00DC99E7 /* NIP05.swift */,
|
4CB8838529656C8B00DC99E7 /* NIP05.swift */,
|
||||||
4CF0ABD72981980C00D66079 /* Lists.swift */,
|
4CF0ABD72981980C00D66079 /* Lists.swift */,
|
||||||
|
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */,
|
||||||
|
7C60CAEE298471A1009C80D6 /* CoreSVG.swift */,
|
||||||
|
4CAAD8AC298851D000060CEA /* AccountDeletion.swift */,
|
||||||
|
4CB883A52975F83C00DC99E7 /* LNUrlPayRequest.swift */,
|
||||||
|
4CB883A72975FC1800DC99E7 /* Zaps.swift */,
|
||||||
|
4CB883B5297730E400DC99E7 /* LNUrls.swift */,
|
||||||
|
3AB72AB8298ECF30004BB58C /* Translator.swift */,
|
||||||
|
4C2CDDF6299D4A5E00879FD5 /* Debouncer.swift */,
|
||||||
|
7C95CAED299DCEF1009DCB67 /* KFOptionSetter+.swift */,
|
||||||
|
4CE0E2AE29A2E82100DB4CA2 /* EventHolder.swift */,
|
||||||
|
3A3040F029A8FF97008A0F29 /* LocalizationUtil.swift */,
|
||||||
);
|
);
|
||||||
path = Util;
|
path = Util;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CAAD8AE29888A9B00060CEA /* Relays */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CE879532996BA0000F758CC /* Detail */,
|
||||||
|
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
|
||||||
|
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
||||||
|
4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */,
|
||||||
|
F7908E91298B0F0700AB113A /* RelayDetailView.swift */,
|
||||||
|
4CE8794D2996B16A00F758CC /* RelayToggle.swift */,
|
||||||
|
4CE8794F2996B2BD00F758CC /* RelayStatus.swift */,
|
||||||
|
4CE879512996B68900F758CC /* RelayType.swift */,
|
||||||
|
);
|
||||||
|
path = Relays;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CB88387296AF97C00DC99E7 /* ActionBar */ = {
|
4CB88387296AF97C00DC99E7 /* ActionBar */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -704,6 +861,15 @@
|
|||||||
path = Reactions;
|
path = Reactions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CB9D4A52992D01900A9A7E4 /* Profile */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CB9D4A62992D02B00A9A7E4 /* ProfileNameView.swift */,
|
||||||
|
4CB9D4A82992D2F400A9A7E4 /* FollowsYou.swift */,
|
||||||
|
);
|
||||||
|
path = Profile;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CC7AAEE297F11B300430951 /* Events */ = {
|
4CC7AAEE297F11B300430951 /* Events */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -715,10 +881,21 @@
|
|||||||
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */,
|
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */,
|
||||||
4CC7AAF9297F64AC00430951 /* EventMenu.swift */,
|
4CC7AAF9297F64AC00430951 /* EventMenu.swift */,
|
||||||
4CF0ABE6298444FC00D66079 /* MutedEventView.swift */,
|
4CF0ABE6298444FC00D66079 /* MutedEventView.swift */,
|
||||||
|
4C3D52B5298DB4E6001C5831 /* ZapEvent.swift */,
|
||||||
|
4C3D52B7298DB5C6001C5831 /* TextEvent.swift */,
|
||||||
);
|
);
|
||||||
path = Events;
|
path = Events;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CE0E2B029A3DF4700DB4CA2 /* Timeline */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CE0E2B129A3DF6900DB4CA2 /* LoadMoreButton.swift */,
|
||||||
|
4CE0E2B529A3ED5500DB4CA2 /* InnerTimelineView.swift */,
|
||||||
|
);
|
||||||
|
path = Timeline;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -732,8 +909,12 @@
|
|||||||
4CB8838C296F710400DC99E7 /* Reposted.swift */,
|
4CB8838C296F710400DC99E7 /* Reposted.swift */,
|
||||||
4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */,
|
4CBCA92F297DB57F00EC6B2F /* WebsiteLink.swift */,
|
||||||
4CC7AAEC297F0B9E00430951 /* Highlight.swift */,
|
4CC7AAEC297F0B9E00430951 /* Highlight.swift */,
|
||||||
|
5C513FB9297F72980072348F /* CustomPicker.swift */,
|
||||||
4CF0ABE22981BC7D00D66079 /* UserView.swift */,
|
4CF0ABE22981BC7D00D66079 /* UserView.swift */,
|
||||||
7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */,
|
7C902AE22981D55B002AB16E /* ZoomableScrollView.swift */,
|
||||||
|
4CB883AF297705DD00DC99E7 /* ZapButton.swift */,
|
||||||
|
4C42812B298C848200DBF26F /* TranslateView.swift */,
|
||||||
|
7CFF6316299FEFE5005D382A /* SelectableText.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -793,6 +974,7 @@
|
|||||||
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */,
|
||||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */,
|
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */,
|
||||||
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */,
|
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */,
|
||||||
4C363A9F2828A8DD006E126D /* LikeTests.swift */,
|
4C363A9F2828A8DD006E126D /* LikeTests.swift */,
|
||||||
@@ -802,6 +984,11 @@
|
|||||||
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */,
|
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */,
|
||||||
4CB88399297322D200DC99E7 /* DMTests.swift */,
|
4CB88399297322D200DC99E7 /* DMTests.swift */,
|
||||||
4CF0ABDB2981A19E00D66079 /* ListTests.swift */,
|
4CF0ABDB2981A19E00D66079 /* ListTests.swift */,
|
||||||
|
4CB883A9297612FF00DC99E7 /* ZapTests.swift */,
|
||||||
|
4CB883AD2976FA9300DC99E7 /* FormatTests.swift */,
|
||||||
|
3A3040EC29A5CB86008A0F29 /* ReplyDescriptionTests.swift */,
|
||||||
|
3A3040EE29A8FEE9008A0F29 /* EventDetailBarTests.swift */,
|
||||||
|
3A3040F229A91366008A0F29 /* ProfileViewTests.swift */,
|
||||||
);
|
);
|
||||||
path = damusTests;
|
path = damusTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -815,6 +1002,31 @@
|
|||||||
path = damusUITests;
|
path = damusUITests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CE879492995B58700F758CC /* Relays */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CE8794729941DA700F758CC /* RelayFilters.swift */,
|
||||||
|
4CE8794B2995B59E00F758CC /* RelayMetadatas.swift */,
|
||||||
|
);
|
||||||
|
path = Relays;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
4CE879532996BA0000F758CC /* Detail */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CE879542996BAB900F758CC /* RelayPaidDetail.swift */,
|
||||||
|
);
|
||||||
|
path = Detail;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
4CE879562996C44A00F758CC /* Zaps */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CE879572996C45300F758CC /* ZapsView.swift */,
|
||||||
|
);
|
||||||
|
path = Zaps;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CEE2AE62804F57B00AB5EEF /* Frameworks */ = {
|
4CEE2AE62804F57B00AB5EEF /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -841,6 +1053,14 @@
|
|||||||
path = AnyCodable;
|
path = AnyCodable;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4CF0ABF42985CD4200D66079 /* Posting */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4CF0ABF52985CD5500D66079 /* UserSearch.swift */,
|
||||||
|
);
|
||||||
|
path = Posting;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
F7F0BA23297892AE009531F3 /* Modifiers */ = {
|
F7F0BA23297892AE009531F3 /* Modifiers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -870,8 +1090,6 @@
|
|||||||
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
||||||
4C06670328FC7EC500038D2A /* Kingfisher */,
|
4C06670328FC7EC500038D2A /* Kingfisher */,
|
||||||
6C7DE41E2955169800E66263 /* Vault */,
|
6C7DE41E2955169800E66263 /* Vault */,
|
||||||
7C45AE6C297352F90031D7BC /* SVGKit */,
|
|
||||||
7C45AE6E297352F90031D7BC /* SVGKitSwift */,
|
|
||||||
);
|
);
|
||||||
productName = damus;
|
productName = damus;
|
||||||
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
||||||
@@ -945,12 +1163,23 @@
|
|||||||
Base,
|
Base,
|
||||||
"es-419",
|
"es-419",
|
||||||
"en-US",
|
"en-US",
|
||||||
"de-AT",
|
|
||||||
"de-DE",
|
|
||||||
"tr-TR",
|
"tr-TR",
|
||||||
"fr-FR",
|
"fr-FR",
|
||||||
"lv-LV",
|
"lv-LV",
|
||||||
"it-IT",
|
"it-IT",
|
||||||
|
de,
|
||||||
|
"pt-PT",
|
||||||
|
"pl-PL",
|
||||||
|
ar,
|
||||||
|
nl,
|
||||||
|
"zh-CN",
|
||||||
|
"el-GR",
|
||||||
|
ja,
|
||||||
|
id,
|
||||||
|
cs,
|
||||||
|
ru,
|
||||||
|
"zh-HK",
|
||||||
|
"zh-TW",
|
||||||
);
|
);
|
||||||
mainGroup = 4CE6DEDA27F7A08100C66700;
|
mainGroup = 4CE6DEDA27F7A08100C66700;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
@@ -958,7 +1187,6 @@
|
|||||||
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
||||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
||||||
7C45AE6B297352F90031D7BC /* XCRemoteSwiftPackageReference "SVGKit" */,
|
|
||||||
);
|
);
|
||||||
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -1012,10 +1240,12 @@
|
|||||||
4C216F34286F5ACD00040376 /* DMView.swift in Sources */,
|
4C216F34286F5ACD00040376 /* DMView.swift in Sources */,
|
||||||
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
|
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
|
||||||
4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */,
|
4CE4F9E1285287B800C00DD9 /* TextFieldAlert.swift in Sources */,
|
||||||
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */,
|
||||||
4C363AA828297703006E126D /* InsertSort.swift in Sources */,
|
4C363AA828297703006E126D /* InsertSort.swift in Sources */,
|
||||||
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
4C285C86283892E7008A31F1 /* CreateAccountModel.swift in Sources */,
|
||||||
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
4C64987C286D03E000EAE2B3 /* DirectMessagesView.swift in Sources */,
|
||||||
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */,
|
7C902AE32981D55B002AB16E /* ZoomableScrollView.swift in Sources */,
|
||||||
|
4CE8794C2995B59E00F758CC /* RelayMetadatas.swift in Sources */,
|
||||||
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
||||||
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
||||||
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
||||||
@@ -1031,19 +1261,28 @@
|
|||||||
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
|
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
|
||||||
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
||||||
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */,
|
3AA247FD297E3CFF0090C62D /* RepostsModel.swift in Sources */,
|
||||||
|
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */,
|
||||||
|
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */,
|
||||||
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
||||||
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
||||||
|
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */,
|
||||||
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,
|
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,
|
||||||
4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */,
|
4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */,
|
||||||
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */,
|
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */,
|
||||||
|
4C3D52B6298DB4E6001C5831 /* ZapEvent.swift in Sources */,
|
||||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */,
|
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */,
|
||||||
F7F0BA272978E54D009531F3 /* ParicipantsView.swift in Sources */,
|
F7F0BA272978E54D009531F3 /* ParicipantsView.swift in Sources */,
|
||||||
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
|
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
|
||||||
4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */,
|
4CF0ABE32981BC7D00D66079 /* UserView.swift in Sources */,
|
||||||
|
4CE0E2AF29A2E82100DB4CA2 /* EventHolder.swift in Sources */,
|
||||||
|
4CE0E2B229A3DF6900DB4CA2 /* LoadMoreButton.swift in Sources */,
|
||||||
|
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */,
|
||||||
|
4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */,
|
||||||
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
|
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
|
||||||
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */,
|
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */,
|
||||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
||||||
|
4CB883A62975F83C00DC99E7 /* LNUrlPayRequest.swift in Sources */,
|
||||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
|
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
|
||||||
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */,
|
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */,
|
||||||
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */,
|
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */,
|
||||||
@@ -1052,6 +1291,7 @@
|
|||||||
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
|
||||||
4C363A8428233689006E126D /* Parser.swift in Sources */,
|
4C363A8428233689006E126D /* Parser.swift in Sources */,
|
||||||
|
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */,
|
||||||
4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
|
4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
|
||||||
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
||||||
@@ -1062,51 +1302,65 @@
|
|||||||
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
|
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
|
||||||
4CF0ABE7298444FD00D66079 /* MutedEventView.swift in Sources */,
|
4CF0ABE7298444FD00D66079 /* MutedEventView.swift in Sources */,
|
||||||
4CF0ABE12981A83900D66079 /* MutelistView.swift in Sources */,
|
4CF0ABE12981A83900D66079 /* MutelistView.swift in Sources */,
|
||||||
|
4CB883A82975FC1800DC99E7 /* Zaps.swift in Sources */,
|
||||||
4C75EFB128049D510006080F /* NostrResponse.swift in Sources */,
|
4C75EFB128049D510006080F /* NostrResponse.swift in Sources */,
|
||||||
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */,
|
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */,
|
||||||
4CC7AAEB297F0AEC00430951 /* BuilderEventView.swift in Sources */,
|
4CC7AAEB297F0AEC00430951 /* BuilderEventView.swift in Sources */,
|
||||||
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */,
|
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */,
|
||||||
|
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */,
|
||||||
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */,
|
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */,
|
||||||
|
3A3040F129A8FF97008A0F29 /* LocalizationUtil.swift in Sources */,
|
||||||
|
F75BA12D29A1855400E10810 /* BookmarksManager.swift in Sources */,
|
||||||
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */,
|
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */,
|
||||||
|
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */,
|
||||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
|
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
|
||||||
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
|
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
|
||||||
7C45AE71297353390031D7BC /* KFImageModel.swift in Sources */,
|
|
||||||
4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */,
|
4CF0ABE929844AF100D66079 /* AnyCodable.swift in Sources */,
|
||||||
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */,
|
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */,
|
||||||
4CC7AAF2297F129C00430951 /* EmbeddedEventView.swift in Sources */,
|
4CC7AAF2297F129C00430951 /* EmbeddedEventView.swift in Sources */,
|
||||||
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
|
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
|
||||||
|
4CC7AAE7297EFA7B00430951 /* Zap.swift in Sources */,
|
||||||
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||||
|
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */,
|
||||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||||
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */,
|
4CC7AAF0297F11C700430951 /* SelectedEventView.swift in Sources */,
|
||||||
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */,
|
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */,
|
||||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
||||||
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
||||||
|
4CE879522996B68900F758CC /* RelayType.swift in Sources */,
|
||||||
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
|
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
|
||||||
|
4CE8795B2996C47A00F758CC /* ZapsModel.swift in Sources */,
|
||||||
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */,
|
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */,
|
||||||
4C3EA67528FF7A5A00C48A62 /* take.c in Sources */,
|
4C3EA67528FF7A5A00C48A62 /* take.c in Sources */,
|
||||||
4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */,
|
4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */,
|
||||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */,
|
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */,
|
||||||
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */,
|
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */,
|
||||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */,
|
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */,
|
||||||
|
4CE879582996C45300F758CC /* ZapsView.swift in Sources */,
|
||||||
4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */,
|
4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */,
|
||||||
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */,
|
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */,
|
||||||
4C363A94282704FA006E126D /* Post.swift in Sources */,
|
4C363A94282704FA006E126D /* Post.swift in Sources */,
|
||||||
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
|
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
|
||||||
|
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */,
|
||||||
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
|
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
|
||||||
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */,
|
4C64987E286D082C00EAE2B3 /* DirectMessagesModel.swift in Sources */,
|
||||||
|
4CE0E2B629A3ED5500DB4CA2 /* InnerTimelineView.swift in Sources */,
|
||||||
4C363A8828236948006E126D /* BlocksView.swift in Sources */,
|
4C363A8828236948006E126D /* BlocksView.swift in Sources */,
|
||||||
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
|
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */,
|
||||||
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
||||||
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
||||||
|
4C42812C298C848200DBF26F /* TranslateView.swift in Sources */,
|
||||||
4C363A9C282838B9006E126D /* EventRef.swift in Sources */,
|
4C363A9C282838B9006E126D /* EventRef.swift in Sources */,
|
||||||
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */,
|
3AA24802297E3DC20090C62D /* RepostView.swift in Sources */,
|
||||||
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */,
|
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */,
|
||||||
4C3EA66528FF5F6800C48A62 /* mem.c in Sources */,
|
4C3EA66528FF5F6800C48A62 /* mem.c in Sources */,
|
||||||
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */,
|
4CF0ABE52981EE0C00D66079 /* EULAView.swift in Sources */,
|
||||||
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */,
|
4CBCA930297DB57F00EC6B2F /* WebsiteLink.swift in Sources */,
|
||||||
|
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */,
|
||||||
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
||||||
|
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
|
||||||
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
||||||
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
|
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
|
||||||
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */,
|
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */,
|
||||||
@@ -1116,6 +1370,7 @@
|
|||||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
||||||
4CF0ABD82981980C00D66079 /* Lists.swift in Sources */,
|
4CF0ABD82981980C00D66079 /* Lists.swift in Sources */,
|
||||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||||
|
7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */,
|
||||||
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */,
|
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */,
|
||||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
||||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
||||||
@@ -1133,27 +1388,37 @@
|
|||||||
4C06670E28FDEAA000038D2A /* utf8.c in Sources */,
|
4C06670E28FDEAA000038D2A /* utf8.c in Sources */,
|
||||||
4C3EA66D28FF782800C48A62 /* amount.c in Sources */,
|
4C3EA66D28FF782800C48A62 /* amount.c in Sources */,
|
||||||
4C3AC7A728369BA200E1F516 /* SearchHomeView.swift in Sources */,
|
4C3AC7A728369BA200E1F516 /* SearchHomeView.swift in Sources */,
|
||||||
|
4CB883B0297705DD00DC99E7 /* ZapButton.swift in Sources */,
|
||||||
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
||||||
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
|
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */,
|
||||||
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
||||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
||||||
4CF0ABDE2981A69500D66079 /* MutelistModel.swift in Sources */,
|
4CF0ABDE2981A69500D66079 /* MutelistModel.swift in Sources */,
|
||||||
|
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
||||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
||||||
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
||||||
|
7CFF6317299FEFE5005D382A /* SelectableText.swift in Sources */,
|
||||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
||||||
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
|
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
|
||||||
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
|
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
|
||||||
|
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */,
|
||||||
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
|
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
|
||||||
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
|
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
|
||||||
4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */,
|
4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */,
|
||||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */,
|
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */,
|
||||||
|
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */,
|
||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
||||||
|
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */,
|
||||||
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
|
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
|
||||||
|
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */,
|
||||||
4CF0ABD42980996B00D66079 /* Report.swift in Sources */,
|
4CF0ABD42980996B00D66079 /* Report.swift in Sources */,
|
||||||
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */,
|
|
||||||
4C06670B28FDE64700038D2A /* damus.c in Sources */,
|
4C06670B28FDE64700038D2A /* damus.c in Sources */,
|
||||||
|
4C2CDDF7299D4A5E00879FD5 /* Debouncer.swift in Sources */,
|
||||||
|
3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */,
|
||||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */,
|
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */,
|
||||||
3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */,
|
3AA247FF297E3D900090C62D /* RepostsView.swift in Sources */,
|
||||||
|
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */,
|
||||||
|
4CE879502996B2BD00F758CC /* RelayStatus.swift in Sources */,
|
||||||
4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */,
|
4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */,
|
||||||
4CC7AAF4297F18B400430951 /* ReplyDescription.swift in Sources */,
|
4CC7AAF4297F18B400430951 /* ReplyDescription.swift in Sources */,
|
||||||
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
|
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
|
||||||
@@ -1162,7 +1427,9 @@
|
|||||||
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */,
|
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */,
|
||||||
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */,
|
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */,
|
||||||
4C3AC7A52836987600E1F516 /* MainTabView.swift in Sources */,
|
4C3AC7A52836987600E1F516 /* MainTabView.swift in Sources */,
|
||||||
|
3AA59D1D2999B0400061C48E /* DraftsModel.swift in Sources */,
|
||||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
|
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
|
||||||
|
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1170,14 +1437,20 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
3A3040ED29A5CB86008A0F29 /* ReplyDescriptionTests.swift in Sources */,
|
||||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
||||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
||||||
|
3A3040EF29A8FEE9008A0F29 /* EventDetailBarTests.swift in Sources */,
|
||||||
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
|
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
|
||||||
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
|
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
|
||||||
|
4CB883AA297612FF00DC99E7 /* ZapTests.swift in Sources */,
|
||||||
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */,
|
4CB8839A297322D200DC99E7 /* DMTests.swift in Sources */,
|
||||||
|
4CB883AE2976FA9300DC99E7 /* FormatTests.swift in Sources */,
|
||||||
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
||||||
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */,
|
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */,
|
||||||
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */,
|
||||||
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */,
|
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */,
|
||||||
|
3A3040F329A91366008A0F29 /* ProfileViewTests.swift in Sources */,
|
||||||
4CF0ABDC2981A19E00D66079 /* ListTests.swift in Sources */,
|
4CF0ABDC2981A19E00D66079 /* ListTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1212,12 +1485,23 @@
|
|||||||
children = (
|
children = (
|
||||||
3A5C4575296A879E0032D398 /* es-419 */,
|
3A5C4575296A879E0032D398 /* es-419 */,
|
||||||
3A2B8B0A296A8982009CC16D /* en-US */,
|
3A2B8B0A296A8982009CC16D /* en-US */,
|
||||||
3A5EA111297CCF6C00569477 /* de-AT */,
|
|
||||||
3AEABD22297CCFA8003F2975 /* de-DE */,
|
|
||||||
3AEB8005297CCEA900713A25 /* tr-TR */,
|
3AEB8005297CCEA900713A25 /* tr-TR */,
|
||||||
3A4F3322297CCFEE004B5F72 /* fr-FR */,
|
3A4F3322297CCFEE004B5F72 /* fr-FR */,
|
||||||
3A185A06297F2C3800F4BDC0 /* lv-LV */,
|
3A185A06297F2C3800F4BDC0 /* lv-LV */,
|
||||||
3A929C22297F2CF80090925E /* it-IT */,
|
3A929C22297F2CF80090925E /* it-IT */,
|
||||||
|
3AB5B86C2986D8A3006599D2 /* de */,
|
||||||
|
3AF6336A29884C6B0005672A /* pt-PT */,
|
||||||
|
3A93342B29884CA600D6A8F3 /* pl-PL */,
|
||||||
|
3AC524F0298C000B00693EBF /* ar */,
|
||||||
|
3A96D41C298DA94500388A2A /* nl */,
|
||||||
|
3A5CAE1F298DC0DB00B5334F /* zh-CN */,
|
||||||
|
3A25EF152992DA5D008ABE69 /* el-GR */,
|
||||||
|
3A66D929299472FA008B44F4 /* ja */,
|
||||||
|
3A41E55B299D52BE001FA465 /* id */,
|
||||||
|
3A8624DB299E82BE00BD8BE9 /* cs */,
|
||||||
|
3A827A1A299FC69D00C4D171 /* ru */,
|
||||||
|
3A3040FB29A91F03008A0F29 /* zh-HK */,
|
||||||
|
3A3040FD29A91F31008A0F29 /* zh-TW */,
|
||||||
);
|
);
|
||||||
name = Localizable.stringsdict;
|
name = Localizable.stringsdict;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1226,12 +1510,23 @@
|
|||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
3ACB685B297633BC00C46468 /* es-419 */,
|
3ACB685B297633BC00C46468 /* es-419 */,
|
||||||
3A5EA10F297CCF6C00569477 /* de-AT */,
|
|
||||||
3AEABD20297CCFA8003F2975 /* de-DE */,
|
|
||||||
3AEB8003297CCEA800713A25 /* tr-TR */,
|
3AEB8003297CCEA800713A25 /* tr-TR */,
|
||||||
3A4F3320297CCFEE004B5F72 /* fr-FR */,
|
3A4F3320297CCFEE004B5F72 /* fr-FR */,
|
||||||
3A185A04297F2C3800F4BDC0 /* lv-LV */,
|
3A185A04297F2C3800F4BDC0 /* lv-LV */,
|
||||||
3A929C20297F2CF80090925E /* it-IT */,
|
3A929C20297F2CF80090925E /* it-IT */,
|
||||||
|
3AB5B86A2986D8A3006599D2 /* de */,
|
||||||
|
3AF6336829884C6B0005672A /* pt-PT */,
|
||||||
|
3A93342929884CA600D6A8F3 /* pl-PL */,
|
||||||
|
3AC524EE298C000B00693EBF /* ar */,
|
||||||
|
3A96D41A298DA94500388A2A /* nl */,
|
||||||
|
3A5CAE1D298DC0DB00B5334F /* zh-CN */,
|
||||||
|
3A25EF132992DA5D008ABE69 /* el-GR */,
|
||||||
|
3A66D927299472FA008B44F4 /* ja */,
|
||||||
|
3A41E559299D52BE001FA465 /* id */,
|
||||||
|
3A8624D9299E82BE00BD8BE9 /* cs */,
|
||||||
|
3A827A18299FC69D00C4D171 /* ru */,
|
||||||
|
3A3040F929A91ED6008A0F29 /* zh-HK */,
|
||||||
|
3A3040FC29A91F31008A0F29 /* zh-TW */,
|
||||||
);
|
);
|
||||||
name = InfoPlist.strings;
|
name = InfoPlist.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1240,12 +1535,23 @@
|
|||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
3ACB685E297633BC00C46468 /* es-419 */,
|
3ACB685E297633BC00C46468 /* es-419 */,
|
||||||
3A5EA110297CCF6C00569477 /* de-AT */,
|
|
||||||
3AEABD21297CCFA8003F2975 /* de-DE */,
|
|
||||||
3AEB8004297CCEA800713A25 /* tr-TR */,
|
3AEB8004297CCEA800713A25 /* tr-TR */,
|
||||||
3A4F3321297CCFEE004B5F72 /* fr-FR */,
|
3A4F3321297CCFEE004B5F72 /* fr-FR */,
|
||||||
3A185A05297F2C3800F4BDC0 /* lv-LV */,
|
3A185A05297F2C3800F4BDC0 /* lv-LV */,
|
||||||
3A929C21297F2CF80090925E /* it-IT */,
|
3A929C21297F2CF80090925E /* it-IT */,
|
||||||
|
3AB5B86B2986D8A3006599D2 /* de */,
|
||||||
|
3AF6336929884C6B0005672A /* pt-PT */,
|
||||||
|
3A93342A29884CA600D6A8F3 /* pl-PL */,
|
||||||
|
3AC524EF298C000B00693EBF /* ar */,
|
||||||
|
3A96D41B298DA94500388A2A /* nl */,
|
||||||
|
3A5CAE1E298DC0DB00B5334F /* zh-CN */,
|
||||||
|
3A25EF142992DA5D008ABE69 /* el-GR */,
|
||||||
|
3A66D928299472FA008B44F4 /* ja */,
|
||||||
|
3A41E55A299D52BE001FA465 /* id */,
|
||||||
|
3A8624DA299E82BE00BD8BE9 /* cs */,
|
||||||
|
3A827A19299FC69D00C4D171 /* ru */,
|
||||||
|
3A3040FA29A91EFC008A0F29 /* zh-HK */,
|
||||||
|
3A3040FE29A91F31008A0F29 /* zh-TW */,
|
||||||
);
|
);
|
||||||
name = Localizable.strings;
|
name = Localizable.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1381,7 +1687,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 11;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1389,6 +1695,7 @@
|
|||||||
INFOPLIST_FILE = damus/Info.plist;
|
INFOPLIST_FILE = damus/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Damus;
|
INFOPLIST_KEY_CFBundleDisplayName = Damus;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = "Local authentication to access private key";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
|
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
@@ -1403,7 +1710,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)",
|
"$(PROJECT_DIR)",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -1422,7 +1729,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 11;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -1430,6 +1737,7 @@
|
|||||||
INFOPLIST_FILE = damus/Info.plist;
|
INFOPLIST_FILE = damus/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Damus;
|
INFOPLIST_KEY_CFBundleDisplayName = Damus;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||||
|
INFOPLIST_KEY_NSFaceIDUsageDescription = "Local authentication to access private key";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
|
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
@@ -1444,7 +1752,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)",
|
"$(PROJECT_DIR)",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.0;
|
MARKETING_VERSION = 1.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
@@ -1604,14 +1912,6 @@
|
|||||||
minimumVersion = 1.0.0;
|
minimumVersion = 1.0.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
7C45AE6B297352F90031D7BC /* XCRemoteSwiftPackageReference "SVGKit" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/SVGKit/SVGKit";
|
|
||||||
requirement = {
|
|
||||||
kind = revision;
|
|
||||||
revision = e1f13e27b1e4c0ffe20e7d8d3984bf49c2a584d0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
@@ -1635,16 +1935,6 @@
|
|||||||
package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */;
|
package = 6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */;
|
||||||
productName = Vault;
|
productName = Vault;
|
||||||
};
|
};
|
||||||
7C45AE6C297352F90031D7BC /* SVGKit */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 7C45AE6B297352F90031D7BC /* XCRemoteSwiftPackageReference "SVGKit" */;
|
|
||||||
productName = SVGKit;
|
|
||||||
};
|
|
||||||
7C45AE6E297352F90031D7BC /* SVGKitSwift */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = 7C45AE6B297352F90031D7BC /* XCRemoteSwiftPackageReference "SVGKit" */;
|
|
||||||
productName = SVGKitSwift;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 4CE6DEDB27F7A08100C66700 /* Project object */;
|
rootObject = 4CE6DEDB27F7A08100C66700 /* Project object */;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/onevcat/Kingfisher",
|
"location" : "https://github.com/onevcat/Kingfisher",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "017f94ccfdacabb1ae7f45b75b4217b24c06e6ac",
|
"revision" : "415b1d97fb38bda1e5a6b2dde63354720832110b",
|
||||||
"version" : "7.4.0"
|
"version" : "7.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -26,14 +26,6 @@
|
|||||||
"version" : "4.0.4"
|
"version" : "4.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "svgkit",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/SVGKit/SVGKit",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "e1f13e27b1e4c0ffe20e7d8d3984bf49c2a584d0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "vault",
|
"identity" : "vault",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0xC5",
|
|
||||||
"green" : "0x43",
|
|
||||||
"red" : "0xCC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0x00",
|
|
||||||
"green" : "0x00",
|
|
||||||
"red" : "0x00"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0xFF",
|
|
||||||
"green" : "0x4D",
|
|
||||||
"red" : "0x4B"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0x1E",
|
|
||||||
"green" : "0x1C",
|
|
||||||
"red" : "0x1C"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0x4F",
|
|
||||||
"green" : "0xC3",
|
|
||||||
"red" : "0x66"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0xF4",
|
|
||||||
"green" : "0xEE",
|
|
||||||
"red" : "0xEE"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0x5F",
|
|
||||||
"green" : "0x5F",
|
|
||||||
"red" : "0x5F"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0xC5",
|
|
||||||
"green" : "0x43",
|
|
||||||
"red" : "0xCC"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -11,24 +11,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"color" : {
|
|
||||||
"color-space" : "srgb",
|
|
||||||
"components" : {
|
|
||||||
"alpha" : "1.000",
|
|
||||||
"blue" : "0xFF",
|
|
||||||
"green" : "0xFF",
|
|
||||||
"red" : "0xFF"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"idiom" : "universal"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-copy.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 354 B |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-key.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 400 B |
@@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-message-black.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"filename" : "ic-message-white 1.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances" : [
|
|
||||||
{
|
|
||||||
"appearance" : "luminosity",
|
|
||||||
"value" : "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 341 B |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-nipverified.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 950 B |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-qr.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 252 B |
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "profile-banner.jpeg",
|
"filename" : "profile-banner.jpeg",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "bbw.jpg",
|
"filename" : "bbw.jpg",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "bitcoin-p2p.png",
|
"filename" : "bitcoin-p2p.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "blixt-wallet.png",
|
"filename" : "blixt-wallet.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "bluewallet.png",
|
"filename" : "bluewallet.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "breez.jpg",
|
"filename" : "breez.jpg",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "cashapp.png",
|
"filename" : "cashapp.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "digital-nomad.png",
|
"filename" : "digital-nomad.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "encrypted-message.png",
|
"filename" : "encrypted-message.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-lightning.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 458 B |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ic-tick.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 671 B |
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "lnlink.png",
|
"filename" : "lnlink.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "damus-nobg.png",
|
"filename" : "damus-nobg.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "muun.png",
|
"filename" : "muun.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "phoenix.png",
|
"filename" : "phoenix.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "river.png",
|
"filename" : "river.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "strike.png",
|
"filename" : "strike.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "undercover.png",
|
"filename" : "undercover.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "walletofsatoshi.png",
|
"filename" : "walletofsatoshi.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "zebedee.png",
|
"filename" : "zebedee.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -2,16 +2,7 @@
|
|||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "zeus.png",
|
"filename" : "zeus.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal"
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// CustomPicker.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Eric Holguin on 1/22/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
let RECTANGLE_GRADIENT = LinearGradient(gradient: Gradient(colors: [
|
||||||
|
Color("DamusPurple"),
|
||||||
|
Color("DamusBlue")
|
||||||
|
]), startPoint: .leading, endPoint: .trailing)
|
||||||
|
|
||||||
|
struct CustomPicker<SelectionValue: Hashable, Content: View>: View {
|
||||||
|
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
|
@Namespace var picker
|
||||||
|
@Binding var selection: SelectionValue
|
||||||
|
@ViewBuilder let content: Content
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
let contentMirror = Mirror(reflecting: content)
|
||||||
|
let blocksCount = Mirror(reflecting: contentMirror.descendant("value")!).children.count
|
||||||
|
HStack {
|
||||||
|
ForEach(0..<blocksCount, id: \.self) { index in
|
||||||
|
let tupleBlock = contentMirror.descendant("value", ".\(index)")
|
||||||
|
let text = Mirror(reflecting: tupleBlock!).descendant("content") as! Text
|
||||||
|
let tag = Mirror(reflecting: tupleBlock!).descendant("modifier", "value", "tagged") as! SelectionValue
|
||||||
|
|
||||||
|
Button {
|
||||||
|
withAnimation(.spring()) {
|
||||||
|
selection = tag
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
text
|
||||||
|
.padding(EdgeInsets(top: 15, leading: 0, bottom: 10, trailing: 0))
|
||||||
|
.font(.system(size: 14, weight: .heavy))
|
||||||
|
}
|
||||||
|
.background(
|
||||||
|
Group {
|
||||||
|
if tag == selection {
|
||||||
|
Rectangle().fill(RECTANGLE_GRADIENT).frame(height: 2.5)
|
||||||
|
.matchedGeometryEffect(id: "selector", in: picker)
|
||||||
|
.cornerRadius(2.5)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
alignment: .bottom
|
||||||
|
)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.accentColor(tag == selection ? textColor() : .gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func textColor() -> Color {
|
||||||
|
colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,20 +66,11 @@ struct ImageContextMenuModifier: ViewModifier {
|
|||||||
|
|
||||||
private struct ImageContainerView: View {
|
private struct ImageContainerView: View {
|
||||||
|
|
||||||
@ObservedObject var imageModel: KFImageModel
|
let url: URL?
|
||||||
|
|
||||||
@State private var image: UIImage?
|
@State private var image: UIImage?
|
||||||
@State private var showShareSheet = false
|
@State private var showShareSheet = false
|
||||||
|
|
||||||
init(url: URL?) {
|
|
||||||
self.imageModel = KFImageModel(
|
|
||||||
url: url,
|
|
||||||
fallbackUrl: nil,
|
|
||||||
maxByteSize: 2000000, // 2 MB
|
|
||||||
downsampleSize: CGSize(width: 400, height: 400)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ImageHandler: ImageModifier {
|
private struct ImageHandler: ImageModifier {
|
||||||
@Binding var handler: UIImage?
|
@Binding var handler: UIImage?
|
||||||
|
|
||||||
@@ -91,30 +82,17 @@ private struct ImageContainerView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
||||||
KFAnimatedImage(imageModel.url)
|
KFAnimatedImage(url)
|
||||||
.callbackQueue(.dispatch(.global(qos: .background)))
|
.imageContext(.note)
|
||||||
.processingQueue(.dispatch(.global(qos: .background)))
|
|
||||||
.cacheOriginalImage()
|
|
||||||
.configure { view in
|
.configure { view in
|
||||||
view.framePreloadCount = 1
|
view.framePreloadCount = 3
|
||||||
}
|
}
|
||||||
.scaleFactor(UIScreen.main.scale)
|
|
||||||
.loadDiskFileSynchronously()
|
|
||||||
.fade(duration: 0.1)
|
|
||||||
.imageModifier(ImageHandler(handler: $image))
|
.imageModifier(ImageHandler(handler: $image))
|
||||||
.onFailure { _ in
|
|
||||||
imageModel.downloadFailed()
|
|
||||||
}
|
|
||||||
.id(imageModel.refreshID)
|
|
||||||
.clipped()
|
.clipped()
|
||||||
.modifier(ImageContextMenuModifier(url: imageModel.url, image: image, showShareSheet: $showShareSheet))
|
.modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet))
|
||||||
.sheet(isPresented: $showShareSheet) {
|
.sheet(isPresented: $showShareSheet) {
|
||||||
ShareSheet(activityItems: [imageModel.url])
|
ShareSheet(activityItems: [url])
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Update ImageCarousel with serializer and processor
|
|
||||||
// .serialize(by: imageModel.serializer)
|
|
||||||
// .setProcessor(imageModel.processor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,14 +105,6 @@ struct ImageView: View {
|
|||||||
@State private var selectedIndex = 0
|
@State private var selectedIndex = 0
|
||||||
@State var showMenu = true
|
@State var showMenu = true
|
||||||
|
|
||||||
var safeAreaInsets: UIEdgeInsets? {
|
|
||||||
return UIApplication
|
|
||||||
.shared
|
|
||||||
.connectedScenes
|
|
||||||
.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
|
|
||||||
.first { $0.isKeyWindow }?.safeAreaInsets
|
|
||||||
}
|
|
||||||
|
|
||||||
var navBarView: some View {
|
var navBarView: some View {
|
||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
@@ -180,8 +150,8 @@ struct ImageView: View {
|
|||||||
ZoomableScrollView {
|
ZoomableScrollView {
|
||||||
ImageContainerView(url: urls[index])
|
ImageContainerView(url: urls[index])
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.padding(.top, safeAreaInsets?.top)
|
.padding(.top, Theme.safeAreaInsets?.top)
|
||||||
.padding(.bottom, safeAreaInsets?.bottom)
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
}
|
}
|
||||||
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
||||||
presentationMode.wrappedValue.dismiss()
|
presentationMode.wrappedValue.dismiss()
|
||||||
@@ -210,7 +180,7 @@ struct ImageView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.animation(.easeInOut, value: showMenu)
|
.animation(.easeInOut, value: showMenu)
|
||||||
.padding(.bottom, safeAreaInsets?.bottom)
|
.padding(.bottom, Theme.safeAreaInsets?.bottom)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,12 +199,8 @@ struct ImageCarousel: View {
|
|||||||
.foregroundColor(Color.clear)
|
.foregroundColor(Color.clear)
|
||||||
.overlay {
|
.overlay {
|
||||||
KFAnimatedImage(url)
|
KFAnimatedImage(url)
|
||||||
.callbackQueue(.dispatch(.global(qos: .background)))
|
.imageContext(.note)
|
||||||
.processingQueue(.dispatch(.global(qos: .background)))
|
.cancelOnDisappear(true)
|
||||||
.cacheOriginalImage()
|
|
||||||
.loadDiskFileSynchronously()
|
|
||||||
.scaleFactor(UIScreen.main.scale)
|
|
||||||
.fade(duration: 0.1)
|
|
||||||
.configure { view in
|
.configure { view in
|
||||||
view.framePreloadCount = 3
|
view.framePreloadCount = 3
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,84 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
struct InvoiceView: View {
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@Environment(\.openURL) private var openURL
|
||||||
|
let our_pubkey: String
|
||||||
|
let invoice: Invoice
|
||||||
|
@State var showing_select_wallet: Bool = false
|
||||||
|
@State var copied = false
|
||||||
|
|
||||||
|
var CopyButton: some View {
|
||||||
|
Button {
|
||||||
|
copied = true
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||||
|
copied = false
|
||||||
|
}
|
||||||
|
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||||
|
UIPasteboard.general.string = invoice.string
|
||||||
|
} label: {
|
||||||
|
if !copied {
|
||||||
|
Image(systemName: "doc.on.clipboard")
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
} else {
|
||||||
|
Image(systemName: "checkmark.circle")
|
||||||
|
.foregroundColor(Color("DamusGreen"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var PayButton: some View {
|
||||||
|
Button {
|
||||||
|
if should_show_wallet_selector(our_pubkey) {
|
||||||
|
showing_select_wallet = true
|
||||||
|
} else {
|
||||||
|
open_with_wallet(wallet: get_default_wallet(our_pubkey).model, invoice: invoice.string)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
RoundedRectangle(cornerRadius: 20, style: .circular)
|
||||||
|
.foregroundColor(colorScheme == .light ? .black : .white)
|
||||||
|
.overlay {
|
||||||
|
Text("Pay", comment: "Button to pay a Lightning invoice.")
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(colorScheme == .light ? .white : .black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
// Temporary solution so that the "pay" button can be clicked (Yes we need an empty tap gesture)
|
||||||
|
print("pay button tap")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.foregroundColor(.secondary.opacity(0.1))
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
HStack {
|
||||||
|
Label("", systemImage: "bolt.fill")
|
||||||
|
.foregroundColor(.orange)
|
||||||
|
Text("Lightning Invoice", comment: "Indicates that the view is for paying a Lightning invoice.")
|
||||||
|
Spacer()
|
||||||
|
CopyButton
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
Text(invoice.description_string)
|
||||||
|
Text(invoice.amount.amount_sats_str())
|
||||||
|
.font(.title)
|
||||||
|
PayButton
|
||||||
|
.frame(height: 50)
|
||||||
|
.zIndex(10.0)
|
||||||
|
}
|
||||||
|
.padding(30)
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
|
||||||
|
SelectWalletView(showingSelectWallet: $showing_select_wallet, our_pubkey: our_pubkey, invoice: invoice.string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func open_with_wallet(wallet: Wallet.Model, invoice: String) {
|
func open_with_wallet(wallet: Wallet.Model, invoice: String) {
|
||||||
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
|
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
@@ -28,68 +106,12 @@ func open_with_wallet(wallet: Wallet.Model, invoice: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InvoiceView: View {
|
|
||||||
@Environment(\.colorScheme) var colorScheme
|
|
||||||
@Environment(\.openURL) private var openURL
|
|
||||||
|
|
||||||
let invoice: Invoice
|
|
||||||
@State var showing_select_wallet: Bool = false
|
|
||||||
@ObservedObject var user_settings = UserSettingsStore()
|
|
||||||
|
|
||||||
var PayButton: some View {
|
|
||||||
Button {
|
|
||||||
if user_settings.show_wallet_selector {
|
|
||||||
showing_select_wallet = true
|
|
||||||
} else {
|
|
||||||
open_with_wallet(wallet: user_settings.default_wallet.model, invoice: invoice.string)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
RoundedRectangle(cornerRadius: 20)
|
|
||||||
.foregroundColor(colorScheme == .light ? .black : .white)
|
|
||||||
.overlay {
|
|
||||||
Text("Pay", comment: "Button to pay a Lightning invoice.")
|
|
||||||
.fontWeight(.medium)
|
|
||||||
.foregroundColor(colorScheme == .light ? .white : .black)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//.buttonStyle(.bordered)
|
|
||||||
.onTapGesture {
|
|
||||||
// Temporary solution so that the "pay" button can be clicked (Yes we need an empty tap gesture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
RoundedRectangle(cornerRadius: 10)
|
|
||||||
.foregroundColor(.secondary.opacity(0.1))
|
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
|
||||||
HStack {
|
|
||||||
Label("", systemImage: "bolt.fill")
|
|
||||||
.foregroundColor(.orange)
|
|
||||||
Text("Lightning Invoice", comment: "Indicates that the view is for paying a Lightning invoice.")
|
|
||||||
}
|
|
||||||
Divider()
|
|
||||||
Text(invoice.description)
|
|
||||||
Text(invoice.amount.amount_sats_str())
|
|
||||||
.font(.title)
|
|
||||||
PayButton
|
|
||||||
.frame(height: 50)
|
|
||||||
.zIndex(10.0)
|
|
||||||
}
|
|
||||||
.padding(30)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
|
|
||||||
SelectWalletView(showingSelectWallet: $showing_select_wallet, invoice: invoice.string).environmentObject(user_settings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let test_invoice = Invoice(description: "this is a description", amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119)
|
let test_invoice = Invoice(description: .description("this is a description"), amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119)
|
||||||
|
|
||||||
struct InvoiceView_Previews: PreviewProvider {
|
struct InvoiceView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
InvoiceView(invoice: test_invoice)
|
InvoiceView(our_pubkey: "", invoice: test_invoice)
|
||||||
.frame(width: 200, height: 200)
|
.frame(width: 300, height: 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct InvoicesView: View {
|
struct InvoicesView: View {
|
||||||
|
let our_pubkey: String
|
||||||
var invoices: [Invoice]
|
var invoices: [Invoice]
|
||||||
|
|
||||||
@State var open_sheet: Bool = false
|
@State var open_sheet: Bool = false
|
||||||
@@ -16,7 +17,7 @@ struct InvoicesView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
TabView {
|
TabView {
|
||||||
ForEach(invoices, id: \.string) { invoice in
|
ForEach(invoices, id: \.string) { invoice in
|
||||||
InvoiceView(invoice: invoice)
|
InvoiceView(our_pubkey: our_pubkey, invoice: invoice)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Text(invoice.string)
|
Text(invoice.string)
|
||||||
}
|
}
|
||||||
@@ -30,7 +31,7 @@ struct InvoicesView: View {
|
|||||||
|
|
||||||
struct InvoicesView_Previews: PreviewProvider {
|
struct InvoicesView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
InvoicesView(invoices: [Invoice.init(description: "description", amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)])
|
InvoicesView(our_pubkey: "", invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)])
|
||||||
.frame(width: 300)
|
.frame(width: 300)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// SelectableText.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Oleg Abalonski on 2/16/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SelectableText: View {
|
||||||
|
|
||||||
|
let attributedString: AttributedString
|
||||||
|
|
||||||
|
@State private var selectedTextHeight: CGFloat = .zero
|
||||||
|
@State private var selectedTextWidth: CGFloat = .zero
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
GeometryReader { geo in
|
||||||
|
TextViewRepresentable(
|
||||||
|
attributedString: attributedString,
|
||||||
|
textColor: UIColor.label,
|
||||||
|
font: UIFont.preferredFont(forTextStyle: .title2),
|
||||||
|
fixedWidth: selectedTextWidth,
|
||||||
|
height: $selectedTextHeight
|
||||||
|
)
|
||||||
|
.onAppear {
|
||||||
|
self.selectedTextWidth = geo.size.width
|
||||||
|
}
|
||||||
|
.onChange(of: geo.size) { newSize in
|
||||||
|
self.selectedTextWidth = newSize.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: selectedTextHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate struct TextViewRepresentable: UIViewRepresentable {
|
||||||
|
|
||||||
|
let attributedString: AttributedString
|
||||||
|
let textColor: UIColor
|
||||||
|
let font: UIFont
|
||||||
|
let fixedWidth: CGFloat
|
||||||
|
|
||||||
|
@Binding var height: CGFloat
|
||||||
|
|
||||||
|
func makeUIView(context: UIViewRepresentableContext<Self>) -> UITextView {
|
||||||
|
let view = UITextView()
|
||||||
|
view.isEditable = false
|
||||||
|
view.dataDetectorTypes = .all
|
||||||
|
view.isSelectable = true
|
||||||
|
view.textContainer.lineFragmentPadding = 0
|
||||||
|
view.textContainerInset = .zero
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<Self>) {
|
||||||
|
let mutableAttributedString = createNSAttributedString()
|
||||||
|
uiView.attributedText = mutableAttributedString
|
||||||
|
|
||||||
|
let newHeight = mutableAttributedString.height(containerWidth: fixedWidth)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
height = newHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNSAttributedString() -> NSMutableAttributedString {
|
||||||
|
let mutableAttributedString = NSMutableAttributedString(attributedString)
|
||||||
|
let myAttribute = [
|
||||||
|
NSAttributedString.Key.font: font,
|
||||||
|
NSAttributedString.Key.foregroundColor: textColor
|
||||||
|
]
|
||||||
|
|
||||||
|
mutableAttributedString.addAttributes(
|
||||||
|
myAttribute,
|
||||||
|
range: NSRange.init(location: 0, length: mutableAttributedString.length)
|
||||||
|
)
|
||||||
|
|
||||||
|
return mutableAttributedString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension NSAttributedString {
|
||||||
|
|
||||||
|
func height(containerWidth: CGFloat) -> CGFloat {
|
||||||
|
|
||||||
|
let rect = self.boundingRect(
|
||||||
|
with: CGSize.init(width: containerWidth, height: CGFloat.greatestFiniteMagnitude),
|
||||||
|
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
||||||
|
context: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
return ceil(rect.size.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
//
|
||||||
|
// TranslateButton.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-02-02.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import NaturalLanguage
|
||||||
|
|
||||||
|
struct TranslateView: View {
|
||||||
|
let damus_state: DamusState
|
||||||
|
let event: NostrEvent
|
||||||
|
|
||||||
|
@State var checkingTranslationStatus: Bool = false
|
||||||
|
@State var currentLanguage: String = "en"
|
||||||
|
@State var noteLanguage: String? = nil
|
||||||
|
@State var translated_note: String? = nil
|
||||||
|
@State var show_translated_note: Bool = false
|
||||||
|
@State var translated_artifacts: NoteArtifacts? = nil
|
||||||
|
|
||||||
|
var TranslateButton: some View {
|
||||||
|
Button(NSLocalizedString("Translate Note", comment: "Button to translate note from different language.")) {
|
||||||
|
show_translated_note = true
|
||||||
|
}
|
||||||
|
.translate_button_style()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Translated(lang: String, artifacts: NoteArtifacts) -> some View {
|
||||||
|
return Group {
|
||||||
|
Button(NSLocalizedString("Translated from \(lang)", comment: "Button to indicate that the note has been translated from a different language.")) {
|
||||||
|
show_translated_note = false
|
||||||
|
}
|
||||||
|
.translate_button_style()
|
||||||
|
|
||||||
|
SelectableText(attributedString: artifacts.content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckingStatus(lang: String) -> some View {
|
||||||
|
return Button(NSLocalizedString("Translating from \(lang)...", comment: "Button to indicate that the note is in the process of being translated from a different language.")) {
|
||||||
|
show_translated_note = false
|
||||||
|
}
|
||||||
|
.translate_button_style()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MainContent(note_lang: String) -> some View {
|
||||||
|
return Group {
|
||||||
|
let languageName = Locale.current.localizedString(forLanguageCode: note_lang)
|
||||||
|
if let lang = languageName, show_translated_note {
|
||||||
|
if checkingTranslationStatus {
|
||||||
|
CheckingStatus(lang: lang)
|
||||||
|
} else if let artifacts = translated_artifacts {
|
||||||
|
Translated(lang: lang, artifacts: artifacts)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TranslateButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if let note_lang = noteLanguage, noteLanguage != currentLanguage {
|
||||||
|
MainContent(note_lang: note_lang)
|
||||||
|
} else {
|
||||||
|
Text("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.task {
|
||||||
|
guard noteLanguage == nil && !checkingTranslationStatus && damus_state.settings.can_translate(damus_state.pubkey) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkingTranslationStatus = true
|
||||||
|
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
currentLanguage = Locale.current.language.languageCode?.identifier ?? "en"
|
||||||
|
} else {
|
||||||
|
currentLanguage = Locale.current.languageCode ?? "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rely on Apple's NLLanguageRecognizer to tell us which language it thinks the note is in
|
||||||
|
// and filter on only the text portions of the content as URLs and hashtags confuse the language recognizer.
|
||||||
|
let originalBlocks = event.blocks(damus_state.keypair.privkey)
|
||||||
|
let originalOnlyText = originalBlocks.compactMap { $0.is_text }.joined(separator: " ")
|
||||||
|
|
||||||
|
// Only accept language recognition hypothesis if there's at least a 50% probability that it's accurate.
|
||||||
|
let languageRecognizer = NLLanguageRecognizer()
|
||||||
|
languageRecognizer.processString(originalOnlyText)
|
||||||
|
noteLanguage = languageRecognizer.languageHypotheses(withMaximum: 1).first(where: { $0.value >= 0.5 })?.key.rawValue ?? currentLanguage
|
||||||
|
|
||||||
|
if let lang = noteLanguage, noteLanguage != currentLanguage {
|
||||||
|
// If the detected dominant language is a variant, remove the variant component and just take the language part as translation services typically only supports the variant-less language.
|
||||||
|
if #available(iOS 16, *) {
|
||||||
|
noteLanguage = Locale.LanguageCode(stringLiteral: lang).identifier(.alpha2)
|
||||||
|
} else {
|
||||||
|
noteLanguage = NSLocale(localeIdentifier: lang).languageCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let note_lang = noteLanguage else {
|
||||||
|
noteLanguage = currentLanguage
|
||||||
|
translated_note = nil
|
||||||
|
checkingTranslationStatus = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if note_lang != currentLanguage {
|
||||||
|
do {
|
||||||
|
// If the note language is different from our language, send a translation request.
|
||||||
|
let translator = Translator(damus_state.settings)
|
||||||
|
let originalContent = event.get_content(damus_state.keypair.privkey)
|
||||||
|
translated_note = try await translator.translate(originalContent, from: note_lang, to: currentLanguage)
|
||||||
|
|
||||||
|
if originalContent == translated_note {
|
||||||
|
// If the translation is the same as the original, don't bother showing it.
|
||||||
|
noteLanguage = currentLanguage
|
||||||
|
translated_note = nil
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If for whatever reason we're not able to figure out the language of the note, or translate the note, fail gracefully and do not retry. It's not the end of the world. Don't want to take down someone's translation server with an accidental denial of service attack.
|
||||||
|
noteLanguage = currentLanguage
|
||||||
|
translated_note = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let translated = translated_note {
|
||||||
|
// Render translated note.
|
||||||
|
let translatedBlocks = event.get_blocks(content: translated)
|
||||||
|
translated_artifacts = render_blocks(blocks: translatedBlocks, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkingTranslationStatus = false
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TranslateView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let ds = test_damus_state()
|
||||||
|
TranslateView(damus_state: ds, event: test_event)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
//
|
||||||
|
// ZapButton.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-01-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ZapButton: View {
|
||||||
|
let damus_state: DamusState
|
||||||
|
let event: NostrEvent
|
||||||
|
let lnurl: String
|
||||||
|
|
||||||
|
@ObservedObject var bar: ActionBarModel
|
||||||
|
|
||||||
|
@State var zapping: Bool = false
|
||||||
|
@State var invoice: String = ""
|
||||||
|
@State var slider_value: Double = 0.0
|
||||||
|
@State var slider_visible: Bool = false
|
||||||
|
@State var showing_select_wallet: Bool = false
|
||||||
|
|
||||||
|
func send_zap() {
|
||||||
|
guard let privkey = damus_state.keypair.privkey else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only take the first 10 because reasons
|
||||||
|
let relays = Array(damus_state.pool.descriptors.prefix(10))
|
||||||
|
let target = ZapTarget.note(id: event.id, author: event.pubkey)
|
||||||
|
// TODO: gather comment?
|
||||||
|
let content = ""
|
||||||
|
let zapreq = make_zap_request_event(pubkey: damus_state.pubkey, privkey: privkey, content: content, relays: relays, target: target)
|
||||||
|
|
||||||
|
zapping = true
|
||||||
|
|
||||||
|
Task {
|
||||||
|
var mpayreq = damus_state.lnurls.lookup(target.pubkey)
|
||||||
|
if mpayreq == nil {
|
||||||
|
mpayreq = await fetch_static_payreq(lnurl)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let payreq = mpayreq else {
|
||||||
|
// TODO: show error
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
zapping = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
damus_state.lnurls.endpoints[target.pubkey] = payreq
|
||||||
|
}
|
||||||
|
|
||||||
|
let zap_amount = get_default_zap_amount(pubkey: damus_state.pubkey) ?? 1000
|
||||||
|
guard let inv = await fetch_zap_invoice(payreq, zapreq: zapreq, sats: zap_amount) else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
zapping = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
zapping = false
|
||||||
|
|
||||||
|
if should_show_wallet_selector(damus_state.pubkey) {
|
||||||
|
self.invoice = inv
|
||||||
|
self.showing_select_wallet = true
|
||||||
|
} else {
|
||||||
|
open_with_wallet(wallet: get_default_wallet(damus_state.pubkey).model, invoice: inv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//damus_state.pool.send(.event(zapreq))
|
||||||
|
}
|
||||||
|
|
||||||
|
var zap_img: String {
|
||||||
|
if bar.zapped {
|
||||||
|
return "bolt.fill"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !zapping {
|
||||||
|
return "bolt"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "bolt.horizontal.fill"
|
||||||
|
}
|
||||||
|
|
||||||
|
var zap_color: Color? {
|
||||||
|
if bar.zapped {
|
||||||
|
return Color.orange
|
||||||
|
}
|
||||||
|
|
||||||
|
if !zapping {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return Color.yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
EventActionButton(img: zap_img, col: zap_color) {
|
||||||
|
if bar.zapped {
|
||||||
|
//notify(.delete, bar.our_tip)
|
||||||
|
} else if !zapping {
|
||||||
|
send_zap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.accessibilityLabel(NSLocalizedString("Zap", comment: "Accessibility label for zap button"))
|
||||||
|
|
||||||
|
if bar.zap_total > 0 {
|
||||||
|
Text(verbatim: format_msats_abbrev(bar.zap_total))
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundColor(bar.zapped ? Color.orange : Color.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
|
||||||
|
SelectWalletView(showingSelectWallet: $showing_select_wallet, our_pubkey: damus_state.pubkey, invoice: invoice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct ZapButton_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let bar = ActionBarModel(likes: 0, boosts: 0, zaps: 10, zap_total: 15623414, our_like: nil, our_boost: nil, our_zap: nil)
|
||||||
|
ZapButton(damus_state: test_damus_state(), event: test_event, lnurl: "lnurl", bar: bar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,15 +7,14 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Starscream
|
import Starscream
|
||||||
import Kingfisher
|
|
||||||
|
|
||||||
var BOOTSTRAP_RELAYS = [
|
var BOOTSTRAP_RELAYS = [
|
||||||
"wss://relay.damus.io",
|
"wss://relay.damus.io",
|
||||||
"wss://eden.nostr.land",
|
"wss://eden.nostr.land",
|
||||||
"wss://nostr.fmt.wiz.biz",
|
|
||||||
"wss://relay.nostr.bg",
|
|
||||||
"wss://nostr.oxtr.dev",
|
|
||||||
"wss://relay.snort.social",
|
"wss://relay.snort.social",
|
||||||
|
"wss://offchain.pub",
|
||||||
|
"wss://nos.lol",
|
||||||
|
"wss://relay.current.fyi",
|
||||||
"wss://brb.io",
|
"wss://brb.io",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -28,12 +27,16 @@ enum Sheets: Identifiable {
|
|||||||
case post
|
case post
|
||||||
case report(ReportTarget)
|
case report(ReportTarget)
|
||||||
case reply(NostrEvent)
|
case reply(NostrEvent)
|
||||||
|
case event(NostrEvent)
|
||||||
|
case filter
|
||||||
|
|
||||||
var id: String {
|
var id: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .report: return "report"
|
case .report: return "report"
|
||||||
case .post: return "post"
|
case .post: return "post"
|
||||||
case .reply(let ev): return "reply-" + ev.id
|
case .reply(let ev): return "reply-" + ev.id
|
||||||
|
case .event(let ev): return "event-" + ev.id
|
||||||
|
case .filter: return "filter"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,6 +76,7 @@ struct ContentView: View {
|
|||||||
@State var damus_state: DamusState? = nil
|
@State var damus_state: DamusState? = nil
|
||||||
@State var selected_timeline: Timeline? = .home
|
@State var selected_timeline: Timeline? = .home
|
||||||
@State var is_thread_open: Bool = false
|
@State var is_thread_open: Bool = false
|
||||||
|
@State var is_deleted_account: Bool = false
|
||||||
@State var is_profile_open: Bool = false
|
@State var is_profile_open: Bool = false
|
||||||
@State var event: NostrEvent? = nil
|
@State var event: NostrEvent? = nil
|
||||||
@State var active_profile: String? = nil
|
@State var active_profile: String? = nil
|
||||||
@@ -88,8 +92,7 @@ struct ContentView: View {
|
|||||||
@State var filter_state : FilterState = .posts_and_replies
|
@State var filter_state : FilterState = .posts_and_replies
|
||||||
@State private var isSideBarOpened = false
|
@State private var isSideBarOpened = false
|
||||||
@StateObject var home: HomeModel = HomeModel()
|
@StateObject var home: HomeModel = HomeModel()
|
||||||
@StateObject var user_settings = UserSettingsStore()
|
|
||||||
|
|
||||||
// connect retry timer
|
// connect retry timer
|
||||||
let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()
|
let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()
|
||||||
|
|
||||||
@@ -111,7 +114,7 @@ struct ContentView: View {
|
|||||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||||
|
|
||||||
if privkey != nil {
|
if privkey != nil {
|
||||||
PostButtonContainer(userSettings: user_settings) {
|
PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) {
|
||||||
self.active_sheet = .post
|
self.active_sheet = .post
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,9 +122,10 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.safeAreaInset(edge: .top, spacing: 0) {
|
.safeAreaInset(edge: .top, spacing: 0) {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
FiltersView
|
CustomPicker(selection: $filter_state, content: {
|
||||||
//.frame(maxWidth: 275)
|
Text("Posts", comment: "Label for filter for seeing only posts (instead of posts and replies).").tag(FilterState.posts)
|
||||||
.padding()
|
Text("Posts & Replies", comment: "Label for filter for seeing posts and replies (instead of only posts).").tag(FilterState.posts_and_replies)
|
||||||
|
})
|
||||||
Divider()
|
Divider()
|
||||||
.frame(height: 1)
|
.frame(height: 1)
|
||||||
}
|
}
|
||||||
@@ -132,18 +136,38 @@ struct ContentView: View {
|
|||||||
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let damus = self.damus_state {
|
if let damus = self.damus_state {
|
||||||
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter)
|
TimelineView(events: home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var FiltersView: some View {
|
func popToRoot() {
|
||||||
VStack{
|
profile_open = false
|
||||||
Picker(NSLocalizedString("Filter State", comment: "Filter state for seeing either only posts, or posts & replies."), selection: $filter_state) {
|
thread_open = false
|
||||||
Text("Posts", comment: "Label for filter for seeing only posts (instead of posts and replies).").tag(FilterState.posts)
|
search_open = false
|
||||||
Text("Posts & Replies", comment: "Label for filter for seeing posts and replies (instead of only posts).").tag(FilterState.posts_and_replies)
|
isSideBarOpened = false
|
||||||
|
}
|
||||||
|
|
||||||
|
var timelineNavItem: some View {
|
||||||
|
VStack {
|
||||||
|
switch selected_timeline {
|
||||||
|
case .home:
|
||||||
|
Image("damus-home")
|
||||||
|
.resizable()
|
||||||
|
.frame(width:30,height:30)
|
||||||
|
.shadow(color: Color("DamusPurple"), radius: 2)
|
||||||
|
case .dms:
|
||||||
|
Text("DMs", comment: "Toolbar label for DMs view, where DM is the English abbreviation for Direct Message.")
|
||||||
|
.bold()
|
||||||
|
case .notifications:
|
||||||
|
Text("Notifications", comment: "Toolbar label for Notifications view.")
|
||||||
|
.bold()
|
||||||
|
case .search:
|
||||||
|
Text("Universe 🛸", comment: "Toolbar label for the universal view where posts from all connected relay servers appear.")
|
||||||
|
.bold()
|
||||||
|
case .none:
|
||||||
|
Text(verbatim: "")
|
||||||
}
|
}
|
||||||
.pickerStyle(.segmented)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,9 +190,10 @@ struct ContentView: View {
|
|||||||
PostingTimelineView
|
PostingTimelineView
|
||||||
|
|
||||||
case .notifications:
|
case .notifications:
|
||||||
TimelineView(events: $home.notifications, loading: $home.loading, damus: damus, show_friend_icon: true, filter: { _ in true })
|
VStack(spacing: 0) {
|
||||||
.navigationTitle(NSLocalizedString("Notifications", comment: "Navigation title for notifications."))
|
Divider()
|
||||||
|
TimelineView(events: home.notifications, loading: $home.loading, damus: damus, show_friend_icon: true, filter: { _ in true })
|
||||||
|
}
|
||||||
case .dms:
|
case .dms:
|
||||||
DirectMessagesView(damus_state: damus_state!)
|
DirectMessagesView(damus_state: damus_state!)
|
||||||
.environmentObject(home.dms)
|
.environmentObject(home.dms)
|
||||||
@@ -177,27 +202,12 @@ struct ContentView: View {
|
|||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationBarTitle(selected_timeline == .home ? NSLocalizedString("Home", comment: "Navigation bar title for Home view where posts and replies appear from those who the user is following.") : NSLocalizedString("Global", comment: "Navigation bar title for Global view where posts from all connected relay servers appear."), displayMode: .inline)
|
.navigationBarTitle(selected_timeline == .home ? NSLocalizedString("Home", comment: "Navigation bar title for Home view where posts and replies appear from those who the user is following.") : NSLocalizedString("Universe 🛸", comment: "Navigation bar title for universal view where posts from all connected relay servers appear."), displayMode: .inline)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .principal) {
|
ToolbarItem(placement: .principal) {
|
||||||
switch selected_timeline {
|
timelineNavItem
|
||||||
case .home:
|
.opacity(isSideBarOpened ? 0 : 1)
|
||||||
Image("damus-home")
|
.animation(isSideBarOpened ? .none : .default, value: isSideBarOpened)
|
||||||
.resizable()
|
|
||||||
.frame(width:30,height:30)
|
|
||||||
.shadow(color: Color("DamusPurple"), radius: 2)
|
|
||||||
case .dms:
|
|
||||||
Text("DMs", comment: "Toolbar label for DMs view, where DM is the English abbreviation for Direct Message.")
|
|
||||||
.bold()
|
|
||||||
case .notifications:
|
|
||||||
Text("Notifications", comment: "Toolbar label for Notifications view.")
|
|
||||||
.bold()
|
|
||||||
case .search:
|
|
||||||
Text("Global", comment: "Toolbar label for Global view where posts from all connected relay servers appear.")
|
|
||||||
.bold()
|
|
||||||
case .none:
|
|
||||||
Text("", comment: "Toolbar label for unknown views. This label would be displayed only if a new timeline view is added but a toolbar label was not explicitly assigned to it yet.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(.keyboard)
|
.ignoresSafeArea(.keyboard)
|
||||||
@@ -206,7 +216,7 @@ struct ContentView: View {
|
|||||||
var MaybeSearchView: some View {
|
var MaybeSearchView: some View {
|
||||||
Group {
|
Group {
|
||||||
if let search = self.active_search {
|
if let search = self.active_search {
|
||||||
SearchView(appstate: damus_state!, search: SearchModel(pool: damus_state!.pool, search: search))
|
SearchView(appstate: damus_state!, search: SearchModel(contacts: damus_state!.contacts, pool: damus_state!.pool, search: search))
|
||||||
} else {
|
} else {
|
||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
@@ -254,7 +264,7 @@ struct ContentView: View {
|
|||||||
if let damus = self.damus_state {
|
if let damus = self.damus_state {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
ZStack {
|
ZStack {
|
||||||
VStack {
|
TabView { // Prevents navbar appearance change on scroll
|
||||||
MainContent(damus: damus)
|
MainContent(damus: damus)
|
||||||
.toolbar() {
|
.toolbar() {
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
@@ -262,29 +272,43 @@ struct ContentView: View {
|
|||||||
isSideBarOpened.toggle()
|
isSideBarOpened.toggle()
|
||||||
} label: {
|
} label: {
|
||||||
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles)
|
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles)
|
||||||
|
.opacity(isSideBarOpened ? 0 : 1)
|
||||||
|
.animation(isSideBarOpened ? .none : .default, value: isSideBarOpened)
|
||||||
}
|
}
|
||||||
|
.disabled(isSideBarOpened)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
if home.signal.signal != home.signal.max_signal {
|
if home.signal.signal != home.signal.max_signal {
|
||||||
Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
|
NavigationLink(destination: RelayConfigView(state: damus_state!)) {
|
||||||
.font(.callout)
|
Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
|
||||||
.foregroundColor(.gray)
|
.font(.callout)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe expand this to other timelines in the future
|
||||||
|
if selected_timeline == .search {
|
||||||
|
Button(action: {
|
||||||
|
//isFilterVisible.toggle()
|
||||||
|
self.active_sheet = .filter
|
||||||
|
}) {
|
||||||
|
// checklist, checklist.checked, lisdt.bullet, list.bullet.circle, line.3.horizontal.decrease..., line.3.horizontail.decrease
|
||||||
|
Label(NSLocalizedString("Filter", comment: "Button label text for filtering relay servers."), systemImage: "line.3.horizontal.decrease")
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
//.contentShape(Rectangle())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||||
Color.clear
|
|
||||||
.overlay(
|
|
||||||
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.navigationBarHidden(isSideBarOpened ? true: false) // Would prefer a different way of doing this.
|
.overlay(
|
||||||
|
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.navigationViewStyle(.stack)
|
.navigationViewStyle(.stack)
|
||||||
|
|
||||||
@@ -294,7 +318,6 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.onAppear() {
|
.onAppear() {
|
||||||
self.connect()
|
self.connect()
|
||||||
//KingfisherManager.shared.cache.clearDiskCache()
|
|
||||||
setup_notifications()
|
setup_notifications()
|
||||||
}
|
}
|
||||||
.sheet(item: $active_sheet) { item in
|
.sheet(item: $active_sheet) { item in
|
||||||
@@ -302,9 +325,20 @@ struct ContentView: View {
|
|||||||
case .report(let target):
|
case .report(let target):
|
||||||
MaybeReportView(target: target)
|
MaybeReportView(target: target)
|
||||||
case .post:
|
case .post:
|
||||||
PostView(replying_to: nil, references: [])
|
PostView(replying_to: nil, references: [], damus_state: damus_state!)
|
||||||
case .reply(let event):
|
case .reply(let event):
|
||||||
ReplyView(replying_to: event, damus: damus_state!)
|
ReplyView(replying_to: event, damus: damus_state!)
|
||||||
|
case .event:
|
||||||
|
EventDetailView()
|
||||||
|
case .filter:
|
||||||
|
let timeline = selected_timeline ?? .home
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
RelayFilterView(state: damus_state!, timeline: timeline)
|
||||||
|
.presentationDetents([.height(550)])
|
||||||
|
.presentationDragIndicator(.visible)
|
||||||
|
} else {
|
||||||
|
RelayFilterView(state: damus_state!, timeline: timeline)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onOpenURL { url in
|
.onOpenURL { url in
|
||||||
@@ -348,6 +382,9 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
.onReceive(handle_notify(.like)) { like in
|
.onReceive(handle_notify(.like)) { like in
|
||||||
}
|
}
|
||||||
|
.onReceive(handle_notify(.deleted_account)) { notif in
|
||||||
|
self.is_deleted_account = true
|
||||||
|
}
|
||||||
.onReceive(handle_notify(.report)) { notif in
|
.onReceive(handle_notify(.report)) { notif in
|
||||||
let target = notif.object as! ReportTarget
|
let target = notif.object as! ReportTarget
|
||||||
self.active_sheet = .report(target)
|
self.active_sheet = .report(target)
|
||||||
@@ -420,6 +457,8 @@ struct ContentView: View {
|
|||||||
let post_res = obj.object as! NostrPostResult
|
let post_res = obj.object as! NostrPostResult
|
||||||
switch post_res {
|
switch post_res {
|
||||||
case .post(let post):
|
case .post(let post):
|
||||||
|
//let post = tup.0
|
||||||
|
//let to_relays = tup.1
|
||||||
print("post \(post.content)")
|
print("post \(post.content)")
|
||||||
let new_ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey)
|
let new_ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey)
|
||||||
self.damus_state?.pool.send(.event(new_ev))
|
self.damus_state?.pool.send(.event(new_ev))
|
||||||
@@ -434,7 +473,13 @@ struct ContentView: View {
|
|||||||
.onReceive(handle_notify(.new_mutes)) { notif in
|
.onReceive(handle_notify(.new_mutes)) { notif in
|
||||||
home.filter_muted()
|
home.filter_muted()
|
||||||
}
|
}
|
||||||
.alert(NSLocalizedString("User blocked", comment: "Alert message to indicate "), isPresented: $user_blocked_confirm, actions: {
|
.alert(NSLocalizedString("Deleted Account", comment: "Alert message to indicate this is a deleted account"), isPresented: $is_deleted_account) {
|
||||||
|
Button(NSLocalizedString("Logout", comment: "Button to close the alert that informs that the current account has been deleted.")) {
|
||||||
|
is_deleted_account = false
|
||||||
|
notify(.logout, ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert(NSLocalizedString("User blocked", comment: "Alert message to indicate the user has been blocked"), isPresented: $user_blocked_confirm, actions: {
|
||||||
Button(NSLocalizedString("Thanks!", comment: "Button to close out of alert that informs that the action to block a user was successful.")) {
|
Button(NSLocalizedString("Thanks!", comment: "Button to close out of alert that informs that the action to block a user was successful.")) {
|
||||||
user_blocked_confirm = false
|
user_blocked_confirm = false
|
||||||
}
|
}
|
||||||
@@ -518,6 +563,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func switch_timeline(_ timeline: Timeline) {
|
func switch_timeline(_ timeline: Timeline) {
|
||||||
|
self.popToRoot()
|
||||||
NotificationCenter.default.post(name: .switched_timeline, object: timeline)
|
NotificationCenter.default.post(name: .switched_timeline, object: timeline)
|
||||||
|
|
||||||
if timeline == self.selected_timeline {
|
if timeline == self.selected_timeline {
|
||||||
@@ -543,21 +589,33 @@ struct ContentView: View {
|
|||||||
|
|
||||||
func connect() {
|
func connect() {
|
||||||
let pool = RelayPool()
|
let pool = RelayPool()
|
||||||
|
let metadatas = RelayMetadatas()
|
||||||
|
let relay_filters = RelayFilters(our_pubkey: pubkey)
|
||||||
|
|
||||||
|
let new_relay_filters = load_relay_filters(pubkey) == nil
|
||||||
for relay in BOOTSTRAP_RELAYS {
|
for relay in BOOTSTRAP_RELAYS {
|
||||||
add_relay(pool, relay)
|
if let url = URL(string: relay) {
|
||||||
|
add_new_relay(relay_filters: relay_filters, metadatas: metadatas, pool: pool, url: url, info: .rw, new_relay_filters: new_relay_filters)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.register_handler(sub_id: sub_id, handler: home.handle_event)
|
pool.register_handler(sub_id: sub_id, handler: home.handle_event)
|
||||||
|
|
||||||
self.damus_state = DamusState(pool: pool, keypair: keypair,
|
self.damus_state = DamusState(pool: pool,
|
||||||
likes: EventCounter(our_pubkey: pubkey),
|
keypair: keypair,
|
||||||
boosts: EventCounter(our_pubkey: pubkey),
|
likes: EventCounter(our_pubkey: pubkey),
|
||||||
contacts: Contacts(our_pubkey: pubkey),
|
boosts: EventCounter(our_pubkey: pubkey),
|
||||||
tips: TipCounter(our_pubkey: pubkey),
|
contacts: Contacts(our_pubkey: pubkey),
|
||||||
profiles: Profiles(),
|
tips: TipCounter(our_pubkey: pubkey),
|
||||||
dms: home.dms,
|
profiles: Profiles(),
|
||||||
previews: PreviewCache()
|
dms: home.dms,
|
||||||
|
previews: PreviewCache(),
|
||||||
|
zaps: Zaps(our_pubkey: pubkey),
|
||||||
|
lnurls: LNUrls(),
|
||||||
|
settings: UserSettingsStore(),
|
||||||
|
relay_filters: relay_filters,
|
||||||
|
relay_metadata: metadatas,
|
||||||
|
drafts: Drafts()
|
||||||
)
|
)
|
||||||
home.damus_state = self.damus_state!
|
home.damus_state = self.damus_state!
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
<string>zeusln</string>
|
<string>zeusln</string>
|
||||||
<string>zebedee</string>
|
<string>zebedee</string>
|
||||||
<string>lightning</string>
|
<string>lightning</string>
|
||||||
<string>squarecash</string>
|
|
||||||
<string>phoenix</string>
|
<string>phoenix</string>
|
||||||
<string>lnlink</string>
|
<string>lnlink</string>
|
||||||
<string>strike</string>
|
<string>strike</string>
|
||||||
|
|||||||
@@ -11,30 +11,43 @@ import Foundation
|
|||||||
class ActionBarModel: ObservableObject {
|
class ActionBarModel: ObservableObject {
|
||||||
@Published var our_like: NostrEvent?
|
@Published var our_like: NostrEvent?
|
||||||
@Published var our_boost: NostrEvent?
|
@Published var our_boost: NostrEvent?
|
||||||
@Published var our_tip: NostrEvent?
|
@Published var our_zap: Zap?
|
||||||
@Published var likes: Int
|
@Published var likes: Int
|
||||||
@Published var boosts: Int
|
@Published var boosts: Int
|
||||||
@Published var tips: Int64
|
@Published var zaps: Int
|
||||||
|
@Published var zap_total: Int64
|
||||||
|
|
||||||
static func empty() -> ActionBarModel {
|
static func empty() -> ActionBarModel {
|
||||||
return ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
|
return ActionBarModel(likes: 0, boosts: 0, zaps: 0, zap_total: 0, our_like: nil, our_boost: nil, our_zap: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(likes: Int, boosts: Int, tips: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_tip: NostrEvent?) {
|
init(likes: Int, boosts: Int, zaps: Int, zap_total: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_zap: Zap?) {
|
||||||
self.likes = likes
|
self.likes = likes
|
||||||
self.boosts = boosts
|
self.boosts = boosts
|
||||||
self.tips = tips
|
self.zaps = zaps
|
||||||
|
self.zap_total = zap_total
|
||||||
self.our_like = our_like
|
self.our_like = our_like
|
||||||
self.our_boost = our_boost
|
self.our_boost = our_boost
|
||||||
self.our_tip = our_tip
|
self.our_zap = our_zap
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(damus: DamusState, evid: String) {
|
||||||
|
self.likes = damus.likes.counts[evid] ?? 0
|
||||||
|
self.boosts = damus.boosts.counts[evid] ?? 0
|
||||||
|
self.zaps = damus.zaps.event_counts[evid] ?? 0
|
||||||
|
self.zap_total = damus.zaps.event_totals[evid] ?? 0
|
||||||
|
self.our_like = damus.likes.our_events[evid]
|
||||||
|
self.our_boost = damus.boosts.our_events[evid]
|
||||||
|
self.our_zap = damus.zaps.our_zaps[evid]?.first
|
||||||
|
self.objectWillChange.send()
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_empty: Bool {
|
var is_empty: Bool {
|
||||||
return likes == 0 && boosts == 0 && tips == 0
|
return likes == 0 && boosts == 0 && zaps == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var tipped: Bool {
|
var zapped: Bool {
|
||||||
return our_tip != nil
|
return our_zap != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var liked: Bool {
|
var liked: Bool {
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// BookmarksManager.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Joel Klabo on 2/18/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class BookmarksManager {
|
||||||
|
|
||||||
|
private let userDefaults = UserDefaults.standard
|
||||||
|
private let pubkey: String
|
||||||
|
|
||||||
|
init(pubkey: String) {
|
||||||
|
self.pubkey = pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
var bookmarks: [String] {
|
||||||
|
get {
|
||||||
|
return userDefaults.stringArray(forKey: storageKey()) ?? []
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
let uniqueBookmarks = Array(Set(newValue))
|
||||||
|
if uniqueBookmarks != bookmarks {
|
||||||
|
userDefaults.set(uniqueBookmarks, forKey: storageKey())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBookmarked(_ string: String) -> Bool {
|
||||||
|
return bookmarks.contains(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateBookmark(_ string: String) {
|
||||||
|
if isBookmarked(string) {
|
||||||
|
bookmarks = bookmarks.filter { $0 != string }
|
||||||
|
} else {
|
||||||
|
bookmarks.append(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearAll() {
|
||||||
|
bookmarks = []
|
||||||
|
}
|
||||||
|
|
||||||
|
private func storageKey() -> String {
|
||||||
|
pk_setting_key(pubkey, key: "bookmarks")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,17 +18,23 @@ struct DamusState {
|
|||||||
let profiles: Profiles
|
let profiles: Profiles
|
||||||
let dms: DirectMessagesModel
|
let dms: DirectMessagesModel
|
||||||
let previews: PreviewCache
|
let previews: PreviewCache
|
||||||
|
let zaps: Zaps
|
||||||
|
let lnurls: LNUrls
|
||||||
|
let settings: UserSettingsStore
|
||||||
|
let relay_filters: RelayFilters
|
||||||
|
let relay_metadata: RelayMetadatas
|
||||||
|
let drafts: Drafts
|
||||||
|
|
||||||
var pubkey: String {
|
var pubkey: String {
|
||||||
return keypair.pubkey
|
return keypair.pubkey
|
||||||
}
|
}
|
||||||
|
|
||||||
var is_privkey_user: Bool {
|
var is_privkey_user: Bool {
|
||||||
keypair.privkey != nil
|
keypair.privkey != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static var empty: DamusState {
|
static var empty: DamusState {
|
||||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache())
|
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""), relay_metadata: RelayMetadatas(), drafts: Drafts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// DeepLPlan.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Terry Yiu on 2/3/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum DeepLPlan: String, CaseIterable, Identifiable {
|
||||||
|
var id: String { self.rawValue }
|
||||||
|
|
||||||
|
struct Model: Identifiable, Hashable {
|
||||||
|
var id: String { self.tag }
|
||||||
|
var tag: String
|
||||||
|
var displayName: String
|
||||||
|
var url: String
|
||||||
|
}
|
||||||
|
|
||||||
|
case free
|
||||||
|
case pro
|
||||||
|
|
||||||
|
var model: Model {
|
||||||
|
switch self {
|
||||||
|
case .free:
|
||||||
|
return .init(tag: self.rawValue, displayName: NSLocalizedString("Free", comment: "Dropdown option for selecting Free plan for DeepL translation service."), url: "https://api-free.deepl.com")
|
||||||
|
case .pro:
|
||||||
|
return .init(tag: self.rawValue, displayName: NSLocalizedString("Pro", comment: "Dropdown option for selecting Pro plan for DeepL translation service."), url: "https://api.deepl.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var allModels: [Model] {
|
||||||
|
return Self.allCases.map { $0.model }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ class DirectMessageModel: ObservableObject {
|
|||||||
is_request = determine_is_request()
|
is_request = determine_is_request()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Published var draft: String
|
||||||
|
|
||||||
var is_request: Bool
|
var is_request: Bool
|
||||||
var our_pubkey: String
|
var our_pubkey: String
|
||||||
@@ -31,11 +33,13 @@ class DirectMessageModel: ObservableObject {
|
|||||||
self.events = events
|
self.events = events
|
||||||
self.is_request = false
|
self.is_request = false
|
||||||
self.our_pubkey = our_pubkey
|
self.our_pubkey = our_pubkey
|
||||||
|
self.draft = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
init(our_pubkey: String) {
|
init(our_pubkey: String) {
|
||||||
self.events = []
|
self.events = []
|
||||||
self.is_request = false
|
self.is_request = false
|
||||||
self.our_pubkey = our_pubkey
|
self.our_pubkey = our_pubkey
|
||||||
|
self.draft = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// DraftsModel.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Terry Yiu on 2/12/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class Drafts: ObservableObject {
|
||||||
|
@Published var post: String = ""
|
||||||
|
@Published var replies: [NostrEvent: String] = [:]
|
||||||
|
}
|
||||||
@@ -9,9 +9,63 @@ import Foundation
|
|||||||
|
|
||||||
|
|
||||||
class EventsModel: ObservableObject {
|
class EventsModel: ObservableObject {
|
||||||
var has_event: Set<String> = Set()
|
let state: DamusState
|
||||||
|
let target: String
|
||||||
|
let kind: NostrKind
|
||||||
|
let sub_id = UUID().uuidString
|
||||||
|
let profiles_id = UUID().uuidString
|
||||||
|
|
||||||
@Published var events: [NostrEvent] = []
|
@Published var events: [NostrEvent] = []
|
||||||
|
|
||||||
init() {
|
init(state: DamusState, target: String, kind: NostrKind) {
|
||||||
|
self.state = state
|
||||||
|
self.target = target
|
||||||
|
self.kind = kind
|
||||||
|
}
|
||||||
|
|
||||||
|
private func get_filter() -> NostrFilter {
|
||||||
|
var filter = NostrFilter.filter_kinds([kind.rawValue])
|
||||||
|
filter.referenced_ids = [target]
|
||||||
|
filter.limit = 500
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func subscribe() {
|
||||||
|
state.pool.subscribe(sub_id: sub_id,
|
||||||
|
filters: [get_filter()],
|
||||||
|
handler: handle_nostr_event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsubscribe() {
|
||||||
|
state.pool.unsubscribe(sub_id: sub_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handle_event(relay_id: String, ev: NostrEvent) {
|
||||||
|
guard ev.kind == kind.rawValue else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard last_etag(tags: ev.tags) == target else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { a, b in a.created_at < b.created_at } ) {
|
||||||
|
objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle_nostr_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||||
|
guard case .nostr_event(let nev) = ev else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch nev {
|
||||||
|
case .event(_, let ev):
|
||||||
|
handle_event(relay_id: relay_id, ev: ev)
|
||||||
|
case .notice(_):
|
||||||
|
break
|
||||||
|
case .eose(_):
|
||||||
|
load_profiles(profiles_subid: profiles_id, relay_id: relay_id, events: events, damus_state: state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,11 +51,7 @@ class FollowersModel: ObservableObject {
|
|||||||
if has_contact.contains(ev.pubkey) {
|
if has_contact.contains(ev.pubkey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
process_contact_event(
|
process_contact_event(state: damus_state, ev: ev)
|
||||||
pool: damus_state.pool,
|
|
||||||
contacts: damus_state.contacts,
|
|
||||||
pubkey: damus_state.pubkey, ev: ev
|
|
||||||
)
|
|
||||||
contacts?.append(ev.pubkey)
|
contacts?.append(ev.pubkey)
|
||||||
has_contact.insert(ev.pubkey)
|
has_contact.insert(ev.pubkey)
|
||||||
}
|
}
|
||||||
@@ -86,7 +82,7 @@ class FollowersModel: ObservableObject {
|
|||||||
if ev.known_kind == .contacts {
|
if ev.known_kind == .contacts {
|
||||||
handle_contact_event(ev)
|
handle_contact_event(ev)
|
||||||
} else if ev.known_kind == .metadata {
|
} else if ev.known_kind == .metadata {
|
||||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .notice(let msg):
|
case .notice(let msg):
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class FollowingModel {
|
|||||||
switch nev {
|
switch nev {
|
||||||
case .event(_, let ev):
|
case .event(_, let ev):
|
||||||
if ev.kind == 0 {
|
if ev.kind == 0 {
|
||||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
case .notice(let msg):
|
case .notice(let msg):
|
||||||
print("followingmodel notice: \(msg)")
|
print("followingmodel notice: \(msg)")
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ class HomeModel: ObservableObject {
|
|||||||
var channels: [String: NostrEvent] = [:]
|
var channels: [String: NostrEvent] = [:]
|
||||||
var last_event_of_kind: [String: [Int: NostrEvent]] = [:]
|
var last_event_of_kind: [String: [Int: NostrEvent]] = [:]
|
||||||
var done_init: Bool = false
|
var done_init: Bool = false
|
||||||
|
var incoming_dms: [NostrEvent] = []
|
||||||
|
let dm_debouncer = Debouncer(interval: 0.5)
|
||||||
|
var should_debounce_dms = true
|
||||||
|
|
||||||
let home_subid = UUID().description
|
let home_subid = UUID().description
|
||||||
let contacts_subid = UUID().description
|
let contacts_subid = UUID().description
|
||||||
@@ -47,25 +50,37 @@ class HomeModel: ObservableObject {
|
|||||||
let profiles_subid = UUID().description
|
let profiles_subid = UUID().description
|
||||||
|
|
||||||
@Published var new_events: NewEventsBits = NewEventsBits()
|
@Published var new_events: NewEventsBits = NewEventsBits()
|
||||||
@Published var notifications: [NostrEvent] = []
|
@Published var notifications: EventHolder
|
||||||
@Published var dms: DirectMessagesModel
|
@Published var dms: DirectMessagesModel
|
||||||
@Published var events: [NostrEvent] = []
|
@Published var events: EventHolder
|
||||||
@Published var loading: Bool = false
|
@Published var loading: Bool = false
|
||||||
@Published var signal: SignalModel = SignalModel()
|
@Published var signal: SignalModel = SignalModel()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
self.events = EventHolder()
|
||||||
|
self.notifications = EventHolder()
|
||||||
self.damus_state = DamusState.empty
|
self.damus_state = DamusState.empty
|
||||||
self.dms = DirectMessagesModel(our_pubkey: damus_state.pubkey)
|
self.dms = DirectMessagesModel(our_pubkey: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
init(damus_state: DamusState) {
|
init(damus_state: DamusState) {
|
||||||
|
self.events = EventHolder()
|
||||||
|
self.notifications = EventHolder()
|
||||||
self.damus_state = damus_state
|
self.damus_state = damus_state
|
||||||
self.dms = DirectMessagesModel(our_pubkey: damus_state.pubkey)
|
self.dms = DirectMessagesModel(our_pubkey: damus_state.pubkey)
|
||||||
|
self.setup_debouncer()
|
||||||
}
|
}
|
||||||
|
|
||||||
var pool: RelayPool {
|
var pool: RelayPool {
|
||||||
return damus_state.pool
|
return damus_state.pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setup_debouncer() {
|
||||||
|
// turn off debouncer after initial load
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
|
||||||
|
self.should_debounce_dms = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func has_sub_id_event(sub_id: String, ev_id: String) -> Bool {
|
func has_sub_id_event(sub_id: String, ev_id: String) -> Bool {
|
||||||
if !has_event.keys.contains(sub_id) {
|
if !has_event.keys.contains(sub_id) {
|
||||||
@@ -112,9 +127,62 @@ class HomeModel: ObservableObject {
|
|||||||
handle_channel_create(ev)
|
handle_channel_create(ev)
|
||||||
case .channel_meta:
|
case .channel_meta:
|
||||||
handle_channel_meta(ev)
|
handle_channel_meta(ev)
|
||||||
|
case .zap:
|
||||||
|
handle_zap_event(ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handle_zap_event_with_zapper(_ ev: NostrEvent, our_pubkey: String, zapper: String) {
|
||||||
|
guard let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
damus_state.zaps.add_zap(zap: zap)
|
||||||
|
|
||||||
|
guard zap.target.pubkey == our_pubkey else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !notifications.insert(ev) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_last_event(ev: ev, timeline: .notifications)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle_zap_event(_ ev: NostrEvent) {
|
||||||
|
// These are zap notifications
|
||||||
|
guard let ptag = event_tag(ev, name: "p") else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let local_zapper = damus_state.profiles.lookup_zapper(pubkey: ptag) {
|
||||||
|
handle_zap_event_with_zapper(ev, our_pubkey: damus_state.pubkey, zapper: local_zapper)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let profile = damus_state.profiles.lookup(id: ptag) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let lnurl = profile.lnurl else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
guard let zapper = await fetch_zapper_from_lnurl(lnurl) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.damus_state.profiles.zappers[ptag] = zapper
|
||||||
|
self.handle_zap_event_with_zapper(ev, our_pubkey: self.damus_state.pubkey, zapper: zapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func handle_channel_create(_ ev: NostrEvent) {
|
func handle_channel_create(_ ev: NostrEvent) {
|
||||||
guard ev.is_valid else {
|
guard ev.is_valid else {
|
||||||
return
|
return
|
||||||
@@ -127,9 +195,9 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func filter_muted() {
|
func filter_muted() {
|
||||||
self.events = events.filter { !damus_state.contacts.is_muted($0.pubkey) }
|
events.filter { !damus_state.contacts.is_muted($0.pubkey) }
|
||||||
self.dms.dms = dms.dms.filter { !damus_state.contacts.is_muted($0.0) }
|
self.dms.dms = dms.dms.filter { !damus_state.contacts.is_muted($0.0) }
|
||||||
self.notifications = notifications.filter { !damus_state.contacts.is_muted($0.pubkey) }
|
notifications.filter { !damus_state.contacts.is_muted($0.pubkey) }
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_delete_event(_ ev: NostrEvent) {
|
func handle_delete_event(_ ev: NostrEvent) {
|
||||||
@@ -141,7 +209,7 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handle_contact_event(sub_id: String, relay_id: String, ev: NostrEvent) {
|
func handle_contact_event(sub_id: String, relay_id: String, ev: NostrEvent) {
|
||||||
process_contact_event(pool: damus_state.pool, contacts: damus_state.contacts, pubkey: damus_state.pubkey, ev: ev)
|
process_contact_event(state: self.damus_state, ev: ev)
|
||||||
|
|
||||||
if sub_id == init_subid {
|
if sub_id == init_subid {
|
||||||
pool.send(.unsubscribe(init_subid), to: [relay_id])
|
pool.send(.unsubscribe(init_subid), to: [relay_id])
|
||||||
@@ -177,6 +245,7 @@ class HomeModel: ObservableObject {
|
|||||||
case .success(let n):
|
case .success(let n):
|
||||||
let boosted = Counted(event: ev, id: e, total: n)
|
let boosted = Counted(event: ev, id: e, total: n)
|
||||||
notify(.boosted, boosted)
|
notify(.boosted, boosted)
|
||||||
|
notify(.update_stats, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +263,7 @@ class HomeModel: ObservableObject {
|
|||||||
case .success(let n):
|
case .success(let n):
|
||||||
let liked = Counted(event: ev, id: e.ref_id, total: n)
|
let liked = Counted(event: ev, id: e.ref_id, total: n)
|
||||||
notify(.liked, liked)
|
notify(.liked, liked)
|
||||||
|
notify(.update_stats, e.ref_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +304,7 @@ class HomeModel: ObservableObject {
|
|||||||
switch ev {
|
switch ev {
|
||||||
case .event(let sub_id, let ev):
|
case .event(let sub_id, let ev):
|
||||||
// globally handle likes
|
// globally handle likes
|
||||||
let always_process = sub_id == notifications_subid || sub_id == contacts_subid || sub_id == home_subid || sub_id == dms_subid || sub_id == init_subid || ev.known_kind == .like || ev.known_kind == .contacts || ev.known_kind == .metadata
|
let always_process = sub_id == notifications_subid || sub_id == contacts_subid || sub_id == home_subid || sub_id == dms_subid || sub_id == init_subid || ev.known_kind == .like || ev.known_kind == .boost || ev.known_kind == .zap || ev.known_kind == .contacts || ev.known_kind == .metadata
|
||||||
if !always_process {
|
if !always_process {
|
||||||
// TODO: other views like threads might have their own sub ids, so ignore those events... or should we?
|
// TODO: other views like threads might have their own sub ids, so ignore those events... or should we?
|
||||||
return
|
return
|
||||||
@@ -248,10 +318,11 @@ class HomeModel: ObservableObject {
|
|||||||
case .eose(let sub_id):
|
case .eose(let sub_id):
|
||||||
|
|
||||||
if sub_id == dms_subid {
|
if sub_id == dms_subid {
|
||||||
let dms = dms.dms.flatMap { $0.1.events }
|
var dms = dms.dms.flatMap { $0.1.events }
|
||||||
|
dms.append(contentsOf: incoming_dms)
|
||||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: dms, damus_state: damus_state)
|
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: dms, damus_state: damus_state)
|
||||||
} else if sub_id == notifications_subid {
|
} else if sub_id == notifications_subid {
|
||||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: notifications, damus_state: damus_state)
|
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: notifications.all_events, damus_state: damus_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.loading = false
|
self.loading = false
|
||||||
@@ -317,6 +388,7 @@ class HomeModel: ObservableObject {
|
|||||||
NostrKind.chat.rawValue,
|
NostrKind.chat.rawValue,
|
||||||
NostrKind.like.rawValue,
|
NostrKind.like.rawValue,
|
||||||
NostrKind.boost.rawValue,
|
NostrKind.boost.rawValue,
|
||||||
|
NostrKind.zap.rawValue,
|
||||||
])
|
])
|
||||||
notifications_filter.pubkeys = [damus_state.pubkey]
|
notifications_filter.pubkeys = [damus_state.pubkey]
|
||||||
notifications_filter.limit = 100
|
notifications_filter.limit = 100
|
||||||
@@ -372,7 +444,7 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handle_metadata_event(_ ev: NostrEvent) {
|
func handle_metadata_event(_ ev: NostrEvent) {
|
||||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_last_event_of_kind(relay_id: String, kind: Int) -> NostrEvent? {
|
func get_last_event_of_kind(relay_id: String, kind: Int) -> NostrEvent? {
|
||||||
@@ -389,10 +461,10 @@ class HomeModel: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !insert_uniq_sorted_event(events: ¬ifications, new_ev: ev, cmp: { $0.created_at > $1.created_at }) {
|
if !notifications.insert(ev) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_last_event(ev: ev, timeline: .notifications)
|
handle_last_event(ev: ev, timeline: .notifications)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,29 +474,45 @@ class HomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func insert_home_event(_ ev: NostrEvent) -> Bool {
|
func insert_home_event(_ ev: NostrEvent) {
|
||||||
let ok = insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at })
|
if events.insert(ev) {
|
||||||
if ok {
|
|
||||||
handle_last_event(ev: ev, timeline: .home)
|
handle_last_event(ev: ev, timeline: .home)
|
||||||
}
|
}
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_text_event(sub_id: String, _ ev: NostrEvent) {
|
func handle_text_event(sub_id: String, _ ev: NostrEvent) {
|
||||||
if should_hide_event(contacts: damus_state.contacts, ev: ev) {
|
guard should_show_event(contacts: damus_state.contacts, ev: ev) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if sub_id == home_subid {
|
if sub_id == home_subid {
|
||||||
let _ = insert_home_event(ev)
|
insert_home_event(ev)
|
||||||
} else if sub_id == notifications_subid {
|
} else if sub_id == notifications_subid {
|
||||||
handle_notification(ev: ev)
|
handle_notification(ev: ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_dm(_ ev: NostrEvent) {
|
func handle_dm(_ ev: NostrEvent) {
|
||||||
if let notifs = handle_incoming_dm(prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, ev: ev) {
|
guard should_show_event(contacts: damus_state.contacts, ev: ev) else {
|
||||||
self.new_events = notifs
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !should_debounce_dms {
|
||||||
|
self.incoming_dms.append(ev)
|
||||||
|
if let notifs = handle_incoming_dms(prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, evs: self.incoming_dms) {
|
||||||
|
self.new_events = notifs
|
||||||
|
}
|
||||||
|
self.incoming_dms = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
incoming_dms.append(ev)
|
||||||
|
|
||||||
|
dm_debouncer.debounce {
|
||||||
|
if let notifs = handle_incoming_dms(prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, evs: self.incoming_dms) {
|
||||||
|
self.new_events = notifs
|
||||||
|
}
|
||||||
|
self.incoming_dms = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -530,10 +618,17 @@ func print_filters(relay_id: String?, filters groups: [[NostrFilter]]) {
|
|||||||
print("-----")
|
print("-----")
|
||||||
}
|
}
|
||||||
|
|
||||||
func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
func process_metadata_event(our_pubkey: String, profiles: Profiles, ev: NostrEvent) {
|
||||||
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if our_pubkey == ev.pubkey && (profile.deleted ?? false) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
notify(.deleted_account, ())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var old_nip05: String? = nil
|
var old_nip05: String? = nil
|
||||||
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||||
@@ -563,14 +658,14 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
|||||||
|
|
||||||
// load pfps asap
|
// load pfps asap
|
||||||
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
||||||
if let _ = URL(string: picture) {
|
if URL(string: picture) != nil {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let banner = tprof.profile.banner ?? ""
|
let banner = tprof.profile.banner ?? ""
|
||||||
if let _ = URL(string: banner) {
|
if URL(string: banner) != nil {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||||
}
|
}
|
||||||
@@ -583,31 +678,31 @@ func robohash(_ pk: String) -> String {
|
|||||||
return "https://robohash.org/" + pk
|
return "https://robohash.org/" + pk
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_our_stuff(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) {
|
func load_our_stuff(state: DamusState, ev: NostrEvent) {
|
||||||
guard ev.pubkey == pubkey else {
|
guard ev.pubkey == state.pubkey else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// only use new stuff
|
// only use new stuff
|
||||||
if let current_ev = contacts.event {
|
if let current_ev = state.contacts.event {
|
||||||
guard ev.created_at > current_ev.created_at else {
|
guard ev.created_at > current_ev.created_at else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let m_old_ev = contacts.event
|
let m_old_ev = state.contacts.event
|
||||||
contacts.event = ev
|
state.contacts.event = ev
|
||||||
|
|
||||||
load_our_contacts(contacts: contacts, our_pubkey: pubkey, m_old_ev: m_old_ev, ev: ev)
|
load_our_contacts(contacts: state.contacts, our_pubkey: state.pubkey, m_old_ev: m_old_ev, ev: ev)
|
||||||
load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, m_old_ev: m_old_ev, ev: ev)
|
load_our_relays(state: state, m_old_ev: m_old_ev, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) {
|
func process_contact_event(state: DamusState, ev: NostrEvent) {
|
||||||
load_our_stuff(pool: pool, contacts: contacts, pubkey: pubkey, ev: ev)
|
load_our_stuff(state: state, ev: ev)
|
||||||
add_contact_if_friend(contacts: contacts, ev: ev)
|
add_contact_if_friend(contacts: state.contacts, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
func load_our_relays(state: DamusState, m_old_ev: NostrEvent?, ev: NostrEvent) {
|
||||||
let bootstrap_dict: [String: RelayInfo] = [:]
|
let bootstrap_dict: [String: RelayInfo] = [:]
|
||||||
let old_decoded = m_old_ev.flatMap { decode_json_relays($0.content) } ?? BOOTSTRAP_RELAYS.reduce(into: bootstrap_dict) { (d, r) in
|
let old_decoded = m_old_ev.flatMap { decode_json_relays($0.content) } ?? BOOTSTRAP_RELAYS.reduce(into: bootstrap_dict) { (d, r) in
|
||||||
d[r] = .rw
|
d[r] = .rw
|
||||||
@@ -631,14 +726,15 @@ func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_
|
|||||||
|
|
||||||
let diff = old.symmetricDifference(new)
|
let diff = old.symmetricDifference(new)
|
||||||
|
|
||||||
|
let new_relay_filters = load_relay_filters(state.pubkey) == nil
|
||||||
for d in diff {
|
for d in diff {
|
||||||
changed = true
|
changed = true
|
||||||
if new.contains(d) {
|
if new.contains(d) {
|
||||||
if let url = URL(string: d) {
|
if let url = URL(string: d) {
|
||||||
try? pool.add_relay(url, info: decoded[d] ?? .rw)
|
add_new_relay(relay_filters: state.relay_filters, metadatas: state.relay_metadata, pool: state.pool, url: url, info: decoded[d] ?? .rw, new_relay_filters: new_relay_filters)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pool.remove_relay(d)
|
state.pool.remove_relay(d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,9 +743,61 @@ func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_incoming_dm(prev_events: NewEventsBits, dms: DirectMessagesModel, our_pubkey: String, ev: NostrEvent) -> NewEventsBits? {
|
func add_new_relay(relay_filters: RelayFilters, metadatas: RelayMetadatas, pool: RelayPool, url: URL, info: RelayInfo, new_relay_filters: Bool) {
|
||||||
|
try? pool.add_relay(url, info: info)
|
||||||
|
|
||||||
|
let relay_id = url.absoluteString
|
||||||
|
guard metadatas.lookup(relay_id: relay_id) == nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.detached(priority: .background) {
|
||||||
|
guard let meta = try? await fetch_relay_metadata(relay_id: relay_id) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
metadatas.insert(relay_id: relay_id, metadata: meta)
|
||||||
|
|
||||||
|
// if this is the first time adding filters, we should filter non-paid relays
|
||||||
|
if new_relay_filters && !meta.is_paid {
|
||||||
|
relay_filters.insert(timeline: .search, relay_id: relay_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetch_relay_metadata(relay_id: String) async throws -> RelayMetadata? {
|
||||||
|
var urlString = relay_id.replacingOccurrences(of: "wss://", with: "https://")
|
||||||
|
urlString = urlString.replacingOccurrences(of: "ws://", with: "http://")
|
||||||
|
|
||||||
|
guard let url = URL(string: urlString) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.setValue("application/nostr+json", forHTTPHeaderField: "Accept")
|
||||||
|
|
||||||
|
var res: (Data, URLResponse)? = nil
|
||||||
|
|
||||||
|
res = try await URLSession.shared.data(for: request)
|
||||||
|
|
||||||
|
guard let data = res?.0 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let nip11 = try JSONDecoder().decode(RelayMetadata.self, from: data)
|
||||||
|
return nip11
|
||||||
|
}
|
||||||
|
|
||||||
|
func process_relay_metadata() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func handle_incoming_dm(ev: NostrEvent, our_pubkey: String, dms: DirectMessagesModel, prev_events: NewEventsBits) -> (Bool, NewEventsBits?) {
|
||||||
var inserted = false
|
var inserted = false
|
||||||
var found = false
|
var found = false
|
||||||
|
|
||||||
let ours = ev.pubkey == our_pubkey
|
let ours = ev.pubkey == our_pubkey
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
||||||
@@ -676,15 +824,34 @@ func handle_incoming_dm(prev_events: NewEventsBits, dms: DirectMessagesModel, ou
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
inserted = true
|
|
||||||
let model = DirectMessageModel(events: [ev], our_pubkey: our_pubkey)
|
let model = DirectMessageModel(events: [ev], our_pubkey: our_pubkey)
|
||||||
dms.dms.append((the_pk, model))
|
dms.dms.append((the_pk, model))
|
||||||
|
inserted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var new_bits: NewEventsBits? = nil
|
||||||
|
if inserted {
|
||||||
|
new_bits = handle_last_events(new_events: prev_events, ev: ev, timeline: .dms, shouldNotify: !ours)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (inserted, new_bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func handle_incoming_dms(prev_events: NewEventsBits, dms: DirectMessagesModel, our_pubkey: String, evs: [NostrEvent]) -> NewEventsBits? {
|
||||||
|
var inserted = false
|
||||||
|
|
||||||
var new_events: NewEventsBits? = nil
|
var new_events: NewEventsBits? = nil
|
||||||
|
|
||||||
|
for ev in evs {
|
||||||
|
let res = handle_incoming_dm(ev: ev, our_pubkey: our_pubkey, dms: dms, prev_events: prev_events)
|
||||||
|
inserted = res.0 || inserted
|
||||||
|
if let new = res.1 {
|
||||||
|
new_events = new
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if inserted {
|
if inserted {
|
||||||
new_events = handle_last_events(new_events: prev_events, ev: ev, timeline: .dms, shouldNotify: !ours)
|
|
||||||
|
|
||||||
dms.dms = dms.dms.filter({ $0.1.events.count > 0 }).sorted { a, b in
|
dms.dms = dms.dms.filter({ $0.1.events.count > 0 }).sorted { a, b in
|
||||||
return a.1.events.last!.created_at > b.1.events.last!.created_at
|
return a.1.events.last!.created_at > b.1.events.last!.created_at
|
||||||
}
|
}
|
||||||
@@ -721,9 +888,10 @@ func event_has_our_pubkey(_ ev: NostrEvent, our_pubkey: String) -> Bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func should_hide_event(contacts: Contacts, ev: NostrEvent) -> Bool {
|
func should_show_event(contacts: Contacts, ev: NostrEvent) -> Bool {
|
||||||
if contacts.is_muted(ev.pubkey) {
|
if contacts.is_muted(ev.pubkey) {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
return !ev.should_show_event
|
return ev.should_show_event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
//
|
|
||||||
// KFImageModel.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Oleg Abalonski on 1/11/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Kingfisher
|
|
||||||
import SVGKit
|
|
||||||
|
|
||||||
class KFImageModel: ObservableObject {
|
|
||||||
|
|
||||||
let url: URL?
|
|
||||||
let fallbackUrl: URL?
|
|
||||||
let processor: ImageProcessor
|
|
||||||
let serializer: CacheSerializer
|
|
||||||
|
|
||||||
@Published var refreshID = ""
|
|
||||||
|
|
||||||
init(url: URL?, fallbackUrl: URL?, maxByteSize: Int, downsampleSize: CGSize) {
|
|
||||||
self.url = url
|
|
||||||
self.fallbackUrl = fallbackUrl
|
|
||||||
self.processor = CustomImageProcessor(maxSize: maxByteSize, downsampleSize: downsampleSize)
|
|
||||||
self.serializer = CustomCacheSerializer(maxSize: maxByteSize, downsampleSize: downsampleSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func refresh() -> Void {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.refreshID = UUID().uuidString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cache(_ image: UIImage, forKey key: String) -> Void {
|
|
||||||
KingfisherManager.shared.cache.store(image, forKey: key, processorIdentifier: processor.identifier) { _ in
|
|
||||||
self.refresh()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadFailed() -> Void {
|
|
||||||
guard let url = url, let fallbackUrl = fallbackUrl else { return }
|
|
||||||
|
|
||||||
DispatchQueue.global(qos: .background).async {
|
|
||||||
KingfisherManager.shared.downloader.downloadImage(with: fallbackUrl) { result in
|
|
||||||
|
|
||||||
var fallbackImage: UIImage {
|
|
||||||
switch result {
|
|
||||||
case .success(let imageLoadingResult):
|
|
||||||
return imageLoadingResult.image
|
|
||||||
case .failure(let error):
|
|
||||||
print(error)
|
|
||||||
return UIImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cache(fallbackImage, forKey: url.absoluteString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CustomImageProcessor: ImageProcessor {
|
|
||||||
|
|
||||||
let maxSize: Int
|
|
||||||
let downsampleSize: CGSize
|
|
||||||
|
|
||||||
let identifier = "com.damus.customimageprocessor"
|
|
||||||
|
|
||||||
func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
|
|
||||||
|
|
||||||
switch item {
|
|
||||||
case .image(_):
|
|
||||||
// This case will never run
|
|
||||||
return DefaultImageProcessor.default.process(item: item, options: options)
|
|
||||||
case .data(let data):
|
|
||||||
|
|
||||||
// Handle large image size
|
|
||||||
if data.count > maxSize {
|
|
||||||
return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle SVG image
|
|
||||||
if let svgImage = SVGKImage(data: data), let image = svgImage.uiImage {
|
|
||||||
return image.kf.scaled(to: options.scaleFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefaultImageProcessor.default.process(item: item, options: options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CustomCacheSerializer: CacheSerializer {
|
|
||||||
|
|
||||||
let maxSize: Int
|
|
||||||
let downsampleSize: CGSize
|
|
||||||
|
|
||||||
func data(with image: Kingfisher.KFCrossPlatformImage, original: Data?) -> Data? {
|
|
||||||
return DefaultCacheSerializer.default.data(with: image, original: original)
|
|
||||||
}
|
|
||||||
|
|
||||||
func image(with data: Data, options: Kingfisher.KingfisherParsedOptionsInfo) -> Kingfisher.KFCrossPlatformImage? {
|
|
||||||
if data.count > maxSize {
|
|
||||||
return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefaultCacheSerializer.default.image(with: data, options: options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// LibreTranslateServer.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Terry Yiu on 1/21/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum LibreTranslateServer: String, CaseIterable, Identifiable {
|
||||||
|
var id: String { self.rawValue }
|
||||||
|
|
||||||
|
struct Model: Identifiable, Hashable {
|
||||||
|
var id: String { self.tag }
|
||||||
|
var tag: String
|
||||||
|
var displayName: String
|
||||||
|
var url: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
case argosopentech
|
||||||
|
case terraprint
|
||||||
|
case vern
|
||||||
|
case custom
|
||||||
|
|
||||||
|
var model: Model {
|
||||||
|
switch self {
|
||||||
|
case .argosopentech:
|
||||||
|
return .init(tag: self.rawValue, displayName: "translate.argosopentech.com", url: "https://translate.argosopentech.com")
|
||||||
|
case .terraprint:
|
||||||
|
return .init(tag: self.rawValue, displayName: "translate.terraprint.co", url: "https://translate.terraprint.co")
|
||||||
|
case .vern:
|
||||||
|
return .init(tag: self.rawValue, displayName: "lt.vern.cc", url: "https://lt.vern.cc")
|
||||||
|
case .custom:
|
||||||
|
return .init(tag: self.rawValue, displayName: NSLocalizedString("Custom", comment: "Dropdown option for selecting a custom translation server."), url: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var allModels: [Model] {
|
||||||
|
return Self.allCases.map { $0.model }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
enum CountResult {
|
||||||
|
case already_counted
|
||||||
|
case success(Int)
|
||||||
|
}
|
||||||
|
|
||||||
class EventCounter {
|
class EventCounter {
|
||||||
var counts: [String: Int] = [:]
|
var counts: [String: Int] = [:]
|
||||||
@@ -14,11 +18,6 @@ class EventCounter {
|
|||||||
var our_events: [String: NostrEvent] = [:]
|
var our_events: [String: NostrEvent] = [:]
|
||||||
var our_pubkey: String
|
var our_pubkey: String
|
||||||
|
|
||||||
enum CountResult {
|
|
||||||
case already_counted
|
|
||||||
case success(Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
init (our_pubkey: String) {
|
init (our_pubkey: String) {
|
||||||
self.our_pubkey = our_pubkey
|
self.our_pubkey = our_pubkey
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,13 +32,30 @@ struct IdBlock: Identifiable {
|
|||||||
let block: Block
|
let block: Block
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Invoice {
|
typealias Invoice = LightningInvoice<Amount>
|
||||||
let description: String
|
typealias ZapInvoice = LightningInvoice<Int64>
|
||||||
let amount: Amount
|
|
||||||
|
enum InvoiceDescription {
|
||||||
|
case description(String)
|
||||||
|
case description_hash(Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LightningInvoice<T> {
|
||||||
|
let description: InvoiceDescription
|
||||||
|
let amount: T
|
||||||
let string: String
|
let string: String
|
||||||
let expiry: UInt64
|
let expiry: UInt64
|
||||||
let payment_hash: Data
|
let payment_hash: Data
|
||||||
let created_at: UInt64
|
let created_at: UInt64
|
||||||
|
|
||||||
|
var description_string: String {
|
||||||
|
switch description {
|
||||||
|
case .description(let string):
|
||||||
|
return string
|
||||||
|
case .description_hash:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Block {
|
enum Block {
|
||||||
@@ -189,20 +206,78 @@ enum Amount: Equatable {
|
|||||||
case .any:
|
case .any:
|
||||||
return NSLocalizedString("Any", comment: "Any amount of sats")
|
return NSLocalizedString("Any", comment: "Any amount of sats")
|
||||||
case .specific(let amt):
|
case .specific(let amt):
|
||||||
let numberFormatter = NumberFormatter()
|
return format_msats(amt)
|
||||||
numberFormatter.numberStyle = .decimal
|
|
||||||
numberFormatter.minimumFractionDigits = 0
|
|
||||||
numberFormatter.maximumFractionDigits = 3
|
|
||||||
numberFormatter.roundingMode = .down
|
|
||||||
|
|
||||||
let sats = NSNumber(value: (Double(amt) / 1000.0))
|
|
||||||
let formattedSats = numberFormatter.string(from: sats) ?? sats.stringValue
|
|
||||||
|
|
||||||
return String(format: NSLocalizedString("sats_count", comment: "Amount of sats."), sats.decimalValue as NSDecimalNumber, formattedSats)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func format_actions_abbrev(_ actions: Int) -> String {
|
||||||
|
let formatter = NumberFormatter()
|
||||||
|
formatter.numberStyle = .decimal
|
||||||
|
formatter.positiveSuffix = "m"
|
||||||
|
formatter.positivePrefix = ""
|
||||||
|
formatter.minimumFractionDigits = 0
|
||||||
|
formatter.maximumFractionDigits = 3
|
||||||
|
formatter.roundingMode = .down
|
||||||
|
formatter.roundingIncrement = 0.1
|
||||||
|
formatter.multiplier = 1
|
||||||
|
|
||||||
|
if actions >= 1_000_000 {
|
||||||
|
formatter.positiveSuffix = "m"
|
||||||
|
formatter.multiplier = 0.000001
|
||||||
|
} else if actions >= 1000 {
|
||||||
|
formatter.positiveSuffix = "k"
|
||||||
|
formatter.multiplier = 0.001
|
||||||
|
} else {
|
||||||
|
return "\(actions)"
|
||||||
|
}
|
||||||
|
|
||||||
|
let actions = NSNumber(value: actions)
|
||||||
|
|
||||||
|
return formatter.string(from: actions) ?? "\(actions)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func format_msats_abbrev(_ msats: Int64) -> String {
|
||||||
|
let formatter = NumberFormatter()
|
||||||
|
formatter.numberStyle = .decimal
|
||||||
|
formatter.positiveSuffix = "m"
|
||||||
|
formatter.positivePrefix = ""
|
||||||
|
formatter.minimumFractionDigits = 0
|
||||||
|
formatter.maximumFractionDigits = 3
|
||||||
|
formatter.roundingMode = .down
|
||||||
|
formatter.roundingIncrement = 0.1
|
||||||
|
formatter.multiplier = 1
|
||||||
|
|
||||||
|
let sats = NSNumber(value: (Double(msats) / 1000.0))
|
||||||
|
|
||||||
|
if msats >= 1_000_000*1000 {
|
||||||
|
formatter.positiveSuffix = "m"
|
||||||
|
formatter.multiplier = 0.000001
|
||||||
|
} else if msats >= 1000*1000 {
|
||||||
|
formatter.positiveSuffix = "k"
|
||||||
|
formatter.multiplier = 0.001
|
||||||
|
} else {
|
||||||
|
return sats.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatter.string(from: sats) ?? sats.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func format_msats(_ msat: Int64, locale: Locale = Locale.current) -> String {
|
||||||
|
let numberFormatter = NumberFormatter()
|
||||||
|
numberFormatter.numberStyle = .decimal
|
||||||
|
numberFormatter.minimumFractionDigits = 0
|
||||||
|
numberFormatter.maximumFractionDigits = 3
|
||||||
|
numberFormatter.roundingMode = .down
|
||||||
|
numberFormatter.locale = locale
|
||||||
|
|
||||||
|
let sats = NSNumber(value: (Double(msat) / 1000.0))
|
||||||
|
let formattedSats = numberFormatter.string(from: sats) ?? sats.stringValue
|
||||||
|
|
||||||
|
let bundle = bundleForLocale(locale: locale)
|
||||||
|
return String(format: bundle.localizedString(forKey: "sats_count", value: nil, table: nil), locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats)
|
||||||
|
}
|
||||||
|
|
||||||
func convert_invoice_block(_ b: invoice_block) -> Block? {
|
func convert_invoice_block(_ b: invoice_block) -> Block? {
|
||||||
guard let invstr = strblock_to_string(b.invstr) else {
|
guard let invstr = strblock_to_string(b.invstr) else {
|
||||||
return nil
|
return nil
|
||||||
@@ -212,9 +287,8 @@ func convert_invoice_block(_ b: invoice_block) -> Block? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var description = ""
|
guard let description = convert_invoice_description(b11: b11) else {
|
||||||
if b11.description != nil {
|
return nil
|
||||||
description = String(cString: b11.description)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let amount: Amount = maybe_pointee(b11.msat).map { .specific(Int64($0.millisatoshis)) } ?? .any
|
let amount: Amount = maybe_pointee(b11.msat).map { .specific(Int64($0.millisatoshis)) } ?? .any
|
||||||
@@ -225,6 +299,18 @@ func convert_invoice_block(_ b: invoice_block) -> Block? {
|
|||||||
return .invoice(Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, payment_hash: payment_hash, created_at: created_at))
|
return .invoice(Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, payment_hash: payment_hash, created_at: created_at))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convert_invoice_description(b11: bolt11) -> InvoiceDescription? {
|
||||||
|
if let desc = b11.description {
|
||||||
|
return .description(String(cString: desc))
|
||||||
|
}
|
||||||
|
|
||||||
|
if var deschash = maybe_pointee(b11.description_hash) {
|
||||||
|
return .description_hash(Data(bytes: &deschash, count: 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func convert_mention_block(ind: Int32, tags: [[String]]) -> Block?
|
func convert_mention_block(ind: Int32, tags: [[String]]) -> Block?
|
||||||
{
|
{
|
||||||
let ind = Int(ind)
|
let ind = Int(ind)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class ProfileModel: ObservableObject, Equatable {
|
class ProfileModel: ObservableObject, Equatable {
|
||||||
@Published var events: [NostrEvent] = []
|
var events: EventHolder = EventHolder()
|
||||||
@Published var contacts: NostrEvent? = nil
|
@Published var contacts: NostrEvent? = nil
|
||||||
@Published var following: Int = 0
|
@Published var following: Int = 0
|
||||||
@Published var relays: [String: RelayInfo]? = nil
|
@Published var relays: [String: RelayInfo]? = nil
|
||||||
@@ -20,6 +20,24 @@ class ProfileModel: ObservableObject, Equatable {
|
|||||||
var sub_id = UUID().description
|
var sub_id = UUID().description
|
||||||
var prof_subid = UUID().description
|
var prof_subid = UUID().description
|
||||||
|
|
||||||
|
func follows(pubkey: String) -> Bool {
|
||||||
|
guard let contacts = self.contacts else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for tag in contacts.tags {
|
||||||
|
guard tag.count >= 2 && tag[0] == "p" else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tag[1] == pubkey {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func get_follow_target() -> FollowTarget {
|
func get_follow_target() -> FollowTarget {
|
||||||
if let contacts = contacts {
|
if let contacts = contacts {
|
||||||
return .contact(contacts)
|
return .contact(contacts)
|
||||||
@@ -70,7 +88,7 @@ class ProfileModel: ObservableObject, Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handle_profile_contact_event(_ ev: NostrEvent) {
|
func handle_profile_contact_event(_ ev: NostrEvent) {
|
||||||
process_contact_event(pool: damus.pool, contacts: damus.contacts, pubkey: damus.pubkey, ev: ev)
|
process_contact_event(state: damus, ev: ev)
|
||||||
|
|
||||||
// only use new stuff
|
// only use new stuff
|
||||||
if let current_ev = self.contacts {
|
if let current_ev = self.contacts {
|
||||||
@@ -93,11 +111,13 @@ class ProfileModel: ObservableObject, Equatable {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ev.is_textlike || ev.known_kind == .boost {
|
if ev.is_textlike || ev.known_kind == .boost {
|
||||||
let _ = insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at})
|
if self.events.insert(ev) {
|
||||||
|
self.objectWillChange.send()
|
||||||
|
}
|
||||||
} else if ev.known_kind == .contacts {
|
} else if ev.known_kind == .contacts {
|
||||||
handle_profile_contact_event(ev)
|
handle_profile_contact_event(ev)
|
||||||
} else if ev.known_kind == .metadata {
|
} else if ev.known_kind == .metadata {
|
||||||
process_metadata_event(profiles: damus.profiles, ev: ev)
|
process_metadata_event(our_pubkey: damus.pubkey, profiles: damus.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
seen_event.insert(ev.id)
|
seen_event.insert(ev.id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,71 +8,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
class ReactionsModel: ObservableObject {
|
final class ReactionsModel: EventsModel {
|
||||||
let state: DamusState
|
|
||||||
let target: String
|
|
||||||
let sub_id: String
|
|
||||||
let profiles_id: String
|
|
||||||
|
|
||||||
@Published var reactions: [NostrEvent]
|
init(state: DamusState, target: String) {
|
||||||
|
super.init(state: state, target: target, kind: .like)
|
||||||
init (state: DamusState, target: String) {
|
|
||||||
self.state = state
|
|
||||||
self.target = target
|
|
||||||
self.sub_id = UUID().description
|
|
||||||
self.profiles_id = UUID().description
|
|
||||||
self.reactions = []
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_filter() -> NostrFilter {
|
|
||||||
var filter = NostrFilter.filter_kinds([7])
|
|
||||||
filter.referenced_ids = [target]
|
|
||||||
filter.limit = 500
|
|
||||||
return filter
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribe() {
|
|
||||||
let filter = get_filter()
|
|
||||||
let filters = [filter]
|
|
||||||
self.state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_nostr_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsubscribe() {
|
|
||||||
self.state.pool.unsubscribe(sub_id: sub_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle_event(relay_id: String, ev: NostrEvent) {
|
|
||||||
guard ev.kind == 7 else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let reacted_to = last_etag(tags: ev.tags) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard reacted_to == self.target else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if insert_uniq_sorted_event(events: &self.reactions, new_ev: ev, cmp: { a, b in a.created_at < b.created_at } ) {
|
|
||||||
objectWillChange.send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle_nostr_event(relay_id: String, ev: NostrConnectionEvent) {
|
|
||||||
guard case .nostr_event(let nev) = ev else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch nev {
|
|
||||||
case .event(_, let ev):
|
|
||||||
handle_event(relay_id: relay_id, ev: ev)
|
|
||||||
|
|
||||||
case .notice(_):
|
|
||||||
break
|
|
||||||
case .eose(_):
|
|
||||||
load_profiles(profiles_subid: profiles_id, relay_id: relay_id, events: reactions, damus_state: state)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,71 +7,9 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class RepostsModel: ObservableObject {
|
final class RepostsModel: EventsModel {
|
||||||
let state: DamusState
|
|
||||||
let target: String
|
init(state: DamusState, target: String) {
|
||||||
let sub_id: String
|
super.init(state: state, target: target, kind: .boost)
|
||||||
let profiles_id: String
|
|
||||||
|
|
||||||
@Published var reposts: [NostrEvent]
|
|
||||||
|
|
||||||
init (state: DamusState, target: String) {
|
|
||||||
self.state = state
|
|
||||||
self.target = target
|
|
||||||
self.sub_id = UUID().description
|
|
||||||
self.profiles_id = UUID().description
|
|
||||||
self.reposts = []
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_filter() -> NostrFilter {
|
|
||||||
var filter = NostrFilter.filter_kinds([NostrKind.boost.rawValue])
|
|
||||||
filter.referenced_ids = [target]
|
|
||||||
filter.limit = 500
|
|
||||||
return filter
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribe() {
|
|
||||||
let filter = get_filter()
|
|
||||||
let filters = [filter]
|
|
||||||
self.state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_nostr_event)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsubscribe() {
|
|
||||||
self.state.pool.unsubscribe(sub_id: sub_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle_event(relay_id: String, ev: NostrEvent) {
|
|
||||||
guard ev.kind == NostrKind.boost.rawValue else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let reposted_event = last_etag(tags: ev.tags) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard reposted_event == self.target else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if insert_uniq_sorted_event(events: &self.reposts, new_ev: ev, cmp: { a, b in a.created_at < b.created_at } ) {
|
|
||||||
objectWillChange.send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handle_nostr_event(relay_id: String, ev: NostrConnectionEvent) {
|
|
||||||
guard case .nostr_event(let nev) = ev else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch nev {
|
|
||||||
case .event(_, let ev):
|
|
||||||
handle_event(relay_id: relay_id, ev: ev)
|
|
||||||
|
|
||||||
case .notice(_):
|
|
||||||
break
|
|
||||||
case .eose(_):
|
|
||||||
load_profiles(profiles_subid: profiles_id, relay_id: relay_id, events: reposts, damus_state: state)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
/// The data model for the SearchHome view, typically something global-like
|
/// The data model for the SearchHome view, typically something global-like
|
||||||
class SearchHomeModel: ObservableObject {
|
class SearchHomeModel: ObservableObject {
|
||||||
@Published var events: [NostrEvent] = []
|
var events: EventHolder = EventHolder()
|
||||||
@Published var loading: Bool = false
|
@Published var loading: Bool = false
|
||||||
|
|
||||||
var seen_pubkey: Set<String> = Set()
|
var seen_pubkey: Set<String> = Set()
|
||||||
@@ -31,12 +31,14 @@ class SearchHomeModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func filter_muted() {
|
func filter_muted() {
|
||||||
events = events.filter { !should_hide_event(contacts: damus_state.contacts, ev: $0) }
|
events.filter { should_show_event(contacts: damus_state.contacts, ev: $0) }
|
||||||
|
self.objectWillChange.send()
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
loading = true
|
loading = true
|
||||||
damus_state.pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event)
|
let to_relays = determine_to_relays(pool: damus_state.pool, filters: damus_state.relay_filters)
|
||||||
|
damus_state.pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event, to: to_relays)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsubscribe(to: String? = nil) {
|
func unsubscribe(to: String? = nil) {
|
||||||
@@ -54,14 +56,14 @@ class SearchHomeModel: ObservableObject {
|
|||||||
guard sub_id == self.base_subid || sub_id == self.profiles_subid else {
|
guard sub_id == self.base_subid || sub_id == self.profiles_subid else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ev.is_textlike && !should_hide_event(contacts: damus_state.contacts, ev: ev) && !ev.is_reply(nil) {
|
if ev.is_textlike && should_show_event(contacts: damus_state.contacts, ev: ev) && !ev.is_reply(nil) {
|
||||||
if seen_pubkey.contains(ev.pubkey) {
|
if seen_pubkey.contains(ev.pubkey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
seen_pubkey.insert(ev.pubkey)
|
seen_pubkey.insert(ev.pubkey)
|
||||||
|
|
||||||
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
|
if self.events.insert(ev) {
|
||||||
$0.created_at > $1.created_at
|
self.objectWillChange.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .notice(let msg):
|
case .notice(let msg):
|
||||||
@@ -74,7 +76,7 @@ class SearchHomeModel: ObservableObject {
|
|||||||
// global events are not realtime
|
// global events are not realtime
|
||||||
unsubscribe(to: relay_id)
|
unsubscribe(to: relay_id)
|
||||||
|
|
||||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events.all_events, damus_state: damus_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -129,7 +131,7 @@ func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ev.known_kind == .metadata {
|
if ev.known_kind == .metadata {
|
||||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,20 +9,27 @@ import Foundation
|
|||||||
|
|
||||||
|
|
||||||
class SearchModel: ObservableObject {
|
class SearchModel: ObservableObject {
|
||||||
@Published var events: [NostrEvent] = []
|
var events: EventHolder = EventHolder()
|
||||||
@Published var loading: Bool = false
|
@Published var loading: Bool = false
|
||||||
@Published var channel_name: String? = nil
|
@Published var channel_name: String? = nil
|
||||||
|
|
||||||
let pool: RelayPool
|
let pool: RelayPool
|
||||||
var search: NostrFilter
|
var search: NostrFilter
|
||||||
|
let contacts: Contacts
|
||||||
let sub_id = UUID().description
|
let sub_id = UUID().description
|
||||||
let limit: UInt32 = 500
|
let limit: UInt32 = 500
|
||||||
|
|
||||||
init(pool: RelayPool, search: NostrFilter) {
|
init(contacts: Contacts, pool: RelayPool, search: NostrFilter) {
|
||||||
|
self.contacts = contacts
|
||||||
self.pool = pool
|
self.pool = pool
|
||||||
self.search = search
|
self.search = search
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filter_muted() {
|
||||||
|
self.events.filter { should_show_event(contacts: contacts, ev: $0) }
|
||||||
|
self.objectWillChange.send()
|
||||||
|
}
|
||||||
|
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
// since 1 month
|
// since 1 month
|
||||||
search.limit = self.limit
|
search.limit = self.limit
|
||||||
@@ -47,7 +54,11 @@ class SearchModel: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at } ) {
|
guard should_show_event(contacts: contacts, ev: ev) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.events.insert(ev) {
|
||||||
objectWillChange.send()
|
objectWillChange.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,6 +99,21 @@ func event_matches_hashtag(_ ev: NostrEvent, hashtags: [String]) -> Bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tag_is_hashtag(_ tag: [String]) -> Bool {
|
||||||
|
// "hashtag" is deprecated, will remove in the future
|
||||||
|
return tag.count >= 2 && (tag[0] == "hashtag" || tag[0] == "t")
|
||||||
|
}
|
||||||
|
|
||||||
|
func has_hashtag(_ tags: [[String]], hashtag: String) -> Bool {
|
||||||
|
for tag in tags {
|
||||||
|
if tag_is_hashtag(tag) && tag[1] == hashtag {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool {
|
func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool {
|
||||||
if let hashtags = filter.hashtag {
|
if let hashtags = filter.hashtag {
|
||||||
return event_matches_hashtag(ev, hashtags: hashtags)
|
return event_matches_hashtag(ev, hashtags: hashtags)
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ class ThreadModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ev.known_kind == .metadata {
|
if ev.known_kind == .metadata {
|
||||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev)
|
||||||
} else if ev.is_textlike {
|
} else if ev.is_textlike {
|
||||||
self.add_event(ev, privkey: self.damus_state.keypair.privkey)
|
self.add_event(ev, privkey: self.damus_state.keypair.privkey)
|
||||||
} else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create {
|
} else if ev.known_kind == .channel_meta || ev.known_kind == .channel_create {
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// TranslationService.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Terry Yiu on 2/3/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum TranslationService: String, CaseIterable, Identifiable {
|
||||||
|
var id: String { self.rawValue }
|
||||||
|
|
||||||
|
struct Model: Identifiable, Hashable {
|
||||||
|
var id: String { self.tag }
|
||||||
|
var tag: String
|
||||||
|
var displayName: String
|
||||||
|
}
|
||||||
|
|
||||||
|
case none
|
||||||
|
case libretranslate
|
||||||
|
case deepl
|
||||||
|
|
||||||
|
var model: Model {
|
||||||
|
switch self {
|
||||||
|
case .none:
|
||||||
|
return .init(tag: self.rawValue, displayName: NSLocalizedString("None", comment: "Dropdown option for selecting no translation service."))
|
||||||
|
case .libretranslate:
|
||||||
|
return .init(tag: self.rawValue, displayName: NSLocalizedString("LibreTranslate (Open Source)", comment: "Dropdown option for selecting LibreTranslate as the translation service."))
|
||||||
|
case .deepl:
|
||||||
|
return .init(tag: self.rawValue, displayName: NSLocalizedString("DeepL (Proprietary, Higher Accuracy)", comment: "Dropdown option for selecting DeepL as the translation service."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static var allModels: [Model] {
|
||||||
|
return Self.allCases.map { $0.model }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,76 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Vault
|
||||||
|
|
||||||
|
func should_show_wallet_selector(_ pubkey: String) -> Bool {
|
||||||
|
return UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
|
||||||
|
}
|
||||||
|
|
||||||
|
func pk_setting_key(_ pubkey: String, key: String) -> String {
|
||||||
|
return "\(pubkey)_\(key)"
|
||||||
|
}
|
||||||
|
|
||||||
|
func default_zap_setting_key(pubkey: String) -> String {
|
||||||
|
return pk_setting_key(pubkey, key: "default_zap_amount")
|
||||||
|
}
|
||||||
|
|
||||||
|
func set_default_zap_amount(pubkey: String, amount: Int) {
|
||||||
|
let key = default_zap_setting_key(pubkey: pubkey)
|
||||||
|
UserDefaults.standard.setValue(amount, forKey: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_default_zap_amount(pubkey: String) -> Int? {
|
||||||
|
let key = default_zap_setting_key(pubkey: pubkey)
|
||||||
|
let amt = UserDefaults.standard.integer(forKey: key)
|
||||||
|
if amt == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return amt
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func get_default_wallet(_ pubkey: String) -> Wallet {
|
||||||
|
if let defaultWalletName = UserDefaults.standard.string(forKey: "default_wallet"),
|
||||||
|
let default_wallet = Wallet(rawValue: defaultWalletName)
|
||||||
|
{
|
||||||
|
return default_wallet
|
||||||
|
} else {
|
||||||
|
return .system_default_wallet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func get_translation_service(_ pubkey: String) -> TranslationService? {
|
||||||
|
guard let translation_service = UserDefaults.standard.string(forKey: "translation_service") else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return TranslationService(rawValue: translation_service)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func get_deepl_plan(_ pubkey: String) -> DeepLPlan? {
|
||||||
|
guard let server_name = UserDefaults.standard.string(forKey: "deepl_plan") else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeepLPlan(rawValue: server_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func get_libretranslate_server(_ pubkey: String) -> LibreTranslateServer? {
|
||||||
|
guard let server_name = UserDefaults.standard.string(forKey: "libretranslate_server") else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return LibreTranslateServer(rawValue: server_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func get_libretranslate_url(_ pubkey: String, server: LibreTranslateServer) -> String? {
|
||||||
|
if let url = server.model.url {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserDefaults.standard.object(forKey: "libretranslate_url") as? String
|
||||||
|
}
|
||||||
|
|
||||||
class UserSettingsStore: ObservableObject {
|
class UserSettingsStore: ObservableObject {
|
||||||
@Published var default_wallet: Wallet {
|
@Published var default_wallet: Wallet {
|
||||||
@@ -26,16 +96,154 @@ class UserSettingsStore: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
@Published var translation_service: TranslationService {
|
||||||
if let defaultWalletName = UserDefaults.standard.string(forKey: "default_wallet"),
|
didSet {
|
||||||
let default_wallet = Wallet(rawValue: defaultWalletName)
|
UserDefaults.standard.set(translation_service.rawValue, forKey: "translation_service")
|
||||||
{
|
|
||||||
self.default_wallet = default_wallet
|
|
||||||
} else {
|
|
||||||
default_wallet = .system_default_wallet
|
|
||||||
}
|
}
|
||||||
show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
|
}
|
||||||
|
|
||||||
|
@Published var deepl_plan: DeepLPlan {
|
||||||
|
didSet {
|
||||||
|
UserDefaults.standard.set(deepl_plan.rawValue, forKey: "deepl_plan")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var deepl_api_key: String {
|
||||||
|
didSet {
|
||||||
|
do {
|
||||||
|
if deepl_api_key == "" {
|
||||||
|
try clearDeepLApiKey()
|
||||||
|
} else {
|
||||||
|
try saveDeepLApiKey(deepl_api_key)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// No-op.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var libretranslate_server: LibreTranslateServer {
|
||||||
|
didSet {
|
||||||
|
if oldValue == libretranslate_server {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDefaults.standard.set(libretranslate_server.rawValue, forKey: "libretranslate_server")
|
||||||
|
|
||||||
|
libretranslate_api_key = ""
|
||||||
|
|
||||||
|
if libretranslate_server == .custom {
|
||||||
|
libretranslate_url = ""
|
||||||
|
} else {
|
||||||
|
libretranslate_url = libretranslate_server.model.url!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var libretranslate_url: String {
|
||||||
|
didSet {
|
||||||
|
UserDefaults.standard.set(libretranslate_url, forKey: "libretranslate_url")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var libretranslate_api_key: String {
|
||||||
|
didSet {
|
||||||
|
do {
|
||||||
|
if libretranslate_api_key == "" {
|
||||||
|
try clearLibreTranslateApiKey()
|
||||||
|
} else {
|
||||||
|
try saveLibreTranslateApiKey(libretranslate_api_key)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// No-op.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// TODO: pubkey-scoped settings
|
||||||
|
let pubkey = ""
|
||||||
|
self.default_wallet = get_default_wallet(pubkey)
|
||||||
|
show_wallet_selector = should_show_wallet_selector(pubkey)
|
||||||
|
|
||||||
left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false
|
left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false
|
||||||
|
|
||||||
|
// Note from @tyiu:
|
||||||
|
// Default translation service is disabled by default for now until we gain some confidence that it is working well in production.
|
||||||
|
// Instead of throwing all Damus users onto feature immediately, allow for discovery of feature organically.
|
||||||
|
// Also, we are connecting to servers listed as mirrors on the official LibreTranslate GitHub README that do not require API keys.
|
||||||
|
// However, we have not asked them for permission to use, so we're trying to be good neighbors for now.
|
||||||
|
// Opportunity: spin up dedicated trusted LibreTranslate server that requires an API key for any access (or higher rate limit access).
|
||||||
|
if let translation_service = get_translation_service(pubkey) {
|
||||||
|
self.translation_service = translation_service
|
||||||
|
} else {
|
||||||
|
self.translation_service = .none
|
||||||
|
}
|
||||||
|
|
||||||
|
if let libretranslate_server = get_libretranslate_server(pubkey) {
|
||||||
|
self.libretranslate_server = libretranslate_server
|
||||||
|
self.libretranslate_url = get_libretranslate_url(pubkey, server: libretranslate_server) ?? ""
|
||||||
|
} else {
|
||||||
|
// Choose a random server to distribute load.
|
||||||
|
libretranslate_server = .allCases.filter { $0 != .custom }.randomElement()!
|
||||||
|
libretranslate_url = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
libretranslate_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
|
||||||
|
} catch {
|
||||||
|
libretranslate_api_key = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if let deepl_plan = get_deepl_plan(pubkey) {
|
||||||
|
self.deepl_plan = deepl_plan
|
||||||
|
} else {
|
||||||
|
self.deepl_plan = .free
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
deepl_api_key = try Vault.getPrivateKey(keychainConfiguration: DamusDeepLKeychainConfiguration())
|
||||||
|
} catch {
|
||||||
|
deepl_api_key = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveLibreTranslateApiKey(_ apiKey: String) throws {
|
||||||
|
try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func clearLibreTranslateApiKey() throws {
|
||||||
|
try Vault.deletePrivateKey(keychainConfiguration: DamusLibreTranslateKeychainConfiguration())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveDeepLApiKey(_ apiKey: String) throws {
|
||||||
|
try Vault.savePrivateKey(apiKey, keychainConfiguration: DamusDeepLKeychainConfiguration())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func clearDeepLApiKey() throws {
|
||||||
|
try Vault.deletePrivateKey(keychainConfiguration: DamusDeepLKeychainConfiguration())
|
||||||
|
}
|
||||||
|
|
||||||
|
func can_translate(_ pubkey: String) -> Bool {
|
||||||
|
switch translation_service {
|
||||||
|
case .none:
|
||||||
|
return false
|
||||||
|
case .libretranslate:
|
||||||
|
return URLComponents(string: libretranslate_url) != nil
|
||||||
|
case .deepl:
|
||||||
|
return deepl_api_key != ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DamusLibreTranslateKeychainConfiguration: KeychainConfiguration {
|
||||||
|
var serviceName = "damus"
|
||||||
|
var accessGroup: String? = nil
|
||||||
|
var accountName = "libretranslate_apikey"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DamusDeepLKeychainConfiguration: KeychainConfiguration {
|
||||||
|
var serviceName = "damus"
|
||||||
|
var accessGroup: String? = nil
|
||||||
|
var accountName = "deepl_apikey"
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ enum Wallet: String, CaseIterable, Identifiable {
|
|||||||
return .init(index: 0, tag: "strike", displayName: NSLocalizedString("Strike", comment: "Dropdown option label for Lightning wallet, Strike."), link: "strike:",
|
return .init(index: 0, tag: "strike", displayName: NSLocalizedString("Strike", comment: "Dropdown option label for Lightning wallet, Strike."), link: "strike:",
|
||||||
appStoreLink: "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", image: "strike")
|
appStoreLink: "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", image: "strike")
|
||||||
case .cashapp:
|
case .cashapp:
|
||||||
return .init(index: 1, tag: "cashapp", displayName: NSLocalizedString("Cash App", comment: "Dropdown option label for Lightning wallet, Cash App."), link: "squarecash://",
|
return .init(index: 1, tag: "cashapp", displayName: NSLocalizedString("Cash App", comment: "Dropdown option label for Lightning wallet, Cash App."), link: "https://cash.app/launch/lightning/",
|
||||||
appStoreLink: "https://apps.apple.com/us/app/cash-app/id711923939", image: "cashapp")
|
appStoreLink: "https://apps.apple.com/us/app/cash-app/id711923939", image: "cashapp")
|
||||||
case .muun:
|
case .muun:
|
||||||
return .init(index: 2, tag: "muun", displayName: NSLocalizedString("Muun", comment: "Dropdown option label for Lightning wallet, Muun."), link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun")
|
return .init(index: 2, tag: "muun", displayName: NSLocalizedString("Muun", comment: "Dropdown option label for Lightning wallet, Muun."), link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun")
|
||||||
@@ -53,7 +53,7 @@ enum Wallet: String, CaseIterable, Identifiable {
|
|||||||
return .init(index: 3, tag: "bluewallet", displayName: NSLocalizedString("Blue Wallet", comment: "Dropdown option label for Lightning wallet, Blue Wallet."), link: "bluewallet:lightning:",
|
return .init(index: 3, tag: "bluewallet", displayName: NSLocalizedString("Blue Wallet", comment: "Dropdown option label for Lightning wallet, Blue Wallet."), link: "bluewallet:lightning:",
|
||||||
appStoreLink: "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", image: "bluewallet")
|
appStoreLink: "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", image: "bluewallet")
|
||||||
case .walletofsatoshi:
|
case .walletofsatoshi:
|
||||||
return .init(index: 4, tag: "walletofsatoshi", displayName: NSLocalizedString("Wallet Of Satoshi", comment: "Dropdown option label for Lightning wallet, Wallet Of Satoshi."), link: "walletofsatoshi:lightning:",
|
return .init(index: 4, tag: "walletofsatoshi", displayName: NSLocalizedString("Wallet of Satoshi", comment: "Dropdown option label for Lightning wallet, Wallet of Satoshi."), link: "walletofsatoshi:lightning:",
|
||||||
appStoreLink: "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", image: "walletofsatoshi")
|
appStoreLink: "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", image: "walletofsatoshi")
|
||||||
case .zebedee:
|
case .zebedee:
|
||||||
return .init(index: 5, tag: "zebedee", displayName: NSLocalizedString("Zebedee", comment: "Dropdown option label for Lightning wallet, Zebedee."), link: "zebedee:lightning:",
|
return .init(index: 5, tag: "zebedee", displayName: NSLocalizedString("Zebedee", comment: "Dropdown option label for Lightning wallet, Zebedee."), link: "zebedee:lightning:",
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// ZapsModel.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-02-10.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ZapsModel: ObservableObject {
|
||||||
|
let state: DamusState
|
||||||
|
let target: ZapTarget
|
||||||
|
var zaps: [Zap]
|
||||||
|
|
||||||
|
let zaps_subid = UUID().description
|
||||||
|
let profiles_subid = UUID().description
|
||||||
|
|
||||||
|
init(state: DamusState, target: ZapTarget) {
|
||||||
|
self.state = state
|
||||||
|
self.target = target
|
||||||
|
self.zaps = []
|
||||||
|
}
|
||||||
|
|
||||||
|
func subscribe() {
|
||||||
|
var filter = NostrFilter.filter_kinds([9735])
|
||||||
|
switch target {
|
||||||
|
case .profile(let profile_id):
|
||||||
|
filter.pubkeys = [profile_id]
|
||||||
|
case .note(let note_target):
|
||||||
|
filter.referenced_ids = [note_target.note_id]
|
||||||
|
}
|
||||||
|
state.pool.subscribe(sub_id: zaps_subid, filters: [filter], handler: handle_event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsubscribe() {
|
||||||
|
state.pool.unsubscribe(sub_id: zaps_subid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) {
|
||||||
|
guard case .nostr_event(let resp) = conn_ev else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard resp.subid == zaps_subid else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch resp {
|
||||||
|
case .notice:
|
||||||
|
break
|
||||||
|
case .eose:
|
||||||
|
let events = self.zaps.map { $0.request.ev }
|
||||||
|
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: state)
|
||||||
|
case .event(_, let ev):
|
||||||
|
guard ev.kind == 9735 else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let zap = state.zaps.zaps[ev.id] {
|
||||||
|
if insert_uniq_sorted_zap(zaps: &zaps, new_zap: zap) {
|
||||||
|
objectWillChange.send()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
guard let zapper = state.profiles.lookup_zapper(pubkey: target.pubkey) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.zaps.add_zap(zap: zap)
|
||||||
|
|
||||||
|
if insert_uniq_sorted_zap(zaps: &zaps, new_zap: zap) {
|
||||||
|
objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,18 +24,22 @@ struct Profile: Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func str(_ str: String) -> String? {
|
private func str(_ str: String) -> String? {
|
||||||
guard let val = self.value[str] else{
|
return get_val(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func get_val<T>(_ v: String) -> T? {
|
||||||
|
guard let val = self.value[v] else{
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let s = val.value as? String else {
|
guard let s = val.value as? T else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
private mutating func set_str(_ key: String, _ val: String?) {
|
private mutating func set_val<T>(_ key: String, _ val: T?) {
|
||||||
if val == nil {
|
if val == nil {
|
||||||
self.value.removeValue(forKey: key)
|
self.value.removeValue(forKey: key)
|
||||||
return
|
return
|
||||||
@@ -44,6 +48,15 @@ struct Profile: Codable {
|
|||||||
self.value[key] = AnyCodable.init(val)
|
self.value[key] = AnyCodable.init(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private mutating func set_str(_ key: String, _ val: String?) {
|
||||||
|
set_val(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
var deleted: Bool? {
|
||||||
|
get { return get_val("deleted"); }
|
||||||
|
set(s) { set_val("deleted", s) }
|
||||||
|
}
|
||||||
|
|
||||||
var display_name: String? {
|
var display_name: String? {
|
||||||
get { return str("display_name"); }
|
get { return str("display_name"); }
|
||||||
set(s) { set_str("display_name", s) }
|
set(s) { set_str("display_name", s) }
|
||||||
@@ -89,7 +102,7 @@ struct Profile: Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var lnurl: String? {
|
var lnurl: String? {
|
||||||
guard let addr = lud06 ?? lud16 else {
|
guard let addr = lud16 ?? lud06 else {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +110,10 @@ struct Profile: Codable {
|
|||||||
return lnaddress_to_lnurl(addr);
|
return lnaddress_to_lnurl(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !addr.lowercased().hasPrefix("lnurl") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +126,10 @@ struct Profile: Codable {
|
|||||||
return make_ln_url(self.lnurl)
|
return make_ln_url(self.lnurl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.value = [:]
|
||||||
|
}
|
||||||
|
|
||||||
init(from decoder: Decoder) throws {
|
init(from decoder: Decoder) throws {
|
||||||
let container = try decoder.singleValueContainer()
|
let container = try decoder.singleValueContainer()
|
||||||
self.value = try container.decode([String: AnyCodable].self)
|
self.value = try container.decode([String: AnyCodable].self)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import secp256k1
|
|||||||
import secp256k1_implementation
|
import secp256k1_implementation
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum ValidationResult: Decodable {
|
enum ValidationResult: Decodable {
|
||||||
case ok
|
case ok
|
||||||
case bad_id
|
case bad_id
|
||||||
@@ -27,7 +29,7 @@ struct KeyEvent {
|
|||||||
let relay_url: String
|
let relay_url: String
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReferencedId: Identifiable, Hashable {
|
struct ReferencedId: Identifiable, Hashable, Equatable {
|
||||||
let ref_id: String
|
let ref_id: String
|
||||||
let relay_id: String?
|
let relay_id: String?
|
||||||
let key: String
|
let key: String
|
||||||
@@ -79,7 +81,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
}
|
}
|
||||||
|
|
||||||
var too_big: Bool {
|
var too_big: Bool {
|
||||||
return self.content.count > 32000
|
return self.content.count > 16000
|
||||||
}
|
}
|
||||||
|
|
||||||
var should_show_event: Bool {
|
var should_show_event: Bool {
|
||||||
@@ -103,11 +105,15 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
if let bs = _blocks {
|
if let bs = _blocks {
|
||||||
return bs
|
return bs
|
||||||
}
|
}
|
||||||
let blocks = parse_mentions(content: self.get_content(privkey), tags: self.tags)
|
let blocks = get_blocks(content: self.get_content(privkey))
|
||||||
self._blocks = blocks
|
self._blocks = blocks
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func get_blocks(content: String) -> [Block] {
|
||||||
|
return parse_mentions(content: content, tags: self.tags)
|
||||||
|
}
|
||||||
|
|
||||||
lazy var inner_event: NostrEvent? = {
|
lazy var inner_event: NostrEvent? = {
|
||||||
// don't try to deserialize an inner event if we know there won't be one
|
// don't try to deserialize an inner event if we know there won't be one
|
||||||
if self.known_kind == .boost {
|
if self.known_kind == .boost {
|
||||||
@@ -272,21 +278,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
return (self.flags & 1) != 0
|
return (self.flags & 1) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
init(content: String, pubkey: String, kind: Int = 1, tags: [[String]] = [], createdAt: Int64 = Int64(Date().timeIntervalSince1970)) {
|
init(id: String = "", content: String, pubkey: String, kind: Int = 1, tags: [[String]] = [], createdAt: Int64 = Int64(Date().timeIntervalSince1970)) {
|
||||||
self.id = ""
|
|
||||||
self.sig = ""
|
|
||||||
|
|
||||||
self.content = content
|
|
||||||
self.pubkey = pubkey
|
|
||||||
self.kind = kind
|
|
||||||
self.tags = tags
|
|
||||||
self.created_at = createdAt
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Intiialization statement used to specificy ID
|
|
||||||
///
|
|
||||||
/// This is mainly used for contant and testing data
|
|
||||||
init(id: String, content: String, pubkey: String, kind: Int = 1, tags: [[String]] = []) {
|
|
||||||
self.id = id
|
self.id = id
|
||||||
self.sig = ""
|
self.sig = ""
|
||||||
|
|
||||||
@@ -294,7 +286,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
|
|||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.created_at = Int64(Date().timeIntervalSince1970)
|
self.created_at = createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from: NostrEvent, content: String? = nil) {
|
init(from: NostrEvent, content: String? = nil) {
|
||||||
@@ -364,9 +356,14 @@ func decode_nostr_event(txt: String) -> NostrResponse? {
|
|||||||
|
|
||||||
func encode_json<T: Encodable>(_ val: T) -> String? {
|
func encode_json<T: Encodable>(_ val: T) -> String? {
|
||||||
let encoder = JSONEncoder()
|
let encoder = JSONEncoder()
|
||||||
|
encoder.outputFormatting = .withoutEscapingSlashes
|
||||||
return (try? encoder.encode(val)).map { String(decoding: $0, as: UTF8.self) }
|
return (try? encoder.encode(val)).map { String(decoding: $0, as: UTF8.self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decode_nostr_event_json(json: String) -> NostrEvent? {
|
||||||
|
return decode_json(json)
|
||||||
|
}
|
||||||
|
|
||||||
func decode_json<T: Decodable>(_ val: String) -> T? {
|
func decode_json<T: Decodable>(_ val: String) -> T? {
|
||||||
return try? JSONDecoder().decode(T.self, from: Data(val.utf8))
|
return try? JSONDecoder().decode(T.self, from: Data(val.utf8))
|
||||||
}
|
}
|
||||||
@@ -567,6 +564,26 @@ func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> Nost
|
|||||||
return ev
|
return ev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func zap_target_to_tags(_ target: ZapTarget) -> [[String]] {
|
||||||
|
switch target {
|
||||||
|
case .profile(let pk):
|
||||||
|
return [["p", pk]]
|
||||||
|
case .note(let note_target):
|
||||||
|
return [["e", note_target.note_id], ["p", note_target.author]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func make_zap_request_event(pubkey: String, privkey: String, content: String, relays: [RelayDescriptor], target: ZapTarget) -> NostrEvent {
|
||||||
|
var tags = zap_target_to_tags(target)
|
||||||
|
var relay_tag = ["relays"]
|
||||||
|
relay_tag.append(contentsOf: relays.map { $0.url.absoluteString })
|
||||||
|
tags.append(relay_tag)
|
||||||
|
let ev = NostrEvent(content: content, pubkey: pubkey, kind: 9734, tags: tags)
|
||||||
|
ev.id = calculate_event_id(ev: ev)
|
||||||
|
ev.sig = sign_event(privkey: privkey, ev: ev)
|
||||||
|
return ev
|
||||||
|
}
|
||||||
|
|
||||||
func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] {
|
func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] {
|
||||||
var ids = get_referenced_ids(tags: from.tags, key: "e").first.map { [$0] } ?? []
|
var ids = get_referenced_ids(tags: from.tags, key: "e").first.map { [$0] } ?? []
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct NostrFilter: Codable {
|
struct NostrFilter: Codable, Equatable {
|
||||||
var ids: [String]?
|
var ids: [String]?
|
||||||
var kinds: [Int]?
|
var kinds: [Int]?
|
||||||
var referenced_ids: [String]?
|
var referenced_ids: [String]?
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ enum NostrKind: Int {
|
|||||||
case channel_meta = 41
|
case channel_meta = 41
|
||||||
case chat = 42
|
case chat = 42
|
||||||
case list = 30000
|
case list = 30000
|
||||||
|
case zap = 9735
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
enum NostrLink {
|
enum NostrLink: Equatable {
|
||||||
case ref(ReferencedId)
|
case ref(ReferencedId)
|
||||||
case filter(NostrFilter)
|
case filter(NostrFilter)
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,24 @@ func decode_universal_link(_ s: String) -> NostrLink? {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decode_nostr_bech32_uri(_ s: String) -> NostrLink? {
|
||||||
|
guard let obj = Bech32Object.parse(s) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch obj {
|
||||||
|
case .nsec(let privkey):
|
||||||
|
guard let pubkey = privkey_to_pubkey(privkey: privkey) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p"))
|
||||||
|
case .npub(let pubkey):
|
||||||
|
return .ref(ReferencedId(ref_id: pubkey, relay_id: nil, key: "p"))
|
||||||
|
case .note(let id):
|
||||||
|
return .ref(ReferencedId(ref_id: id, relay_id: nil, key: "e"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func decode_nostr_uri(_ s: String) -> NostrLink? {
|
func decode_nostr_uri(_ s: String) -> NostrLink? {
|
||||||
if s.starts(with: "https://damus.io/") {
|
if s.starts(with: "https://damus.io/") {
|
||||||
return decode_universal_link(s)
|
return decode_universal_link(s)
|
||||||
@@ -122,5 +140,15 @@ func decode_nostr_uri(_ s: String) -> NostrLink? {
|
|||||||
return .filter(NostrFilter.filter_hashtag([parts[1].lowercased()]))
|
return .filter(NostrFilter.filter_hashtag([parts[1].lowercased()]))
|
||||||
}
|
}
|
||||||
|
|
||||||
return tag_to_refid(parts).map { .ref($0) }
|
if let rid = tag_to_refid(parts) {
|
||||||
|
return .ref(rid)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard parts.count == 1 else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let part = parts[0]
|
||||||
|
|
||||||
|
return decode_nostr_bech32_uri(part)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,20 @@ import UIKit
|
|||||||
class Profiles {
|
class Profiles {
|
||||||
var profiles: [String: TimestampedProfile] = [:]
|
var profiles: [String: TimestampedProfile] = [:]
|
||||||
var validated: [String: NIP05] = [:]
|
var validated: [String: NIP05] = [:]
|
||||||
|
var zappers: [String: String] = [:]
|
||||||
|
|
||||||
func is_validated(_ pk: String) -> NIP05? {
|
func is_validated(_ pk: String) -> NIP05? {
|
||||||
return validated[pk]
|
return validated[pk]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookup_zapper(pubkey: String) -> String? {
|
||||||
|
if let zapper = zappers[pubkey] {
|
||||||
|
return zapper
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func add(id: String, profile: TimestampedProfile) {
|
func add(id: String, profile: TimestampedProfile) {
|
||||||
profiles[id] = profile
|
profiles[id] = profile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func char_to_hex(_ c: UInt8) -> UInt8?
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
func hex_decode(_ str: String) -> [UInt8]?
|
func hex_decode(_ str: String) -> [UInt8]?
|
||||||
{
|
{
|
||||||
if str.count == 0 {
|
if str.count == 0 {
|
||||||
|
|||||||
@@ -7,16 +7,16 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct RelayInfo: Codable {
|
public struct RelayInfo: Codable {
|
||||||
let read: Bool
|
let read: Bool
|
||||||
let write: Bool
|
let write: Bool
|
||||||
|
|
||||||
static let rw = RelayInfo(read: true, write: true)
|
static let rw = RelayInfo(read: true, write: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RelayDescriptor: Codable {
|
public struct RelayDescriptor: Codable {
|
||||||
let url: URL
|
public let url: URL
|
||||||
let info: RelayInfo
|
public let info: RelayInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RelayFlags: Int {
|
enum RelayFlags: Int {
|
||||||
@@ -24,6 +24,30 @@ enum RelayFlags: Int {
|
|||||||
case broken = 1
|
case broken = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Limitations: Codable {
|
||||||
|
let payment_required: Bool?
|
||||||
|
|
||||||
|
static var empty: Limitations {
|
||||||
|
Limitations(payment_required: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RelayMetadata: Codable {
|
||||||
|
let name: String?
|
||||||
|
let description: String?
|
||||||
|
let pubkey: String?
|
||||||
|
let contact: String?
|
||||||
|
let supported_nips: [Int]?
|
||||||
|
let software: String?
|
||||||
|
let version: String?
|
||||||
|
let limitation: Limitations?
|
||||||
|
let payments_url: String?
|
||||||
|
|
||||||
|
var is_paid: Bool {
|
||||||
|
return limitation?.payment_required ?? false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Relay: Identifiable {
|
class Relay: Identifiable {
|
||||||
let descriptor: RelayDescriptor
|
let descriptor: RelayDescriptor
|
||||||
let connection: RelayConnection
|
let connection: RelayConnection
|
||||||
|
|||||||
@@ -13,42 +13,41 @@ enum NostrConnectionEvent {
|
|||||||
case nostr_event(NostrResponse)
|
case nostr_event(NostrResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RelayConnection: WebSocketDelegate {
|
final class RelayConnection: WebSocketDelegate {
|
||||||
var isConnected: Bool = false
|
private(set) var isConnected = false
|
||||||
var isConnecting: Bool = false
|
private(set) var isConnecting = false
|
||||||
var isReconnecting: Bool = false
|
private(set) var isReconnecting = false
|
||||||
var last_connection_attempt: Double = 0
|
|
||||||
var socket: WebSocket
|
private(set) var last_connection_attempt: TimeInterval = 0
|
||||||
var handleEvent: (NostrConnectionEvent) -> ()
|
private lazy var socket = {
|
||||||
let url: URL
|
let req = URLRequest(url: url)
|
||||||
|
let socket = WebSocket(request: req, compressionHandler: .none)
|
||||||
|
socket.delegate = self
|
||||||
|
return socket
|
||||||
|
}()
|
||||||
|
private var handleEvent: (NostrConnectionEvent) -> ()
|
||||||
|
private let url: URL
|
||||||
|
|
||||||
init(url: URL, handleEvent: @escaping (NostrConnectionEvent) -> ()) {
|
init(url: URL, handleEvent: @escaping (NostrConnectionEvent) -> ()) {
|
||||||
self.url = url
|
self.url = url
|
||||||
self.handleEvent = handleEvent
|
self.handleEvent = handleEvent
|
||||||
// just init, we don't actually use this one
|
|
||||||
self.socket = make_websocket(url: url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func reconnect() {
|
func reconnect() {
|
||||||
if self.isConnected {
|
if isConnected {
|
||||||
self.isReconnecting = true
|
isReconnecting = true
|
||||||
self.disconnect()
|
disconnect()
|
||||||
} else {
|
} else {
|
||||||
// we're already disconnected, so just connect
|
// we're already disconnected, so just connect
|
||||||
self.connect(force: true)
|
connect(force: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(force: Bool = false){
|
func connect(force: Bool = false) {
|
||||||
if !force && (self.isConnected || self.isConnecting) {
|
if !force && (isConnected || isConnecting) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var req = URLRequest(url: self.url)
|
|
||||||
req.timeoutInterval = 5
|
|
||||||
socket = make_websocket(url: url)
|
|
||||||
socket.delegate = self
|
|
||||||
|
|
||||||
isConnecting = true
|
isConnecting = true
|
||||||
last_connection_attempt = Date().timeIntervalSince1970
|
last_connection_attempt = Date().timeIntervalSince1970
|
||||||
socket.connect()
|
socket.connect()
|
||||||
@@ -68,7 +67,9 @@ class RelayConnection: WebSocketDelegate {
|
|||||||
|
|
||||||
socket.write(string: req)
|
socket.write(string: req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - WebSocketDelegate
|
||||||
|
|
||||||
func didReceive(event: WebSocketEvent, client: WebSocket) {
|
func didReceive(event: WebSocketEvent, client: WebSocket) {
|
||||||
switch event {
|
switch event {
|
||||||
case .connected:
|
case .connected:
|
||||||
@@ -83,15 +84,25 @@ class RelayConnection: WebSocketDelegate {
|
|||||||
self.connect()
|
self.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
case .cancelled: fallthrough
|
case .cancelled, .error:
|
||||||
case .error:
|
|
||||||
self.isConnecting = false
|
self.isConnecting = false
|
||||||
self.isConnected = false
|
self.isConnected = false
|
||||||
|
|
||||||
case .text(let txt):
|
case .text(let txt):
|
||||||
if let ev = decode_nostr_event(txt: txt) {
|
if txt.count > 2000 {
|
||||||
handleEvent(.nostr_event(ev))
|
DispatchQueue.global(qos: .default).async {
|
||||||
return
|
if let ev = decode_nostr_event(txt: txt) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.handleEvent(.nostr_event(ev))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let ev = decode_nostr_event(txt: txt) {
|
||||||
|
handleEvent(.nostr_event(ev))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print("decode failed for \(txt)")
|
print("decode failed for \(txt)")
|
||||||
@@ -103,7 +114,6 @@ class RelayConnection: WebSocketDelegate {
|
|||||||
|
|
||||||
handleEvent(.ws_event(event))
|
handleEvent(.ws_event(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func make_nostr_req(_ req: NostrRequest) -> String? {
|
func make_nostr_req(_ req: NostrRequest) -> String? {
|
||||||
@@ -118,17 +128,16 @@ func make_nostr_req(_ req: NostrRequest) -> String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func make_nostr_push_event(ev: NostrEvent) -> String? {
|
func make_nostr_push_event(ev: NostrEvent) -> String? {
|
||||||
let encoder = JSONEncoder()
|
guard let event = encode_json(ev) else {
|
||||||
encoder.outputFormatting = .withoutEscapingSlashes
|
return nil
|
||||||
let event_data = try! encoder.encode(ev)
|
}
|
||||||
let event = String(decoding: event_data, as: UTF8.self)
|
|
||||||
let encoded = "[\"EVENT\",\(event)]"
|
let encoded = "[\"EVENT\",\(event)]"
|
||||||
print(encoded)
|
print(encoded)
|
||||||
return encoded
|
return encoded
|
||||||
}
|
}
|
||||||
|
|
||||||
func make_nostr_unsubscribe_req(_ sub_id: String) -> String? {
|
func make_nostr_unsubscribe_req(_ sub_id: String) -> String? {
|
||||||
return "[\"CLOSE\",\"\(sub_id)\"]"
|
"[\"CLOSE\",\"\(sub_id)\"]"
|
||||||
}
|
}
|
||||||
|
|
||||||
func make_nostr_subscription_req(_ filters: [NostrFilter], sub_id: String) -> String? {
|
func make_nostr_subscription_req(_ filters: [NostrFilter], sub_id: String) -> String? {
|
||||||
@@ -145,10 +154,3 @@ func make_nostr_subscription_req(_ filters: [NostrFilter], sub_id: String) -> St
|
|||||||
req += "]"
|
req += "]"
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
func make_websocket(url: URL) -> WebSocket {
|
|
||||||
let req = URLRequest(url: url)
|
|
||||||
//req.setValue("chat,superchat", forHTTPHeaderField: "Sec-WebSocket-Protocol")
|
|
||||||
return WebSocket(request: req, compressionHandler: .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class RelayPool {
|
|||||||
var relays: [Relay] = []
|
var relays: [Relay] = []
|
||||||
var handlers: [RelayHandler] = []
|
var handlers: [RelayHandler] = []
|
||||||
var request_queue: [QueuedRequest] = []
|
var request_queue: [QueuedRequest] = []
|
||||||
|
var seen: Set<String> = Set()
|
||||||
|
var counts: [String: UInt64] = [:]
|
||||||
|
|
||||||
var descriptors: [RelayDescriptor] {
|
var descriptors: [RelayDescriptor] {
|
||||||
relays.map { $0.descriptor }
|
relays.map { $0.descriptor }
|
||||||
@@ -149,9 +151,9 @@ class RelayPool {
|
|||||||
self.send(.unsubscribe(sub_id), to: to)
|
self.send(.unsubscribe(sub_id), to: to)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) {
|
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> (), to: [String]? = nil) {
|
||||||
register_handler(sub_id: sub_id, handler: handler)
|
register_handler(sub_id: sub_id, handler: handler)
|
||||||
send(.subscribe(.init(filters: filters, sub_id: sub_id)))
|
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]?, handler: @escaping (String, NostrConnectionEvent) -> ()) {
|
func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]?, handler: @escaping (String, NostrConnectionEvent) -> ()) {
|
||||||
@@ -193,27 +195,13 @@ class RelayPool {
|
|||||||
relay.connection.send(req)
|
relay.connection.send(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_relays(_ ids: [String]) -> [Relay] {
|
func get_relays(_ ids: [String]) -> [Relay] {
|
||||||
var relays: [Relay] = []
|
relays.filter { ids.contains($0.id) }
|
||||||
|
|
||||||
for id in ids {
|
|
||||||
if let relay = get_relay(id) {
|
|
||||||
relays.append(relay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return relays
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func get_relay(_ id: String) -> Relay? {
|
func get_relay(_ id: String) -> Relay? {
|
||||||
for relay in relays {
|
relays.first(where: { $0.id == id })
|
||||||
if relay.id == id {
|
|
||||||
return relay
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func record_last_pong(relay_id: String, event: NostrConnectionEvent) {
|
func record_last_pong(relay_id: String, event: NostrConnectionEvent) {
|
||||||
@@ -241,8 +229,25 @@ class RelayPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func record_seen(relay_id: String, event: NostrConnectionEvent) {
|
||||||
|
if case .nostr_event(let ev) = event {
|
||||||
|
if case .event(_, let nev) = ev {
|
||||||
|
let k = relay_id + nev.id
|
||||||
|
if !seen.contains(k) {
|
||||||
|
seen.insert(k)
|
||||||
|
if counts[relay_id] == nil {
|
||||||
|
counts[relay_id] = 1
|
||||||
|
} else {
|
||||||
|
counts[relay_id] = (counts[relay_id] ?? 0) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
||||||
record_last_pong(relay_id: relay_id, event: event)
|
record_last_pong(relay_id: relay_id, event: event)
|
||||||
|
record_seen(relay_id: relay_id, event: event)
|
||||||
|
|
||||||
// run req queue when we reconnect
|
// run req queue when we reconnect
|
||||||
if case .ws_event(let ws) = event {
|
if case .ws_event(let ws) = event {
|
||||||
|
|||||||