Compare commits

..

1 Commits

Author SHA1 Message Date
tyiu c5085a3026 Fix bug with copying user pubkey 2023-01-28 22:22:26 -05:00
149 changed files with 2002 additions and 19942 deletions
+4 -30
View File
@@ -1,33 +1,8 @@
## [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 ## [1.0.0-12] - 2023-01-28
### Added ### Added
- Added Arabic and Portuguese translations (Barodane, Antonio Chagas) - Added arabic and portugese translations (William Casarin)
- Add QRCode view for sharing your pubkey (ericholguin) - Add QRCode view for sharing your pubkey (ericholguin)
- Added nostr: uri handling (William Casarin) - Added nostr: uri handling (William Casarin)
@@ -54,7 +29,7 @@
### 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 (Nicolò Carcagnì, Solobalbo, Gregor, Peter Gerstbach, SYX) - Translations for it_IT, it_CH, fr_FR, de_DE, de_AT and lv_LV (William Casarin)
- 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)
@@ -81,7 +56,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 (Gregor, Peter Gerstbach, Taylan Benli, Solobalbo) - Translations for de_AT, de_DE, tr_TR, fr_FR (William Casarin)
- Add DM Message Requests (William Casarin) - Add DM Message Requests (William Casarin)
@@ -101,7 +76,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 (Nicolás Valencia) - Add Latin American Spanish translations (William Casarin)
- Added SVG profile picture support (OlegAba) - Added SVG profile picture support (OlegAba)
@@ -515,4 +490,3 @@
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2 [0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2
+6 -37
View File
@@ -1,4 +1,3 @@
[![Run Test Suite](https://github.com/damus-io/damus/actions/workflows/run-tests.yaml/badge.svg?branch=master)](https://github.com/damus-io/damus/actions/workflows/run-tests.yaml)
# damus # damus
@@ -26,7 +25,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 the iOS App Store: https://apps.apple.com/ca/app/damus/id1628663131 1) Get the Damus app on TestFlight: https://testflight.apple.com/join/CLwjLxWl
#### ⚙️ 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 "+".
@@ -49,7 +48,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
@@ -92,41 +91,15 @@ damus implements the following [Nostr Implementation Possibilities][nips]
## Contributing ## Contributing
Contributors welcome! Contributors welcome! [Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on github as well.
### 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
### Translations ## git log bot
Translators welcome! Join the [Transifex][transifex] project. npub1fjtdwclt9lspjy8huu3qklr7eklp5uq90u6yh8mec290pqxraccqlufnas
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. ### Awards
[transifex]: https://explore.transifex.com/damus/damus-ios/
#### Export Source Translations
If user-facing strings have been added or changed, please export them for translation as part of your pull request or commit by running:
```zsh
./devtools/export-source-translation.sh
```
This command will export source translations to `translations/en-US.xcloc/Localized Contents/en-US.xliff`, which the Transifex integration will read from the `master` branch and allow translators to translate those strings.
#### Import Translations
Once 100% of strings have been translated for a given locale, Transifex will open up a pull request with the `translations/<locale>.xliff` file changed. Currently, it must be manually imported into the project before merging the pull request by running:
```zsh
./devtools/import-translation.sh <locale_code_in_snake_case>
```
### Awards
There may be nostr badges awarded for contributors in the future... :) There may be nostr badges awarded for contributors in the future... :)
@@ -134,7 +107,3 @@ First contributors:
1. @randymcmillan 1. @randymcmillan
2. @jcarucci27 2. @jcarucci27
### git log bot
npub1fjtdwclt9lspjy8huu3qklr7eklp5uq90u6yh8mec290pqxraccqlufnas
@@ -16,11 +16,6 @@
<note>Bundle name</note> <note>Bundle name</note>
</trans-unit> </trans-unit>
<trans-unit id="NSFaceIDUsageDescription" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Privacy - Face ID Usage Description</note>
</trans-unit>
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve"> <trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
<source>Granting Damus access to your photos allows you to save images.</source> <source>Granting Damus access to your photos allows you to save images.</source>
<target>السماح لدامُس بالوصول إلى الصور يتيح لك حفظ الصور</target> <target>السماح لدامُس بالوصول إلى الصور يتيح لك حفظ الصور</target>
@@ -43,14 +38,15 @@
<source>%@</source> <source>%@</source>
<target>%@</target> <target>%@</target>
<note>Abbreviated version of a nostr public key.</note> <note>Amount of time that has passed since reply quote event occurred.
Abbreviated version of a nostr public key.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ %@" xml:space="preserve"> <trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source> <source>%@ %@</source>
<target>%@ %@</target> <target>%@ %@</target>
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'. <note>Sentence composed of 2 variables to describe how many tip payments there are on a post. In source English, the first variable is the number of tip payments, and the second variable is 'Tip' or 'Tips'.
Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.</note> Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ has been blocked" xml:space="preserve"> <trans-unit id="%@ has been blocked" xml:space="preserve">
<source>%@ has been blocked</source> <source>%@ has been blocked</source>
@@ -80,8 +76,8 @@ Sentence composed of 2 variables to describe how many relay servers a user is co
<source>%lld</source> <source>%lld</source>
<target>%lld</target> <target>%lld</target>
<note>Number of zap payments on a post. <note>Number of tip payments on a post.
Number of relay servers a user is connected.</note> Number of profiles a user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld/%lld" xml:space="preserve"> <trans-unit id="%lld/%lld" xml:space="preserve">
<source>%lld/%lld</source> <source>%lld/%lld</source>
@@ -95,9 +91,9 @@ Number of relay servers a user is connected.</note>
<note>Description of how the nip05 identifier would be used for verification.</note> <note>Description of how the nip05 identifier would be used for verification.</note>
</trans-unit> </trans-unit>
<trans-unit id="'%@' is an invalid NIP-05 identifier. It should look like an email." xml:space="preserve"> <trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
<source>'%@' is an invalid NIP-05 identifier. It should look like an email.</source> <source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
<target>'%@' عنوان NIP-05 غير صالح. من المفترض أن يشابه صيغة الايميل مثل المثال الموضح.</target> <target>'%@' العنوان غير صالح. من المفترض أن يشابه صيغة الايميل مثل المثال الموضح.</target>
<note>Description of why the nip05 identifier is invalid.</note> <note>Description of why the nip05 identifier is invalid.</note>
</trans-unit> </trans-unit>
@@ -125,17 +121,6 @@ Number of relay servers a user is connected.</note>
<note>Prefix character to username.</note> <note>Prefix character to username.</note>
</trans-unit> </trans-unit>
<trans-unit id="API Key (optional)" xml:space="preserve">
<source>API Key (optional)</source>
<target>مفتاح API (اختياري)</target>
<note>Prompt for optional entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="API Key (required)" xml:space="preserve">
<source>API Key (required)</source>
<note>Prompt for required entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="About" xml:space="preserve"> <trans-unit id="About" xml:space="preserve">
<source>About</source> <source>About</source>
<target>النبذة التعريفية</target> <target>النبذة التعريفية</target>
@@ -191,14 +176,9 @@ Number of relay servers a user is connected.</note>
<note>Button label to re-add all original participants as profiles to reply to in a note</note> <note>Button label to re-add all original participants as profiles to reply to in a note</note>
</trans-unit> </trans-unit>
<trans-unit id="Admin" xml:space="preserve">
<source>Admin</source>
<note>Label to display relay contact user.</note>
</trans-unit>
<trans-unit id="Any" xml:space="preserve"> <trans-unit id="Any" xml:space="preserve">
<source>Any</source> <source>Any</source>
<target>أي شيء</target> <target>كم</target>
<note>Any amount of sats</note> <note>Any amount of sats</note>
</trans-unit> </trans-unit>
@@ -276,11 +256,6 @@ Number of relay servers a user is connected.</note>
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note> <note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Boosts" xml:space="preserve">
<source>Boosts</source>
<note>Accessibility label for boosts button</note>
</trans-unit>
<trans-unit id="Breez" xml:space="preserve"> <trans-unit id="Breez" xml:space="preserve">
<source>Breez</source> <source>Breez</source>
<target>Breez</target> <target>Breez</target>
@@ -302,7 +277,6 @@ Number of relay servers a user is connected.</note>
Button to cancel out of posting a note. Button to cancel out of posting a note.
Button to cancel out of reposting a post. Button to cancel out of reposting a post.
Button to cancel out of view adding user inputted relay. Button to cancel out of view adding user inputted relay.
Cancel deleting the user.
Cancel out of logging out the user.</note> Cancel out of logging out the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cash App" xml:space="preserve"> <trans-unit id="Cash App" xml:space="preserve">
@@ -329,11 +303,6 @@ Number of relay servers a user is connected.</note>
<note>Section title for clearing cached data.</note> <note>Section title for clearing cached data.</note>
</trans-unit> </trans-unit>
<trans-unit id="Contact" xml:space="preserve">
<source>Contact</source>
<note>Label to display relay contact information.</note>
</trans-unit>
<trans-unit id="Copied" xml:space="preserve"> <trans-unit id="Copied" xml:space="preserve">
<source>Copied</source> <source>Copied</source>
<target>تم النسخ</target> <target>تم النسخ</target>
@@ -437,11 +406,11 @@ Number of relay servers a user is connected.</note>
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note> <note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
</trans-unit> </trans-unit>
<trans-unit id="Custom" xml:space="preserve"> <trans-unit id="DM Type" xml:space="preserve">
<source>Custom</source> <source>DM Type</source>
<target>مخصص</target> <target>خيار العرض</target>
<note>Dropdown option for selecting a custom translation server.</note> <note>DM selector for seeing either DMs or message requests, which are messages that have not been responded to yet. DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="DMs" xml:space="preserve"> <trans-unit id="DMs" xml:space="preserve">
<source>DMs</source> <source>DMs</source>
@@ -456,11 +425,6 @@ Number of relay servers a user is connected.</note>
<note>Name of the app, shown on the first screen when user is not logged in.</note> <note>Name of the app, shown on the first screen when user is not logged in.</note>
</trans-unit> </trans-unit>
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
<source>DeepL (Proprietary, Higher Accuracy)</source>
<note>Dropdown option for selecting DeepL as the translation service.</note>
</trans-unit>
<trans-unit id="Default Wallet" xml:space="preserve"> <trans-unit id="Default Wallet" xml:space="preserve">
<source>Default Wallet</source> <source>Default Wallet</source>
<target>المحفظة الافتراضية</target> <target>المحفظة الافتراضية</target>
@@ -471,28 +435,8 @@ Number of relay servers a user is connected.</note>
<source>Delete</source> <source>Delete</source>
<target>حذف</target> <target>حذف</target>
<note>Button for deleting the users account. <note>Button to delete a relay server that the user connects to.
Button to delete a relay server that the user connects to. Button to remove a user from their blocklist.</note>
Button to remove a user from their blocklist.
Section title for deleting the user</note>
</trans-unit>
<trans-unit id="Delete Account" xml:space="preserve">
<source>Delete Account</source>
<target>حذف الحساب</target>
<note>Alert for deleting the users account.
Button to delete the user's account.</note>
</trans-unit>
<trans-unit id="Deleted Account" xml:space="preserve">
<source>Deleted Account</source>
<target>حذف الحساب</target>
<note>Alert message to indicate this is a deleted account</note>
</trans-unit>
<trans-unit id="Description" xml:space="preserve">
<source>Description</source>
<note>Label to display relay description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Dismiss" xml:space="preserve"> <trans-unit id="Dismiss" xml:space="preserve">
<source>Dismiss</source> <source>Dismiss</source>
@@ -554,22 +498,18 @@ Number of relay servers a user is connected.</note>
<note>Error message indicating why saving keys failed.</note> <note>Error message indicating why saving keys failed.</note>
</trans-unit> </trans-unit>
<trans-unit id="Filter State" xml:space="preserve">
<source>Filter State</source>
<target>اختيار المحتوى</target>
<note>Filter state for seeing either only posts, or posts &amp; replies.</note>
</trans-unit>
<trans-unit id="Follow" xml:space="preserve"> <trans-unit id="Follow" xml:space="preserve">
<source>Follow</source> <source>Follow</source>
<target>متابعة</target> <target>متابعة</target>
<note>Button to follow a user.</note> <note>Button to follow a user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follow Back" xml:space="preserve">
<source>Follow Back</source>
<note>Button to follow a user back.</note>
</trans-unit>
<trans-unit id="Follow me on nostr" xml:space="preserve">
<source>Follow me on nostr</source>
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve"> <trans-unit id="Followers" xml:space="preserve">
<source>Followers</source> <source>Followers</source>
<target>المتابعون</target> <target>المتابعون</target>
@@ -595,21 +535,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note> <note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follows you" xml:space="preserve">
<source>Follows you</source>
<note>Text to indicate that a user is following your profile.</note>
</trans-unit>
<trans-unit id="Free" xml:space="preserve">
<source>Free</source>
<note>Dropdown option for selecting Free plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Get API Key" xml:space="preserve">
<source>Get API Key</source>
<note>Button to navigate to DeepL website to get a translation API key.</note>
</trans-unit>
<trans-unit id="Global" xml:space="preserve"> <trans-unit id="Global" xml:space="preserve">
<source>Global</source> <source>Global</source>
<target>عام</target> <target>عام</target>
@@ -628,19 +553,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation link to go to profile.</note> <note>Navigation link to go to profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Hide" xml:space="preserve">
<source>Hide</source>
<target>اخفاء</target>
<note>Button to hide a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Hide API Key" xml:space="preserve">
<source>Hide API Key</source>
<target>اخفاء مفتاح API</target>
<note>Button to hide the DeepL translation API key.
Button to hide the LibreTranslate server API key.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve"> <trans-unit id="Home" xml:space="preserve">
<source>Home</source> <source>Home</source>
<target>الرئيسية</target> <target>الرئيسية</target>
@@ -683,11 +595,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Button to complete account creation and start using the app.</note> <note>Button to complete account creation and start using the app.</note>
</trans-unit> </trans-unit>
<trans-unit id="LibreTranslate (Open Source)" xml:space="preserve">
<source>LibreTranslate (Open Source)</source>
<note>Dropdown option for selecting LibreTranslate as the translation service.</note>
</trans-unit>
<trans-unit id="Lightning Address or LNURL" xml:space="preserve"> <trans-unit id="Lightning Address or LNURL" xml:space="preserve">
<source>Lightning Address or LNURL</source> <source>Lightning Address or LNURL</source>
<target>عنوان البرق أو LNURL</target> <target>عنوان البرق أو LNURL</target>
@@ -700,16 +607,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Indicates that the view is for paying a Lightning invoice.</note> <note>Indicates that the view is for paying a Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="Like" xml:space="preserve">
<source>Like</source>
<note>Accessibility Label for Like button</note>
</trans-unit>
<trans-unit id="Local authentication to access private key" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Face ID usage description shown when trying to access private key</note>
</trans-unit>
<trans-unit id="Local default" xml:space="preserve"> <trans-unit id="Local default" xml:space="preserve">
<source>Local default</source> <source>Local default</source>
<target>الاختيار</target> <target>الاختيار</target>
@@ -729,7 +626,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Alert for logging out the user. <note>Alert for logging out the user.
Button for logging out the user. Button for logging out the user.
Button to close the alert that informs that the current account has been deleted.</note> Button to logout the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve"> <trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source> <source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
@@ -749,30 +646,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for NIP-05 Verification section of user profile form.</note> <note>Label for NIP-05 Verification section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<target>لا</target>
<note>Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve"> <trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve">
<source>No block list found, create a new one? This will overwrite any previous block lists.</source> <source>No block list found, create a new one? This will overwrite any previous block lists.</source>
<target>لم نعثر على قائمة حظر. هل تريد انشاء قائمة جديدة؟ سيتم استبدال أي قوائم سابقة ان وجدت</target> <target>لم نعثر على قائمة حظر. هل تريد انشاء قائمة جديدة؟ سيتم استبدال أي قوائم سابقة ان وجدت</target>
<note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note> <note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note>
</trans-unit> </trans-unit>
<trans-unit id="None" xml:space="preserve">
<source>None</source>
<target>لا اختيار</target>
<note>Dropdown option for selecting no translation service.</note>
</trans-unit>
<trans-unit id="Note contains &quot;nsec1&quot; private key. Are you sure?" xml:space="preserve">
<source>Note contains &quot;nsec1&quot; private key. Are you sure?</source>
<target>يحتوي المنشور على مفتاح خاص &quot;nsec1&quot;. هل أنت متأكد؟</target>
<note>Alert user that they might be attempting to paste a private key and ask them to confirm.</note>
</trans-unit>
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve"> <trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
<source>Nothing to see here. Check back later!</source> <source>Nothing to see here. Check back later!</source>
<target>لا جديد في هذه اللحظة. يرجى المعاودة لاحقا!</target> <target>لا جديد في هذه اللحظة. يرجى المعاودة لاحقا!</target>
@@ -809,23 +688,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Dropdown option label for Lightning wallet, Phoenix.</note> <note>Dropdown option label for Lightning wallet, Phoenix.</note>
</trans-unit> </trans-unit>
<trans-unit id="Plan" xml:space="preserve">
<source>Plan</source>
<note>Prompt selection of DeepL subscription plan to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Post" xml:space="preserve"> <trans-unit id="Post" xml:space="preserve">
<source>Post</source> <source>Post</source>
<target>انشر</target> <target>انشر</target>
<note>Button to post a note.</note> <note>Button to post a note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Post from a user you've blocked" xml:space="preserve">
<source>Post from a user you've blocked</source>
<target>منشور لمستخدم محظور</target>
<note>Text to indicate that what is being shown is a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve"> <trans-unit id="Posts" xml:space="preserve">
<source>Posts</source> <source>Posts</source>
<target>المنشورات</target> <target>المنشورات</target>
@@ -850,11 +718,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Title of the secure field that holds the user's private key.</note> <note>Title of the secure field that holds the user's private key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Pro" xml:space="preserve">
<source>Pro</source>
<note>Dropdown option for selecting Pro plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Profile" xml:space="preserve"> <trans-unit id="Profile" xml:space="preserve">
<source>Profile</source> <source>Profile</source>
<target>الملف الشخصي</target> <target>الملف الشخصي</target>
@@ -913,15 +776,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Relay</source> <source>Relay</source>
<target>موصّل</target> <target>موصّل</target>
<note>Label to display relay address. <note>Text field for relay server. Used for testing purposes.</note>
Text field for relay server. Used for testing purposes.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays" xml:space="preserve"> <trans-unit id="Relays" xml:space="preserve">
<source>Relays</source> <source>Relays</source>
<target>موصّلات</target> <target>موصّلات</target>
<note>Sidebar menu label for Relay servers view <note>Sidebar menu label for Relay servers view</note>
Sidebar menu label for Relays view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve"> <trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve">
<source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source> <source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source>
@@ -935,11 +796,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Button label to remove all participants from a note reply.</note> <note>Button label to remove all participants from a note reply.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<note>Accessibility label for reply button</note>
</trans-unit>
<trans-unit id="Reply to self" xml:space="preserve"> <trans-unit id="Reply to self" xml:space="preserve">
<source>Reply to self</source> <source>Reply to self</source>
<target>رد على منشوره السابق</target> <target>رد على منشوره السابق</target>
@@ -1002,6 +858,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note> <note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>تسجيل الخروج</target>
<note>Section title for resetting the user</note>
</trans-unit>
<trans-unit id="Retry" xml:space="preserve"> <trans-unit id="Retry" xml:space="preserve">
<source>Retry</source> <source>Retry</source>
<target>اعادة المحاولة</target> <target>اعادة المحاولة</target>
@@ -1032,11 +894,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Context menu option to save an image.</note> <note>Context menu option to save an image.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan the code" xml:space="preserve">
<source>Scan the code</source>
<note>Text on QR code view to prompt viewer to scan the QR code on screen with their device camera.</note>
</trans-unit>
<trans-unit id="Search hashtag: #%@" xml:space="preserve"> <trans-unit id="Search hashtag: #%@" xml:space="preserve">
<source>Search hashtag: #%@</source> <source>Search hashtag: #%@</source>
<target>البحث عن وسم: #%@</target> <target>البحث عن وسم: #%@</target>
@@ -1073,17 +930,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text prompt for user to send a message to the other user.</note> <note>Text prompt for user to send a message to the other user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<target>خادم</target>
<note>Prompt selection of LibreTranslate server to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Service" xml:space="preserve">
<source>Service</source>
<note>Prompt selection of translation service provider.</note>
</trans-unit>
<trans-unit id="Settings" xml:space="preserve"> <trans-unit id="Settings" xml:space="preserve">
<source>Settings</source> <source>Settings</source>
<target>الاعدادات</target> <target>الاعدادات</target>
@@ -1095,23 +941,14 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Share</source> <source>Share</source>
<target>مشاركة</target> <target>مشاركة</target>
<note>Button to share a post <note>Button to share an image.
Button to share an image.
Button to share the link to a profile.</note> Button to share the link to a profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show" xml:space="preserve"> <trans-unit id="Show" xml:space="preserve">
<source>Show</source> <source>Show</source>
<target>عرض</target> <target>عرض</target>
<note>Button to show a post from a user who has been blocked. <note>Toggle to show or hide user's secret account login key.</note>
Toggle to show or hide user's secret account login key.</note>
</trans-unit>
<trans-unit id="Show API Key" xml:space="preserve">
<source>Show API Key</source>
<target>عرض مفتاح API</target>
<note>Button to show the DeepL translation API key.
Button to show the LibreTranslate server API key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve"> <trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source> <source>Show wallet selector</source>
@@ -1121,26 +958,16 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="Sign out" xml:space="preserve"> <trans-unit id="Sign out" xml:space="preserve">
<source>Sign out</source> <source>Sign out</source>
<target>خروج</target> <target>تسجيل الخروج</target>
<note>Sidebar menu label to sign out of the account.</note> <note>Sidebar menu label to sign out of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Software" xml:space="preserve">
<source>Software</source>
<note>Label to display relay software.</note>
</trans-unit>
<trans-unit id="Strike" xml:space="preserve"> <trans-unit id="Strike" xml:space="preserve">
<source>Strike</source> <source>Strike</source>
<target>Strike</target> <target>Strike</target>
<note>Dropdown option label for Lightning wallet, Strike.</note> <note>Dropdown option label for Lightning wallet, Strike.</note>
</trans-unit> </trans-unit>
<trans-unit id="Supported NIPs" xml:space="preserve">
<source>Supported NIPs</source>
<note>Label to display relay's supported NIPs.</note>
</trans-unit>
<trans-unit id="Thanks!" xml:space="preserve"> <trans-unit id="Thanks!" xml:space="preserve">
<source>Thanks!</source> <source>Thanks!</source>
<target>شكرا!</target> <target>شكرا!</target>
@@ -1184,47 +1011,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation bar title for note thread. <note>Navigation bar title for note thread.
Navigation bar title for threaded event detail view.</note> Navigation bar title for threaded event detail view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Translate Note" xml:space="preserve">
<source>Translate Note</source>
<target>ترجم المنشور</target>
<note>Button to translate note from different language.</note>
</trans-unit>
<trans-unit id="Translated from (lang)" xml:space="preserve">
<source>Translated from (lang)</source>
<target>مُترجَم من (lang)</target>
<note>Button to indicate that the note has been translated from a different language.</note>
</trans-unit>
<trans-unit id="Translating from (lang)..." xml:space="preserve">
<source>Translating from (lang)...</source>
<target>الترجمة من (lang)...</target>
<note>Button to indicate that the note is in the process of being translated from a different language.</note>
</trans-unit>
<trans-unit id="Translations" xml:space="preserve">
<source>Translations</source>
<note>Section title for selecting the translation service.</note>
</trans-unit>
<trans-unit id="Type DELETE to delete" xml:space="preserve">
<source>Type DELETE to delete</source>
<target>اكتب DELETE لتأكيد الحذف</target>
<note>Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should.</note>
</trans-unit>
<trans-unit id="Type your post here..." xml:space="preserve"> <trans-unit id="Type your post here..." xml:space="preserve">
<source>Type your post here...</source> <source>Type your post here...</source>
<target>اكتب المنشور هنا...</target> <target>اكتب المنشور هنا...</target>
<note>Text box prompt to ask user to type their post.</note> <note>Text box prompt to ask user to type their post.</note>
</trans-unit> </trans-unit>
<trans-unit id="URL" xml:space="preserve">
<source>URL</source>
<target>رابط</target>
<note>Example URL to LibreTranslate server</note>
</trans-unit>
<trans-unit id="Unfollow" xml:space="preserve"> <trans-unit id="Unfollow" xml:space="preserve">
<source>Unfollow</source> <source>Unfollow</source>
<target>الغاء المتابعة</target> <target>الغاء المتابعة</target>
@@ -1253,7 +1045,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>User blocked</source> <source>User blocked</source>
<target>الحساب محظور</target> <target>الحساب محظور</target>
<note>Alert message to indicate the user has been blocked</note> <note>Alert message to indicate</note>
</trans-unit> </trans-unit>
<trans-unit id="User has been blocked" xml:space="preserve"> <trans-unit id="User has been blocked" xml:space="preserve">
<source>User has been blocked</source> <source>User has been blocked</source>
@@ -1268,29 +1060,24 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for Username section of user profile form. <note>Label for Username section of user profile form.
Label to prompt username entry.</note> Label to prompt username entry.</note>
</trans-unit> </trans-unit>
<trans-unit id="Version" xml:space="preserve">
<source>Version</source>
<note>Label to display relay software version.</note>
</trans-unit>
<trans-unit id="Wallet" xml:space="preserve"> <trans-unit id="Wallet" xml:space="preserve">
<source>Wallet</source> <source>Wallet</source>
<target>المحفظة</target> <target>المحفظة</target>
<note>Sidebar menu label for Wallet view.</note> <note>Sidebar menu label for Wallet view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
<source>Wallet Of Satoshi</source>
<target>Wallet Of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
</trans-unit>
<trans-unit id="Wallet Selector" xml:space="preserve"> <trans-unit id="Wallet Selector" xml:space="preserve">
<source>Wallet Selector</source> <source>Wallet Selector</source>
<target>تفضيلات المحفظة</target> <target>تفضيلات المحفظة</target>
<note>Section title for selection of wallet.</note> <note>Section title for selection of wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet of Satoshi" xml:space="preserve">
<source>Wallet of Satoshi</source>
<target>Wallet of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet of Satoshi.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve"> <trans-unit id="Website" xml:space="preserve">
<source>Website</source> <source>Website</source>
<target>موقع الكتروني</target> <target>موقع الكتروني</target>
@@ -1321,12 +1108,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text of button that confirms to overwrite the existing mutelist.</note> <note>Text of button that confirms to overwrite the existing mutelist.</note>
</trans-unit> </trans-unit>
<trans-unit id="Yes, Post with Private Key" xml:space="preserve">
<source>Yes, Post with Private Key</source>
<target>نعم، انشر المفتاح الخاص</target>
<note>Button to proceed with posting a note even though it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve"> <trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source> <source>Your Name</source>
<target>الاسم</target> <target>الاسم</target>
@@ -1339,11 +1120,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Footer text to inform user what will happen when the report is submitted.</note> <note>Footer text to inform user what will happen when the report is submitted.</note>
</trans-unit> </trans-unit>
<trans-unit id="Zap" xml:space="preserve">
<source>Zap</source>
<note>Accessibility label for zap button</note>
</trans-unit>
<trans-unit id="Zebedee" xml:space="preserve"> <trans-unit id="Zebedee" xml:space="preserve">
<source>Zebedee</source> <source>Zebedee</source>
<target>Zebedee</target> <target>Zebedee</target>
@@ -1452,6 +1228,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats. (Key in .stringsdict)</note> <note>Amount of sats. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="tips_count" translate="no" xml:space="preserve">
<source>tips_count</source>
<target>tips_count</target>
<note>Part of a larger sentence to describe how many tip payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="u{00A0}" xml:space="preserve"> <trans-unit id="u{00A0}" xml:space="preserve">
<source>u{00A0}</source> <source>u{00A0}</source>
<target>u{00A0}</target> <target>u{00A0}</target>
@@ -1470,18 +1252,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note> <note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
</trans-unit> </trans-unit>
<trans-unit id="zaps_count" translate="no" xml:space="preserve">
<source>zaps_count</source>
<target>zaps_count</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="⚡️ %@" xml:space="preserve">
<source>⚡️ %@</source>
<target>⚡️ %@</target>
<note>Text indicating the zap amount. i.e. number of satoshis that were tipped to a user</note>
</trans-unit>
</body> </body>
</file> </file>
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="ar" datatype="plaintext"> <file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="ar" datatype="plaintext">
@@ -1641,21 +1411,23 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats.</note> <note>Amount of sats.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@ZAPS@</source> <source>%#@TIPS@</source>
<target>%#@ZAPS@</target> <target>%#@TIPS@</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/one:dict/:string" xml:space="preserve">
<source>Zap</source> <source>Tip</source>
<target>اكرامية</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/other:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/other:dict/:string" xml:space="preserve">
<source>Zaps</source> <source>Tips</source>
<target>اكراميات</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>
@@ -15,11 +15,6 @@
<target>damus</target> <target>damus</target>
<note>Bundle name</note> <note>Bundle name</note>
</trans-unit> </trans-unit>
<trans-unit id="NSFaceIDUsageDescription" xml:space="preserve">
<source>Local authentication to access private key</source>
<target>Local authentication to access private key</target>
<note>Privacy - Face ID Usage Description</note>
</trans-unit>
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve"> <trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
<source>Granting Damus access to your photos allows you to save images.</source> <source>Granting Damus access to your photos allows you to save images.</source>
<target>Granting Damus access to your photos allows you to save images.</target> <target>Granting Damus access to your photos allows you to save images.</target>
@@ -40,13 +35,14 @@
<trans-unit id="%@" xml:space="preserve"> <trans-unit id="%@" xml:space="preserve">
<source>%@</source> <source>%@</source>
<target>%@</target> <target>%@</target>
<note>Abbreviated version of a nostr public key.</note> <note>Amount of time that has passed since reply quote event occurred.
Abbreviated version of a nostr public key.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ %@" xml:space="preserve"> <trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source> <source>%@ %@</source>
<target>%@ %@</target> <target>%@ %@</target>
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'. <note>Sentence composed of 2 variables to describe how many tip payments there are on a post. In source English, the first variable is the number of tip payments, and the second variable is 'Tip' or 'Tips'.
Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.</note> Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ has been blocked" xml:space="preserve"> <trans-unit id="%@ has been blocked" xml:space="preserve">
<source>%@ has been blocked</source> <source>%@ has been blocked</source>
@@ -71,8 +67,8 @@ Sentence composed of 2 variables to describe how many relay servers a user is co
<trans-unit id="%lld" xml:space="preserve"> <trans-unit id="%lld" xml:space="preserve">
<source>%lld</source> <source>%lld</source>
<target>%lld</target> <target>%lld</target>
<note>Number of zap payments on a post. <note>Number of tip payments on a post.
Number of relay servers a user is connected.</note> Number of profiles a user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld/%lld" xml:space="preserve"> <trans-unit id="%lld/%lld" xml:space="preserve">
<source>%lld/%lld</source> <source>%lld/%lld</source>
@@ -84,9 +80,9 @@ Number of relay servers a user is connected.</note>
<target>'%@' at '%@' will be used for verification</target> <target>'%@' at '%@' will be used for verification</target>
<note>Description of how the nip05 identifier would be used for verification.</note> <note>Description of how the nip05 identifier would be used for verification.</note>
</trans-unit> </trans-unit>
<trans-unit id="'%@' is an invalid NIP-05 identifier. It should look like an email." xml:space="preserve"> <trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
<source>'%@' is an invalid NIP-05 identifier. It should look like an email.</source> <source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
<target>'%@' is an invalid NIP-05 identifier. It should look like an email.</target> <target>'%@' is an invalid nip05 identifier. It should look like an email.</target>
<note>Description of why the nip05 identifier is invalid.</note> <note>Description of why the nip05 identifier is invalid.</note>
</trans-unit> </trans-unit>
<trans-unit id="(Profile.displayName(profile: profile, pubkey: whos))'s Followers" xml:space="preserve"> <trans-unit id="(Profile.displayName(profile: profile, pubkey: whos))'s Followers" xml:space="preserve">
@@ -109,16 +105,6 @@ Number of relay servers a user is connected.</note>
<target>@</target> <target>@</target>
<note>Prefix character to username.</note> <note>Prefix character to username.</note>
</trans-unit> </trans-unit>
<trans-unit id="API Key (optional)" xml:space="preserve">
<source>API Key (optional)</source>
<target>API Key (optional)</target>
<note>Prompt for optional entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="API Key (required)" xml:space="preserve">
<source>API Key (required)</source>
<target>API Key (required)</target>
<note>Prompt for required entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="About" xml:space="preserve"> <trans-unit id="About" xml:space="preserve">
<source>About</source> <source>About</source>
<target>About</target> <target>About</target>
@@ -165,11 +151,6 @@ Number of relay servers a user is connected.</note>
<target>Add all</target> <target>Add all</target>
<note>Button label to re-add all original participants as profiles to reply to in a note</note> <note>Button label to re-add all original participants as profiles to reply to in a note</note>
</trans-unit> </trans-unit>
<trans-unit id="Admin" xml:space="preserve">
<source>Admin</source>
<target>Admin</target>
<note>Label to display relay contact user.</note>
</trans-unit>
<trans-unit id="Any" xml:space="preserve"> <trans-unit id="Any" xml:space="preserve">
<source>Any</source> <source>Any</source>
<target>Any</target> <target>Any</target>
@@ -237,11 +218,6 @@ Number of relay servers a user is connected.</note>
<target>Blue Wallet</target> <target>Blue Wallet</target>
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note> <note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Boosts" xml:space="preserve">
<source>Boosts</source>
<target>Boosts</target>
<note>Accessibility label for boosts button</note>
</trans-unit>
<trans-unit id="Breez" xml:space="preserve"> <trans-unit id="Breez" xml:space="preserve">
<source>Breez</source> <source>Breez</source>
<target>Breez</target> <target>Breez</target>
@@ -260,7 +236,6 @@ Number of relay servers a user is connected.</note>
Button to cancel out of posting a note. Button to cancel out of posting a note.
Button to cancel out of reposting a post. Button to cancel out of reposting a post.
Button to cancel out of view adding user inputted relay. Button to cancel out of view adding user inputted relay.
Cancel deleting the user.
Cancel out of logging out the user.</note> Cancel out of logging out the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cash App" xml:space="preserve"> <trans-unit id="Cash App" xml:space="preserve">
@@ -283,11 +258,6 @@ Number of relay servers a user is connected.</note>
<target>Clear Cache</target> <target>Clear Cache</target>
<note>Section title for clearing cached data.</note> <note>Section title for clearing cached data.</note>
</trans-unit> </trans-unit>
<trans-unit id="Contact" xml:space="preserve">
<source>Contact</source>
<target>Contact</target>
<note>Label to display relay contact information.</note>
</trans-unit>
<trans-unit id="Copied" xml:space="preserve"> <trans-unit id="Copied" xml:space="preserve">
<source>Copied</source> <source>Copied</source>
<target>Copied</target> <target>Copied</target>
@@ -374,10 +344,10 @@ Number of relay servers a user is connected.</note>
<target>Creator(s) of Bitcoin. Absolute legend.</target> <target>Creator(s) of Bitcoin. Absolute legend.</target>
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note> <note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
</trans-unit> </trans-unit>
<trans-unit id="Custom" xml:space="preserve"> <trans-unit id="DM Type" xml:space="preserve">
<source>Custom</source> <source>DM Type</source>
<target>Custom</target> <target>DM Type</target>
<note>Dropdown option for selecting a custom translation server.</note> <note>DM selector for seeing either DMs or message requests, which are messages that have not been responded to yet. DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="DMs" xml:space="preserve"> <trans-unit id="DMs" xml:space="preserve">
<source>DMs</source> <source>DMs</source>
@@ -390,11 +360,6 @@ Number of relay servers a user is connected.</note>
<target>Damus</target> <target>Damus</target>
<note>Name of the app, shown on the first screen when user is not logged in.</note> <note>Name of the app, shown on the first screen when user is not logged in.</note>
</trans-unit> </trans-unit>
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
<source>DeepL (Proprietary, Higher Accuracy)</source>
<target>DeepL (Proprietary, Higher Accuracy)</target>
<note>Dropdown option for selecting DeepL as the translation service.</note>
</trans-unit>
<trans-unit id="Default Wallet" xml:space="preserve"> <trans-unit id="Default Wallet" xml:space="preserve">
<source>Default Wallet</source> <source>Default Wallet</source>
<target>Default Wallet</target> <target>Default Wallet</target>
@@ -403,26 +368,8 @@ Number of relay servers a user is connected.</note>
<trans-unit id="Delete" xml:space="preserve"> <trans-unit id="Delete" xml:space="preserve">
<source>Delete</source> <source>Delete</source>
<target>Delete</target> <target>Delete</target>
<note>Button for deleting the users account. <note>Button to delete a relay server that the user connects to.
Button to delete a relay server that the user connects to. Button to remove a user from their blocklist.</note>
Button to remove a user from their blocklist.
Section title for deleting the user</note>
</trans-unit>
<trans-unit id="Delete Account" xml:space="preserve">
<source>Delete Account</source>
<target>Delete Account</target>
<note>Alert for deleting the users account.
Button to delete the user's account.</note>
</trans-unit>
<trans-unit id="Deleted Account" xml:space="preserve">
<source>Deleted Account</source>
<target>Deleted Account</target>
<note>Alert message to indicate this is a deleted account</note>
</trans-unit>
<trans-unit id="Description" xml:space="preserve">
<source>Description</source>
<target>Description</target>
<note>Label to display relay description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Dismiss" xml:space="preserve"> <trans-unit id="Dismiss" xml:space="preserve">
<source>Dismiss</source> <source>Dismiss</source>
@@ -474,21 +421,16 @@ Number of relay servers a user is connected.</note>
<target>Error: %@</target> <target>Error: %@</target>
<note>Error message indicating why saving keys failed.</note> <note>Error message indicating why saving keys failed.</note>
</trans-unit> </trans-unit>
<trans-unit id="Filter State" xml:space="preserve">
<source>Filter State</source>
<target>Filter State</target>
<note>Filter state for seeing either only posts, or posts &amp; replies.</note>
</trans-unit>
<trans-unit id="Follow" xml:space="preserve"> <trans-unit id="Follow" xml:space="preserve">
<source>Follow</source> <source>Follow</source>
<target>Follow</target> <target>Follow</target>
<note>Button to follow a user.</note> <note>Button to follow a user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follow Back" xml:space="preserve">
<source>Follow Back</source>
<target>Follow Back</target>
<note>Button to follow a user back.</note>
</trans-unit>
<trans-unit id="Follow me on nostr" xml:space="preserve">
<source>Follow me on nostr</source>
<target>Follow me on nostr</target>
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve"> <trans-unit id="Followers" xml:space="preserve">
<source>Followers</source> <source>Followers</source>
<target>Followers</target> <target>Followers</target>
@@ -510,21 +452,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Follows</target> <target>Follows</target>
<note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note> <note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follows you" xml:space="preserve">
<source>Follows you</source>
<target>Follows you</target>
<note>Text to indicate that a user is following your profile.</note>
</trans-unit>
<trans-unit id="Free" xml:space="preserve">
<source>Free</source>
<target>Free</target>
<note>Dropdown option for selecting Free plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Get API Key" xml:space="preserve">
<source>Get API Key</source>
<target>Get API Key</target>
<note>Button to navigate to DeepL website to get a translation API key.</note>
</trans-unit>
<trans-unit id="Global" xml:space="preserve"> <trans-unit id="Global" xml:space="preserve">
<source>Global</source> <source>Global</source>
<target>Global</target> <target>Global</target>
@@ -540,17 +467,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Goto profile %@</target> <target>Goto profile %@</target>
<note>Navigation link to go to profile.</note> <note>Navigation link to go to profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Hide" xml:space="preserve">
<source>Hide</source>
<target>Hide</target>
<note>Button to hide a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Hide API Key" xml:space="preserve">
<source>Hide API Key</source>
<target>Hide API Key</target>
<note>Button to hide the DeepL translation API key.
Button to hide the LibreTranslate server API key.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve"> <trans-unit id="Home" xml:space="preserve">
<source>Home</source> <source>Home</source>
<target>Home</target> <target>Home</target>
@@ -586,11 +502,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Let's go!</target> <target>Let's go!</target>
<note>Button to complete account creation and start using the app.</note> <note>Button to complete account creation and start using the app.</note>
</trans-unit> </trans-unit>
<trans-unit id="LibreTranslate (Open Source)" xml:space="preserve">
<source>LibreTranslate (Open Source)</source>
<target>LibreTranslate (Open Source)</target>
<note>Dropdown option for selecting LibreTranslate as the translation service.</note>
</trans-unit>
<trans-unit id="Lightning Address or LNURL" xml:space="preserve"> <trans-unit id="Lightning Address or LNURL" xml:space="preserve">
<source>Lightning Address or LNURL</source> <source>Lightning Address or LNURL</source>
<target>Lightning Address or LNURL</target> <target>Lightning Address or LNURL</target>
@@ -601,16 +512,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Lightning Invoice</target> <target>Lightning Invoice</target>
<note>Indicates that the view is for paying a Lightning invoice.</note> <note>Indicates that the view is for paying a Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="Like" xml:space="preserve">
<source>Like</source>
<target>Like</target>
<note>Accessibility Label for Like button</note>
</trans-unit>
<trans-unit id="Local authentication to access private key" xml:space="preserve">
<source>Local authentication to access private key</source>
<target>Local authentication to access private key</target>
<note>Face ID usage description shown when trying to access private key</note>
</trans-unit>
<trans-unit id="Local default" xml:space="preserve"> <trans-unit id="Local default" xml:space="preserve">
<source>Local default</source> <source>Local default</source>
<target>Local default</target> <target>Local default</target>
@@ -627,7 +528,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Logout</target> <target>Logout</target>
<note>Alert for logging out the user. <note>Alert for logging out the user.
Button for logging out the user. Button for logging out the user.
Button to close the alert that informs that the current account has been deleted.</note> Button to logout the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve"> <trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source> <source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
@@ -644,26 +545,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>NIP-05 Verification</target> <target>NIP-05 Verification</target>
<note>Label for NIP-05 Verification section of user profile form.</note> <note>Label for NIP-05 Verification section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<target>No</target>
<note>Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve"> <trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve">
<source>No block list found, create a new one? This will overwrite any previous block lists.</source> <source>No block list found, create a new one? This will overwrite any previous block lists.</source>
<target>No block list found, create a new one? This will overwrite any previous block lists.</target> <target>No block list found, create a new one? This will overwrite any previous block lists.</target>
<note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note> <note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note>
</trans-unit> </trans-unit>
<trans-unit id="None" xml:space="preserve">
<source>None</source>
<target>None</target>
<note>Dropdown option for selecting no translation service.</note>
</trans-unit>
<trans-unit id="Note contains &quot;nsec1&quot; private key. Are you sure?" xml:space="preserve">
<source>Note contains "nsec1" private key. Are you sure?</source>
<target>Note contains "nsec1" private key. Are you sure?</target>
<note>Alert user that they might be attempting to paste a private key and ask them to confirm.</note>
</trans-unit>
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve"> <trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
<source>Nothing to see here. Check back later!</source> <source>Nothing to see here. Check back later!</source>
<target>Nothing to see here. Check back later!</target> <target>Nothing to see here. Check back later!</target>
@@ -694,21 +580,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Phoenix</target> <target>Phoenix</target>
<note>Dropdown option label for Lightning wallet, Phoenix.</note> <note>Dropdown option label for Lightning wallet, Phoenix.</note>
</trans-unit> </trans-unit>
<trans-unit id="Plan" xml:space="preserve">
<source>Plan</source>
<target>Plan</target>
<note>Prompt selection of DeepL subscription plan to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Post" xml:space="preserve"> <trans-unit id="Post" xml:space="preserve">
<source>Post</source> <source>Post</source>
<target>Post</target> <target>Post</target>
<note>Button to post a note.</note> <note>Button to post a note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Post from a user you've blocked" xml:space="preserve">
<source>Post from a user you've blocked</source>
<target>Post from a user you've blocked</target>
<note>Text to indicate that what is being shown is a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve"> <trans-unit id="Posts" xml:space="preserve">
<source>Posts</source> <source>Posts</source>
<target>Posts</target> <target>Posts</target>
@@ -729,11 +605,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Private Key</target> <target>Private Key</target>
<note>Title of the secure field that holds the user's private key.</note> <note>Title of the secure field that holds the user's private key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Pro" xml:space="preserve">
<source>Pro</source>
<target>Pro</target>
<note>Dropdown option for selecting Pro plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Profile" xml:space="preserve"> <trans-unit id="Profile" xml:space="preserve">
<source>Profile</source> <source>Profile</source>
<target>Profile</target> <target>Profile</target>
@@ -782,14 +653,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<trans-unit id="Relay" xml:space="preserve"> <trans-unit id="Relay" xml:space="preserve">
<source>Relay</source> <source>Relay</source>
<target>Relay</target> <target>Relay</target>
<note>Label to display relay address. <note>Text field for relay server. Used for testing purposes.</note>
Text field for relay server. Used for testing purposes.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays" xml:space="preserve"> <trans-unit id="Relays" xml:space="preserve">
<source>Relays</source> <source>Relays</source>
<target>Relays</target> <target>Relays</target>
<note>Sidebar menu label for Relay servers view <note>Sidebar menu label for Relay servers view</note>
Sidebar menu label for Relays view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve"> <trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve">
<source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source> <source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source>
@@ -801,11 +670,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Remove all</target> <target>Remove all</target>
<note>Button label to remove all participants from a note reply.</note> <note>Button label to remove all participants from a note reply.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<target>Reply</target>
<note>Accessibility label for reply button</note>
</trans-unit>
<trans-unit id="Reply to self" xml:space="preserve"> <trans-unit id="Reply to self" xml:space="preserve">
<source>Reply to self</source> <source>Reply to self</source>
<target>Reply to self</target> <target>Reply to self</target>
@@ -858,6 +722,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Requests</target> <target>Requests</target>
<note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note> <note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>Reset</target>
<note>Section title for resetting the user</note>
</trans-unit>
<trans-unit id="Retry" xml:space="preserve"> <trans-unit id="Retry" xml:space="preserve">
<source>Retry</source> <source>Retry</source>
<target>Retry</target> <target>Retry</target>
@@ -883,11 +752,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Save Image</target> <target>Save Image</target>
<note>Context menu option to save an image.</note> <note>Context menu option to save an image.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan the code" xml:space="preserve">
<source>Scan the code</source>
<target>Scan the code</target>
<note>Text on QR code view to prompt viewer to scan the QR code on screen with their device camera.</note>
</trans-unit>
<trans-unit id="Search hashtag: #%@" xml:space="preserve"> <trans-unit id="Search hashtag: #%@" xml:space="preserve">
<source>Search hashtag: #%@</source> <source>Search hashtag: #%@</source>
<target>Search hashtag: #%@</target> <target>Search hashtag: #%@</target>
@@ -918,16 +782,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Send a message to start the conversation...</target> <target>Send a message to start the conversation...</target>
<note>Text prompt for user to send a message to the other user.</note> <note>Text prompt for user to send a message to the other user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<target>Server</target>
<note>Prompt selection of LibreTranslate server to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Service" xml:space="preserve">
<source>Service</source>
<target>Service</target>
<note>Prompt selection of translation service provider.</note>
</trans-unit>
<trans-unit id="Settings" xml:space="preserve"> <trans-unit id="Settings" xml:space="preserve">
<source>Settings</source> <source>Settings</source>
<target>Settings</target> <target>Settings</target>
@@ -937,21 +791,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<trans-unit id="Share" xml:space="preserve"> <trans-unit id="Share" xml:space="preserve">
<source>Share</source> <source>Share</source>
<target>Share</target> <target>Share</target>
<note>Button to share a post <note>Button to share an image.
Button to share an image.
Button to share the link to a profile.</note> Button to share the link to a profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show" xml:space="preserve"> <trans-unit id="Show" xml:space="preserve">
<source>Show</source> <source>Show</source>
<target>Show</target> <target>Show</target>
<note>Button to show a post from a user who has been blocked. <note>Toggle to show or hide user's secret account login key.</note>
Toggle to show or hide user's secret account login key.</note>
</trans-unit>
<trans-unit id="Show API Key" xml:space="preserve">
<source>Show API Key</source>
<target>Show API Key</target>
<note>Button to show the DeepL translation API key.
Button to show the LibreTranslate server API key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve"> <trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source> <source>Show wallet selector</source>
@@ -963,21 +809,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Sign out</target> <target>Sign out</target>
<note>Sidebar menu label to sign out of the account.</note> <note>Sidebar menu label to sign out of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Software" xml:space="preserve">
<source>Software</source>
<target>Software</target>
<note>Label to display relay software.</note>
</trans-unit>
<trans-unit id="Strike" xml:space="preserve"> <trans-unit id="Strike" xml:space="preserve">
<source>Strike</source> <source>Strike</source>
<target>Strike</target> <target>Strike</target>
<note>Dropdown option label for Lightning wallet, Strike.</note> <note>Dropdown option label for Lightning wallet, Strike.</note>
</trans-unit> </trans-unit>
<trans-unit id="Supported NIPs" xml:space="preserve">
<source>Supported NIPs</source>
<target>Supported NIPs</target>
<note>Label to display relay's supported NIPs.</note>
</trans-unit>
<trans-unit id="Thanks!" xml:space="preserve"> <trans-unit id="Thanks!" xml:space="preserve">
<source>Thanks!</source> <source>Thanks!</source>
<target>Thanks!</target> <target>Thanks!</target>
@@ -1014,41 +850,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation bar title for note thread. <note>Navigation bar title for note thread.
Navigation bar title for threaded event detail view.</note> Navigation bar title for threaded event detail view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Translate Note" xml:space="preserve">
<source>Translate Note</source>
<target>Translate Note</target>
<note>Button to translate note from different language.</note>
</trans-unit>
<trans-unit id="Translated from (lang)" xml:space="preserve">
<source>Translated from (lang)</source>
<target>Translated from (lang)</target>
<note>Button to indicate that the note has been translated from a different language.</note>
</trans-unit>
<trans-unit id="Translating from (lang)..." xml:space="preserve">
<source>Translating from (lang)...</source>
<target>Translating from (lang)...</target>
<note>Button to indicate that the note is in the process of being translated from a different language.</note>
</trans-unit>
<trans-unit id="Translations" xml:space="preserve">
<source>Translations</source>
<target>Translations</target>
<note>Section title for selecting the translation service.</note>
</trans-unit>
<trans-unit id="Type DELETE to delete" xml:space="preserve">
<source>Type DELETE to delete</source>
<target>Type DELETE to delete</target>
<note>Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should.</note>
</trans-unit>
<trans-unit id="Type your post here..." xml:space="preserve"> <trans-unit id="Type your post here..." xml:space="preserve">
<source>Type your post here...</source> <source>Type your post here...</source>
<target>Type your post here...</target> <target>Type your post here...</target>
<note>Text box prompt to ask user to type their post.</note> <note>Text box prompt to ask user to type their post.</note>
</trans-unit> </trans-unit>
<trans-unit id="URL" xml:space="preserve">
<source>URL</source>
<target>URL</target>
<note>Example URL to LibreTranslate server</note>
</trans-unit>
<trans-unit id="Unfollow" xml:space="preserve"> <trans-unit id="Unfollow" xml:space="preserve">
<source>Unfollow</source> <source>Unfollow</source>
<target>Unfollow</target> <target>Unfollow</target>
@@ -1072,7 +878,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<trans-unit id="User blocked" xml:space="preserve"> <trans-unit id="User blocked" xml:space="preserve">
<source>User blocked</source> <source>User blocked</source>
<target>User blocked</target> <target>User blocked</target>
<note>Alert message to indicate the user has been blocked</note> <note>Alert message to indicate</note>
</trans-unit> </trans-unit>
<trans-unit id="User has been blocked" xml:space="preserve"> <trans-unit id="User has been blocked" xml:space="preserve">
<source>User has been blocked</source> <source>User has been blocked</source>
@@ -1085,26 +891,21 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for Username section of user profile form. <note>Label for Username section of user profile form.
Label to prompt username entry.</note> Label to prompt username entry.</note>
</trans-unit> </trans-unit>
<trans-unit id="Version" xml:space="preserve">
<source>Version</source>
<target>Version</target>
<note>Label to display relay software version.</note>
</trans-unit>
<trans-unit id="Wallet" xml:space="preserve"> <trans-unit id="Wallet" xml:space="preserve">
<source>Wallet</source> <source>Wallet</source>
<target>Wallet</target> <target>Wallet</target>
<note>Sidebar menu label for Wallet view.</note> <note>Sidebar menu label for Wallet view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
<source>Wallet Of Satoshi</source>
<target>Wallet Of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
</trans-unit>
<trans-unit id="Wallet Selector" xml:space="preserve"> <trans-unit id="Wallet Selector" xml:space="preserve">
<source>Wallet Selector</source> <source>Wallet Selector</source>
<target>Wallet Selector</target> <target>Wallet Selector</target>
<note>Section title for selection of wallet.</note> <note>Section title for selection of wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet of Satoshi" xml:space="preserve">
<source>Wallet of Satoshi</source>
<target>Wallet of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet of Satoshi.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve"> <trans-unit id="Website" xml:space="preserve">
<source>Website</source> <source>Website</source>
<target>Website</target> <target>Website</target>
@@ -1130,11 +931,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Yes, Overwrite</target> <target>Yes, Overwrite</target>
<note>Text of button that confirms to overwrite the existing mutelist.</note> <note>Text of button that confirms to overwrite the existing mutelist.</note>
</trans-unit> </trans-unit>
<trans-unit id="Yes, Post with Private Key" xml:space="preserve">
<source>Yes, Post with Private Key</source>
<target>Yes, Post with Private Key</target>
<note>Button to proceed with posting a note even though it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve"> <trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source> <source>Your Name</source>
<target>Your Name</target> <target>Your Name</target>
@@ -1145,11 +941,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>Your report will be sent to the relays you are connected to</target> <target>Your report will be sent to the relays you are connected to</target>
<note>Footer text to inform user what will happen when the report is submitted.</note> <note>Footer text to inform user what will happen when the report is submitted.</note>
</trans-unit> </trans-unit>
<trans-unit id="Zap" xml:space="preserve">
<source>Zap</source>
<target>Zap</target>
<note>Accessibility label for zap button</note>
</trans-unit>
<trans-unit id="Zebedee" xml:space="preserve"> <trans-unit id="Zebedee" xml:space="preserve">
<source>Zebedee</source> <source>Zebedee</source>
<target>Zebedee</target> <target>Zebedee</target>
@@ -1240,6 +1031,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>sats_count</target> <target>sats_count</target>
<note>Amount of sats. (Key in .stringsdict)</note> <note>Amount of sats. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="tips_count" translate="no" xml:space="preserve">
<source>tips_count</source>
<target>tips_count</target>
<note>Part of a larger sentence to describe how many tip payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="u{00A0}" xml:space="preserve"> <trans-unit id="u{00A0}" xml:space="preserve">
<source>u{00A0}</source> <source>u{00A0}</source>
<target>u{00A0}</target> <target>u{00A0}</target>
@@ -1255,16 +1051,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>you</target> <target>you</target>
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note> <note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
</trans-unit> </trans-unit>
<trans-unit id="zaps_count" translate="no" xml:space="preserve">
<source>zaps_count</source>
<target>zaps_count</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="⚡️ %@" xml:space="preserve">
<source>⚡️ %@</source>
<target>⚡️ %@</target>
<note>Text indicating the zap amount. i.e. number of satoshis that were tipped to a user</note>
</trans-unit>
</body> </body>
</file> </file>
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext"> <file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext">
@@ -1402,20 +1188,20 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<target>%2$@ sats</target> <target>%2$@ sats</target>
<note>Amount of sats.</note> <note>Amount of sats.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@ZAPS@</source> <source>%#@TIPS@</source>
<target>%#@ZAPS@</target> <target>%#@TIPS@</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/one:dict/:string" xml:space="preserve">
<source>Zap</source> <source>Tip</source>
<target>Zap</target> <target>Tip</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/other:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/other:dict/:string" xml:space="preserve">
<source>Zaps</source> <source>Tips</source>
<target>Zaps</target> <target>Tips</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>
@@ -2,7 +2,5 @@
"CFBundleDisplayName" = "Damus"; "CFBundleDisplayName" = "Damus";
/* Bundle name */ /* Bundle name */
"CFBundleName" = "damus"; "CFBundleName" = "damus";
/* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "Local authentication to access private key";
/* Privacy - Photo Library Additions Usage Description */ /* Privacy - Photo Library Additions Usage Description */
"NSPhotoLibraryAddUsageDescription" = "Granting Damus access to your photos allows you to save images."; "NSPhotoLibraryAddUsageDescription" = "Granting Damus access to your photos allows you to save images.";
@@ -134,20 +134,20 @@
<string>%2$@ sats</string> <string>%2$@ sats</string>
</dict> </dict>
</dict> </dict>
<key>zaps_count</key> <key>tips_count</key>
<dict> <dict>
<key>NSStringLocalizedFormatKey</key> <key>NSStringLocalizedFormatKey</key>
<string>%#@ZAPS@</string> <string>%#@TIPS@</string>
<key>ZAPS</key> <key>TIPS</key>
<dict> <dict>
<key>NSStringFormatSpecTypeKey</key> <key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string> <string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key> <key>NSStringFormatValueTypeKey</key>
<string>d</string> <string>d</string>
<key>one</key> <key>one</key>
<string>Zap</string> <string>Tip</string>
<key>other</key> <key>other</key>
<string>Zaps</string> <string>Tips</string>
</dict> </dict>
</dict> </dict>
</dict> </dict>
@@ -16,14 +16,9 @@
<note>Bundle name</note> <note>Bundle name</note>
</trans-unit> </trans-unit>
<trans-unit id="NSFaceIDUsageDescription" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Privacy - Face ID Usage Description</note>
</trans-unit>
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve"> <trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
<source>Granting Damus access to your photos allows you to save images.</source> <source>&quot;Granting Damus access to your photo library allows you to save photos.</source>
<target>Si le concedes acceso a Damus a tus fotos, podrás guardar imágenes.</target> <target>Si le concedes acceso a Damus a tu fototeca, podrás guardar fotos.</target>
<note>Privacy - Photo Library Additions Usage Description</note> <note>Privacy - Photo Library Additions Usage Description</note>
</trans-unit> </trans-unit>
@@ -43,20 +38,15 @@
<source>%@</source> <source>%@</source>
<target>%@</target> <target>%@</target>
<note>Abbreviated version of a nostr public key.</note> <note>Amount of time that has passed since reply quote event occurred.
Abbreviated version of a nostr public key.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ %@" xml:space="preserve"> <trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source> <source>%@ %@</source>
<target>%@ %@</target> <target>%@ %@</target>
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'. <note>Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.
Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.</note> Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.</note>
</trans-unit>
<trans-unit id="%@ has been blocked" xml:space="preserve">
<source>%@ has been blocked</source>
<target>Se bloqueó a %@</target>
<note>Alert message that informs a user was blocked.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." xml:space="preserve"> <trans-unit id="%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." xml:space="preserve">
<source>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</source> <source>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</source>
@@ -80,8 +70,8 @@ Sentence composed of 2 variables to describe how many relay servers a user is co
<source>%lld</source> <source>%lld</source>
<target>%lld</target> <target>%lld</target>
<note>Number of zap payments on a post. <note>Number of reposts.
Number of relay servers a user is connected.</note> Number of profiles a user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld/%lld" xml:space="preserve"> <trans-unit id="%lld/%lld" xml:space="preserve">
<source>%lld/%lld</source> <source>%lld/%lld</source>
@@ -91,13 +81,13 @@ Number of relay servers a user is connected.</note>
</trans-unit> </trans-unit>
<trans-unit id="'%@' at '%@' will be used for verification" xml:space="preserve"> <trans-unit id="'%@' at '%@' will be used for verification" xml:space="preserve">
<source>'%@' at '%@' will be used for verification</source> <source>'%@' at '%@' will be used for verification</source>
<target>'%@' en '%@' se usará con fines de verificación</target> <target>'%@' en '%@' se usarán con fines de verificación</target>
<note>Description of how the nip05 identifier would be used for verification.</note> <note>Description of how the nip05 identifier would be used for verification.</note>
</trans-unit> </trans-unit>
<trans-unit id="'%@' is an invalid NIP-05 identifier. It should look like an email." xml:space="preserve"> <trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
<source>'%@' is an invalid NIP-05 identifier. It should look like an email.</source> <source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
<target>'%@' es un identificador NIP-05 no válido. Debería de tener la apariencia de un correo electrónico.</target> <target>'%@' es un identificador nip05 no válido. Debería de tener la apariencia de un correo electrónico.</target>
<note>Description of why the nip05 identifier is invalid.</note> <note>Description of why the nip05 identifier is invalid.</note>
</trans-unit> </trans-unit>
@@ -125,17 +115,6 @@ Number of relay servers a user is connected.</note>
<note>Prefix character to username.</note> <note>Prefix character to username.</note>
</trans-unit> </trans-unit>
<trans-unit id="API Key (optional)" xml:space="preserve">
<source>API Key (optional)</source>
<target>Clave de API (opcional)</target>
<note>Prompt for optional entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="API Key (required)" xml:space="preserve">
<source>API Key (required)</source>
<note>Prompt for required entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="About" xml:space="preserve"> <trans-unit id="About" xml:space="preserve">
<source>About</source> <source>About</source>
<target>Información</target> <target>Información</target>
@@ -154,24 +133,12 @@ Number of relay servers a user is connected.</note>
<note>Placeholder text for About Me description.</note> <note>Placeholder text for About Me description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Accept" xml:space="preserve">
<source>Accept</source>
<target>Aceptar</target>
<note>Button to accept the end user license agreement before being allowed into the app.</note>
</trans-unit>
<trans-unit id="Account ID" xml:space="preserve"> <trans-unit id="Account ID" xml:space="preserve">
<source>Account ID</source> <source>Account ID</source>
<target>Identificador de cuenta</target> <target>Identificador de cuenta</target>
<note>Label to indicate the public ID of the account.</note> <note>Label to indicate the public ID of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Actions" xml:space="preserve">
<source>Actions</source>
<target>Acciones</target>
<note>Title for confirmation dialog to either share, report, or block a profile.</note>
</trans-unit>
<trans-unit id="Add" xml:space="preserve"> <trans-unit id="Add" xml:space="preserve">
<source>Add</source> <source>Add</source>
<target>Agregar</target> <target>Agregar</target>
@@ -185,17 +152,6 @@ Number of relay servers a user is connected.</note>
<note>Label for section for adding a relay server.</note> <note>Label for section for adding a relay server.</note>
</trans-unit> </trans-unit>
<trans-unit id="Add all" xml:space="preserve">
<source>Add all</source>
<target>Agregar todo</target>
<note>Button label to re-add all original participants as profiles to reply to in a note</note>
</trans-unit>
<trans-unit id="Admin" xml:space="preserve">
<source>Admin</source>
<note>Label to display relay contact user.</note>
</trans-unit>
<trans-unit id="Any" xml:space="preserve"> <trans-unit id="Any" xml:space="preserve">
<source>Any</source> <source>Any</source>
<target>Cualquiera</target> <target>Cualquiera</target>
@@ -238,49 +194,12 @@ Number of relay servers a user is connected.</note>
<note>Dropdown option label for Lightning wallet, Blixt Wallet</note> <note>Dropdown option label for Lightning wallet, Blixt Wallet</note>
</trans-unit> </trans-unit>
<trans-unit id="Block" xml:space="preserve">
<source>Block</source>
<target>Bloquear</target>
<note>Alert button to block a user.
Button to block a profile.
Context menu option for blocking users.</note>
</trans-unit>
<trans-unit id="Block %@?" xml:space="preserve">
<source>Block %@?</source>
<target>¿Bloquear a %@?</target>
<note>Alert message prompt to ask if a user should be blocked.</note>
</trans-unit>
<trans-unit id="Block User" xml:space="preserve">
<source>Block User</source>
<target>Bloquear usuario</target>
<note>Title of alert for blocking a user.</note>
</trans-unit>
<trans-unit id="Blocked" xml:space="preserve">
<source>Blocked</source>
<target>Bloqueado</target>
<note>Sidebar menu label for Profile view.</note>
</trans-unit>
<trans-unit id="Blocked Users" xml:space="preserve">
<source>Blocked Users</source>
<target>Usuarios bloqueados</target>
<note>Navigation title of view to see list of blocked users.</note>
</trans-unit>
<trans-unit id="Blue Wallet" xml:space="preserve"> <trans-unit id="Blue Wallet" xml:space="preserve">
<source>Blue Wallet</source> <source>Blue Wallet</source>
<target>Blue Wallet</target> <target>Blue Wallet</target>
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note> <note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Boosts" xml:space="preserve">
<source>Boosts</source>
<note>Accessibility label for boosts button</note>
</trans-unit>
<trans-unit id="Breez" xml:space="preserve"> <trans-unit id="Breez" xml:space="preserve">
<source>Breez</source> <source>Breez</source>
<target>Breez</target> <target>Breez</target>
@@ -297,12 +216,9 @@ Number of relay servers a user is connected.</note>
<source>Cancel</source> <source>Cancel</source>
<target>Cancelar</target> <target>Cancelar</target>
<note>Alert button to cancel out of alert for blocking a user. <note>Button to cancel out of posting a note.
Button to cancel out of alert that creates a new mutelist.
Button to cancel out of posting a note.
Button to cancel out of reposting a post. Button to cancel out of reposting a post.
Button to cancel out of view adding user inputted relay. Button to cancel out of view adding user inputted relay.
Cancel deleting the user.
Cancel out of logging out the user.</note> Cancel out of logging out the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cash App" xml:space="preserve"> <trans-unit id="Cash App" xml:space="preserve">
@@ -329,11 +245,6 @@ Number of relay servers a user is connected.</note>
<note>Section title for clearing cached data.</note> <note>Section title for clearing cached data.</note>
</trans-unit> </trans-unit>
<trans-unit id="Contact" xml:space="preserve">
<source>Contact</source>
<note>Label to display relay contact information.</note>
</trans-unit>
<trans-unit id="Copied" xml:space="preserve"> <trans-unit id="Copied" xml:space="preserve">
<source>Copied</source> <source>Copied</source>
<target>Copiada</target> <target>Copiada</target>
@@ -383,21 +294,15 @@ Number of relay servers a user is connected.</note>
<note>Context menu option for copying the JSON text from the note.</note> <note>Context menu option for copying the JSON text from the note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Copy Report ID" xml:space="preserve">
<source>Copy Report ID</source>
<target>Copiar identificador de reporte</target>
<note>Button to copy report ID.</note>
</trans-unit>
<trans-unit id="Copy Text" xml:space="preserve"> <trans-unit id="Copy Text" xml:space="preserve">
<source>Copy Text</source> <source>Copy Text</source>
<target>Copiar texto</target> <target>Copiar texto</target>
<note>Context menu option for copying the text from an note.</note> <note>Context menu option for copying the text from an note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Copy User Pubkey" xml:space="preserve"> <trans-unit id="Copy User ID" xml:space="preserve">
<source>Copy User Pubkey</source> <source>Copy User ID</source>
<target>Copiar clave pública de usuario</target> <target>Copiar identificador de usuario</target>
<note>Context menu option for copying the ID of the user who created the note.</note> <note>Context menu option for copying the ID of the user who created the note.</note>
</trans-unit> </trans-unit>
@@ -407,12 +312,6 @@ Number of relay servers a user is connected.</note>
<note>Title of section for copying a Lightning invoice identifier.</note> <note>Title of section for copying a Lightning invoice identifier.</note>
</trans-unit> </trans-unit>
<trans-unit id="Could not find user to block..." xml:space="preserve">
<source>Could not find user to block...</source>
<target>No se pudo encontrar al usuario para bloquearlo...</target>
<note>Alert message to indicate that the blocked user could not be found.</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve"> <trans-unit id="Create" xml:space="preserve">
<source>Create</source> <source>Create</source>
<target>Crear</target> <target>Crear</target>
@@ -425,30 +324,17 @@ Number of relay servers a user is connected.</note>
<note>Button to create an account.</note> <note>Button to create an account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create new mutelist" xml:space="preserve">
<source>Create new mutelist</source>
<target>Crear nueva lista de silenciados</target>
<note>Title of alert prompting the user to create a new mutelist.</note>
</trans-unit>
<trans-unit id="Creator(s) of Bitcoin. Absolute legend." xml:space="preserve"> <trans-unit id="Creator(s) of Bitcoin. Absolute legend." xml:space="preserve">
<source>Creator(s) of Bitcoin. Absolute legend.</source> <source>Creator(s) of Bitcoin. Absolute legend.</source>
<target>Creador(es) de Bitcoin. Toda una leyenda.</target> <target>Creador(es) de Bitcoin. Toda una leyenda.</target>
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note> <note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
</trans-unit> </trans-unit>
<trans-unit id="Custom" xml:space="preserve"> <trans-unit id="DM" xml:space="preserve">
<source>Custom</source> <source>DM</source>
<target>Personalizado</target> <target>MD</target>
<note>Dropdown option for selecting a custom translation server.</note> <note>Navigation title for DM view, which is the English abbreviation for Direct Message.</note>
</trans-unit>
<trans-unit id="DMs" xml:space="preserve">
<source>DMs</source>
<target>Mensajes directos</target>
<note>Navigation title for DMs view, where DM is the English abbreviation for Direct Message.
Navigation title for view of DMs, where DM is an English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Damus" xml:space="preserve"> <trans-unit id="Damus" xml:space="preserve">
<source>Damus</source> <source>Damus</source>
@@ -456,11 +342,6 @@ Number of relay servers a user is connected.</note>
<note>Name of the app, shown on the first screen when user is not logged in.</note> <note>Name of the app, shown on the first screen when user is not logged in.</note>
</trans-unit> </trans-unit>
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
<source>DeepL (Proprietary, Higher Accuracy)</source>
<note>Dropdown option for selecting DeepL as the translation service.</note>
</trans-unit>
<trans-unit id="Default Wallet" xml:space="preserve"> <trans-unit id="Default Wallet" xml:space="preserve">
<source>Default Wallet</source> <source>Default Wallet</source>
<target>Billetera predeterminada</target> <target>Billetera predeterminada</target>
@@ -471,26 +352,7 @@ Number of relay servers a user is connected.</note>
<source>Delete</source> <source>Delete</source>
<target>Borrar</target> <target>Borrar</target>
<note>Button for deleting the users account. <note>Button to delete a relay server that the user connects to.</note>
Button to delete a relay server that the user connects to.
Button to remove a user from their blocklist.
Section title for deleting the user</note>
</trans-unit>
<trans-unit id="Delete Account" xml:space="preserve">
<source>Delete Account</source>
<note>Alert for deleting the users account.
Button to delete the user's account.</note>
</trans-unit>
<trans-unit id="Deleted Account" xml:space="preserve">
<source>Deleted Account</source>
<note>Alert message to indicate this is a deleted account</note>
</trans-unit>
<trans-unit id="Description" xml:space="preserve">
<source>Description</source>
<note>Label to display relay description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Dismiss" xml:space="preserve"> <trans-unit id="Dismiss" xml:space="preserve">
<source>Dismiss</source> <source>Dismiss</source>
@@ -510,12 +372,6 @@ Number of relay servers a user is connected.</note>
<note>Button to dismiss wallet selection view for paying Lightning invoice.</note> <note>Button to dismiss wallet selection view for paying Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="EULA" xml:space="preserve">
<source>EULA</source>
<target>CLUF</target>
<note>Label indicating that the below text is the EULA, an acronym for End User License Agreement.</note>
</trans-unit>
<trans-unit id="Earn Money" xml:space="preserve"> <trans-unit id="Earn Money" xml:space="preserve">
<source>Earn Money</source> <source>Earn Money</source>
<target>Gana dinero</target> <target>Gana dinero</target>
@@ -528,18 +384,18 @@ Number of relay servers a user is connected.</note>
<note>Button to edit user's profile.</note> <note>Button to edit user's profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Edit participants" xml:space="preserve">
<source>Edit participants</source>
<target>Editar participantes</target>
<note>Text indicating that the view is used for editing which participants are replied to in a note.</note>
</trans-unit>
<trans-unit id="Encrypted" xml:space="preserve"> <trans-unit id="Encrypted" xml:space="preserve">
<source>Encrypted</source> <source>Encrypted</source>
<target>Cifrada</target> <target>Cifrada</target>
<note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note> <note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note>
</trans-unit> </trans-unit>
<trans-unit id="Encrypted DMs" xml:space="preserve">
<source>Encrypted DMs</source>
<target>MD cifrados</target>
<note>Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message.</note>
</trans-unit>
<trans-unit id="Enter your account key to login:" xml:space="preserve"> <trans-unit id="Enter your account key to login:" xml:space="preserve">
<source>Enter your account key to login:</source> <source>Enter your account key to login:</source>
<target>Ingresa la clave de tu cuenta para iniciar sesión:</target> <target>Ingresa la clave de tu cuenta para iniciar sesión:</target>
@@ -552,22 +408,18 @@ Number of relay servers a user is connected.</note>
<note>Error message indicating why saving keys failed.</note> <note>Error message indicating why saving keys failed.</note>
</trans-unit> </trans-unit>
<trans-unit id="Filter State" xml:space="preserve">
<source>Filter State</source>
<target>Estado del filtro</target>
<note>Filter state for seeing either only posts, or posts &amp; replies.</note>
</trans-unit>
<trans-unit id="Follow" xml:space="preserve"> <trans-unit id="Follow" xml:space="preserve">
<source>Follow</source> <source>Follow</source>
<target>Seguir</target> <target>Seguir</target>
<note>Button to follow a user.</note> <note>Button to follow a user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follow Back" xml:space="preserve">
<source>Follow Back</source>
<note>Button to follow a user back.</note>
</trans-unit>
<trans-unit id="Follow me on nostr" xml:space="preserve">
<source>Follow me on nostr</source>
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve"> <trans-unit id="Followers" xml:space="preserve">
<source>Followers</source> <source>Followers</source>
<target>Seguidores</target> <target>Seguidores</target>
@@ -593,21 +445,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note> <note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follows you" xml:space="preserve">
<source>Follows you</source>
<note>Text to indicate that a user is following your profile.</note>
</trans-unit>
<trans-unit id="Free" xml:space="preserve">
<source>Free</source>
<note>Dropdown option for selecting Free plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Get API Key" xml:space="preserve">
<source>Get API Key</source>
<note>Button to navigate to DeepL website to get a translation API key.</note>
</trans-unit>
<trans-unit id="Global" xml:space="preserve"> <trans-unit id="Global" xml:space="preserve">
<source>Global</source> <source>Global</source>
<target>Global</target> <target>Global</target>
@@ -626,43 +463,18 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation link to go to profile.</note> <note>Navigation link to go to profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Hide" xml:space="preserve">
<source>Hide</source>
<target>Ocultar</target>
<note>Button to hide a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Hide API Key" xml:space="preserve">
<source>Hide API Key</source>
<target>Ocultar clave de API</target>
<note>Button to hide the DeepL translation API key.
Button to hide the LibreTranslate server API key.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve"> <trans-unit id="Home" xml:space="preserve">
<source>Home</source> <source>Home</source>
<target>Inicio</target> <target>Inicio</target>
<note>Navigation bar title for Home view where posts and replies appear from those who the user is following.</note> <note>Navigation bar title for Home view where posts and replies appear from those who the user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="Illegal content" xml:space="preserve">
<source>Illegal content</source>
<target>Contenido ilegal</target>
<note>Button for user to report that the account or content has illegal content.</note>
</trans-unit>
<trans-unit id="Invalid key" xml:space="preserve"> <trans-unit id="Invalid key" xml:space="preserve">
<source>Invalid key</source> <source>Invalid key</source>
<target>Clave inválida</target> <target>Clave inválida</target>
<note>Error message indicating that an invalid account key was entered for login.</note> <note>Error message indicating that an invalid account key was entered for login.</note>
</trans-unit> </trans-unit>
<trans-unit id="It's spam" xml:space="preserve">
<source>It's spam</source>
<target>Es spam</target>
<note>Button for user to report that the account or content has spam.</note>
</trans-unit>
<trans-unit id="LNLink" xml:space="preserve"> <trans-unit id="LNLink" xml:space="preserve">
<source>LNLink</source> <source>LNLink</source>
<target>LNLink</target> <target>LNLink</target>
@@ -681,11 +493,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Button to complete account creation and start using the app.</note> <note>Button to complete account creation and start using the app.</note>
</trans-unit> </trans-unit>
<trans-unit id="LibreTranslate (Open Source)" xml:space="preserve">
<source>LibreTranslate (Open Source)</source>
<note>Dropdown option for selecting LibreTranslate as the translation service.</note>
</trans-unit>
<trans-unit id="Lightning Address or LNURL" xml:space="preserve"> <trans-unit id="Lightning Address or LNURL" xml:space="preserve">
<source>Lightning Address or LNURL</source> <source>Lightning Address or LNURL</source>
<target>Dirección de Lightning o LNURL</target> <target>Dirección de Lightning o LNURL</target>
@@ -698,16 +505,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Indicates that the view is for paying a Lightning invoice.</note> <note>Indicates that the view is for paying a Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="Like" xml:space="preserve">
<source>Like</source>
<note>Accessibility Label for Like button</note>
</trans-unit>
<trans-unit id="Local authentication to access private key" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Face ID usage description shown when trying to access private key</note>
</trans-unit>
<trans-unit id="Local default" xml:space="preserve"> <trans-unit id="Local default" xml:space="preserve">
<source>Local default</source> <source>Local default</source>
<target>Predeterminada del sistema</target> <target>Predeterminada del sistema</target>
@@ -727,7 +524,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Alert for logging out the user. <note>Alert for logging out the user.
Button for logging out the user. Button for logging out the user.
Button to close the alert that informs that the current account has been deleted.</note> Button to logout the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve"> <trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source> <source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
@@ -747,28 +544,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for NIP-05 Verification section of user profile form.</note> <note>Label for NIP-05 Verification section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<note>Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve">
<source>No block list found, create a new one? This will overwrite any previous block lists.</source>
<target>No se encontró una lista de bloqueo. ¿Crear una nueva? Esto sobrescribirá las listas de bloqueo anteriores.</target>
<note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note>
</trans-unit>
<trans-unit id="None" xml:space="preserve">
<source>None</source>
<target>Ninguno</target>
<note>Dropdown option for selecting no translation service.</note>
</trans-unit>
<trans-unit id="Note contains &quot;nsec1&quot; private key. Are you sure?" xml:space="preserve">
<source>Note contains &quot;nsec1&quot; private key. Are you sure?</source>
<note>Alert user that they might be attempting to paste a private key and ask them to confirm.</note>
</trans-unit>
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve"> <trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
<source>Nothing to see here. Check back later!</source> <source>Nothing to see here. Check back later!</source>
<target>Nada para ver aquí. ¡Vuelve a consultar luego!</target> <target>Nada para ver aquí. ¡Vuelve a consultar luego!</target>
@@ -781,12 +556,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation title for notifications.</note> <note>Navigation title for notifications.</note>
</trans-unit> </trans-unit>
<trans-unit id="Nudity or explicit content" xml:space="preserve">
<source>Nudity or explicit content</source>
<target>Desnudos o contenido explícito</target>
<note>Button for user to report that the account or content has nudity or explicit content.</note>
</trans-unit>
<trans-unit id="Pay" xml:space="preserve"> <trans-unit id="Pay" xml:space="preserve">
<source>Pay</source> <source>Pay</source>
<target>Pagar</target> <target>Pagar</target>
@@ -805,23 +574,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Dropdown option label for Lightning wallet, Phoenix.</note> <note>Dropdown option label for Lightning wallet, Phoenix.</note>
</trans-unit> </trans-unit>
<trans-unit id="Plan" xml:space="preserve">
<source>Plan</source>
<note>Prompt selection of DeepL subscription plan to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Post" xml:space="preserve"> <trans-unit id="Post" xml:space="preserve">
<source>Post</source> <source>Post</source>
<target>Publicar</target> <target>Publicar</target>
<note>Button to post a note.</note> <note>Button to post a note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Post from a user you've blocked" xml:space="preserve">
<source>Post from a user you've blocked</source>
<target>Publicación de un usuario que bloqueaste</target>
<note>Text to indicate that what is being shown is a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve"> <trans-unit id="Posts" xml:space="preserve">
<source>Posts</source> <source>Posts</source>
<target>Publicaciones</target> <target>Publicaciones</target>
@@ -844,12 +602,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Private Key</source> <source>Private Key</source>
<target>Clave privada</target> <target>Clave privada</target>
<note>Title of the secure field that holds the user's private key.</note> <note>Label to indicate that the text below is the user's private key used by only the user themself as a secret to login to access their account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Pro" xml:space="preserve"> <trans-unit id="PrivateKey" xml:space="preserve">
<source>Pro</source> <source>PrivateKey</source>
<target>ClavePrivada</target>
<note>Dropdown option for selecting Pro plan for DeepL translation service.</note> <note>Title of the secure field that holds the user's private key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Profile" xml:space="preserve"> <trans-unit id="Profile" xml:space="preserve">
<source>Profile</source> <source>Profile</source>
@@ -899,42 +658,17 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Section title for recommend relay servers that could be added as part of configuration</note> <note>Section title for recommend relay servers that could be added as part of configuration</note>
</trans-unit> </trans-unit>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>Rechazar</target>
<note>Button to reject the end user license agreement, which disallows the user from being let into the app.</note>
</trans-unit>
<trans-unit id="Relay" xml:space="preserve"> <trans-unit id="Relay" xml:space="preserve">
<source>Relay</source> <source>Relay</source>
<target>Relé</target> <target>Relé</target>
<note>Label to display relay address. <note>Text field for relay server. Used for testing purposes.</note>
Text field for relay server. Used for testing purposes.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays" xml:space="preserve"> <trans-unit id="Relays" xml:space="preserve">
<source>Relays</source> <source>Relays</source>
<target>Relés</target> <target>Relés</target>
<note>Sidebar menu label for Relay servers view <note>Sidebar menu label for Relay servers view</note>
Sidebar menu label for Relays view.</note>
</trans-unit>
<trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve">
<source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source>
<target>Se notificó a los relés, por lo que los clientes podrán usar esta información para filtrar contenido. ¡Gracias!</target>
<note>Description of what was done as a result of sending a report to relay servers.</note>
</trans-unit>
<trans-unit id="Remove all" xml:space="preserve">
<source>Remove all</source>
<target>Eliminar todo</target>
<note>Button label to remove all participants from a note reply.</note>
</trans-unit>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<note>Accessibility label for reply button</note>
</trans-unit> </trans-unit>
<trans-unit id="Reply to self" xml:space="preserve"> <trans-unit id="Reply to self" xml:space="preserve">
<source>Reply to self</source> <source>Reply to self</source>
@@ -954,25 +688,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Indicating that the user is replying to the following listed people.</note> <note>Indicating that the user is replying to the following listed people.</note>
</trans-unit> </trans-unit>
<trans-unit id="Report" xml:space="preserve">
<source>Report</source>
<target>Reportar</target>
<note>Button to report a profile.
Context menu option for reporting content.</note>
</trans-unit>
<trans-unit id="Report ID:" xml:space="preserve">
<source>Report ID:</source>
<target>Identificador de reporte:</target>
<note>Label indicating that the text underneath is the identifier of the report that was sent to relay servers.</note>
</trans-unit>
<trans-unit id="Report sent!" xml:space="preserve">
<source>Report sent!</source>
<target>¡Reporte enviado!</target>
<note>Message indicating that a report was successfully sent to relay servers.</note>
</trans-unit>
<trans-unit id="Repost" xml:space="preserve"> <trans-unit id="Repost" xml:space="preserve">
<source>Repost</source> <source>Repost</source>
<target>Republicar</target> <target>Republicar</target>
@@ -986,17 +701,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text indicating that the post was reposted (i.e. re-shared).</note> <note>Text indicating that the post was reposted (i.e. re-shared).</note>
</trans-unit> </trans-unit>
<trans-unit id="Reposts" xml:space="preserve"> <trans-unit id="Reset" xml:space="preserve">
<source>Reposts</source> <source>Reset</source>
<target>Republicaciones</target> <target>Reiniciar</target>
<note>Navigation bar title for Reposts view.</note> <note>Section title for resetting the user</note>
</trans-unit>
<trans-unit id="Requests" xml:space="preserve">
<source>Requests</source>
<target>Solicitudes</target>
<note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Retry" xml:space="preserve"> <trans-unit id="Retry" xml:space="preserve">
<source>Retry</source> <source>Retry</source>
@@ -1028,11 +737,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Context menu option to save an image.</note> <note>Context menu option to save an image.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan the code" xml:space="preserve">
<source>Scan the code</source>
<note>Text on QR code view to prompt viewer to scan the QR code on screen with their device camera.</note>
</trans-unit>
<trans-unit id="Search hashtag: #%@" xml:space="preserve"> <trans-unit id="Search hashtag: #%@" xml:space="preserve">
<source>Search hashtag: #%@</source> <source>Search hashtag: #%@</source>
<target>Buscar hashtag: #%@</target> <target>Buscar hashtag: #%@</target>
@@ -1041,7 +745,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="Search..." xml:space="preserve"> <trans-unit id="Search..." xml:space="preserve">
<source>Search...</source> <source>Search...</source>
<target>Buscar...</target> <target>Búsqueda...</target>
<note>Placeholder text to prompt entry of search query.</note> <note>Placeholder text to prompt entry of search query.</note>
</trans-unit> </trans-unit>
@@ -1069,17 +773,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text prompt for user to send a message to the other user.</note> <note>Text prompt for user to send a message to the other user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<target>Servidor</target>
<note>Prompt selection of LibreTranslate server to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Service" xml:space="preserve">
<source>Service</source>
<note>Prompt selection of translation service provider.</note>
</trans-unit>
<trans-unit id="Settings" xml:space="preserve"> <trans-unit id="Settings" xml:space="preserve">
<source>Settings</source> <source>Settings</source>
<target>Configuración</target> <target>Configuración</target>
@@ -1091,23 +784,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Share</source> <source>Share</source>
<target>Compartir</target> <target>Compartir</target>
<note>Button to share a post <note>Button to share an image.</note>
Button to share an image.
Button to share the link to a profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show" xml:space="preserve"> <trans-unit id="Show" xml:space="preserve">
<source>Show</source> <source>Show</source>
<target>Mostrar</target> <target>Mostrar</target>
<note>Button to show a post from a user who has been blocked. <note>Toggle to show or hide user's secret account login key.</note>
Toggle to show or hide user's secret account login key.</note>
</trans-unit>
<trans-unit id="Show API Key" xml:space="preserve">
<source>Show API Key</source>
<target>Mostrar clave de API</target>
<note>Button to show the DeepL translation API key.
Button to show the LibreTranslate server API key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve"> <trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source> <source>Show wallet selector</source>
@@ -1121,34 +804,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Sidebar menu label to sign out of the account.</note> <note>Sidebar menu label to sign out of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Software" xml:space="preserve">
<source>Software</source>
<note>Label to display relay software.</note>
</trans-unit>
<trans-unit id="Strike" xml:space="preserve"> <trans-unit id="Strike" xml:space="preserve">
<source>Strike</source> <source>Strike</source>
<target>Strike</target> <target>Strike</target>
<note>Dropdown option label for Lightning wallet, Strike.</note> <note>Dropdown option label for Lightning wallet, Strike.</note>
</trans-unit> </trans-unit>
<trans-unit id="Supported NIPs" xml:space="preserve">
<source>Supported NIPs</source>
<note>Label to display relay's supported NIPs.</note>
</trans-unit>
<trans-unit id="Thanks!" xml:space="preserve">
<source>Thanks!</source>
<target>¡Gracias!</target>
<note>Button to close out of alert that informs that the action to block a user was successful.</note>
</trans-unit>
<trans-unit id="They are impersonating someone" xml:space="preserve">
<source>They are impersonating someone</source>
<target>Está suplantando a alguien</target>
<note>Button for user to report that the account is impersonating someone.</note>
</trans-unit>
<trans-unit id="This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." xml:space="preserve"> <trans-unit id="This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." xml:space="preserve">
<source>This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective.</source> <source>This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective.</source>
<target>Esta es una clave pública, por lo que no podrás hacer publicaciones ni interactuar de ningún modo. Se usa para ver cuentas desde su perspectiva.</target> <target>Esta es una clave pública, por lo que no podrás hacer publicaciones ni interactuar de ningún modo. Se usa para ver cuentas desde su perspectiva.</target>
@@ -1169,7 +830,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!" xml:space="preserve"> <trans-unit id="This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!" xml:space="preserve">
<source>This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!</source> <source>This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!</source>
<target>Esta es tu clave de cuenta secreta, que necesitas para acceder a tu cuenta. ¡No la compartas con nadie! Guárdala en un administrador de contraseñas y protégela.</target> <target>Esta es tu clave de cuenta secreta, que necesitas para acceder a tu cuenta. ¡No la compartas con nadie! Guárdala en un administrador de contraseñas y protégela!</target>
<note>Label to describe that a private key is the user's secret account key and what they should do with it.</note> <note>Label to describe that a private key is the user's secret account key and what they should do with it.</note>
</trans-unit> </trans-unit>
@@ -1180,45 +841,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation bar title for note thread. <note>Navigation bar title for note thread.
Navigation bar title for threaded event detail view.</note> Navigation bar title for threaded event detail view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Translate Note" xml:space="preserve">
<source>Translate Note</source>
<target>Traducir nota</target>
<note>Button to translate note from different language.</note>
</trans-unit>
<trans-unit id="Translated from (lang)" xml:space="preserve">
<source>Translated from (lang)</source>
<target>Traducida del (lang)</target>
<note>Button to indicate that the note has been translated from a different language.</note>
</trans-unit>
<trans-unit id="Translating from (lang)..." xml:space="preserve">
<source>Translating from (lang)...</source>
<note>Button to indicate that the note is in the process of being translated from a different language.</note>
</trans-unit>
<trans-unit id="Translations" xml:space="preserve">
<source>Translations</source>
<note>Section title for selecting the translation service.</note>
</trans-unit>
<trans-unit id="Type DELETE to delete" xml:space="preserve">
<source>Type DELETE to delete</source>
<note>Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should.</note>
</trans-unit>
<trans-unit id="Type your post here..." xml:space="preserve"> <trans-unit id="Type your post here..." xml:space="preserve">
<source>Type your post here...</source> <source>Type your post here...</source>
<target>Escribe tu publicación aquí...</target> <target>Ingresa tu publicación aquí...</target>
<note>Text box prompt to ask user to type their post.</note> <note>Text box prompt to ask user to type their post.</note>
</trans-unit> </trans-unit>
<trans-unit id="URL" xml:space="preserve">
<source>URL</source>
<target>URL</target>
<note>Example URL to LibreTranslate server</note>
</trans-unit>
<trans-unit id="Unfollow" xml:space="preserve"> <trans-unit id="Unfollow" xml:space="preserve">
<source>Unfollow</source> <source>Unfollow</source>
<target>Dejar de seguir</target> <target>Dejar de seguir</target>
@@ -1243,18 +871,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.</note> <note>Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="User blocked" xml:space="preserve">
<source>User blocked</source>
<target>Usuario bloqueado</target>
<note>Alert message to indicate the user has been blocked</note>
</trans-unit>
<trans-unit id="User has been blocked" xml:space="preserve">
<source>User has been blocked</source>
<target>Se bloqueó al usuario</target>
<note>Alert message that informs a user was blocked.</note>
</trans-unit>
<trans-unit id="Username" xml:space="preserve"> <trans-unit id="Username" xml:space="preserve">
<source>Username</source> <source>Username</source>
<target>Nombre de usuario</target> <target>Nombre de usuario</target>
@@ -1262,29 +878,24 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for Username section of user profile form. <note>Label for Username section of user profile form.
Label to prompt username entry.</note> Label to prompt username entry.</note>
</trans-unit> </trans-unit>
<trans-unit id="Version" xml:space="preserve">
<source>Version</source>
<note>Label to display relay software version.</note>
</trans-unit>
<trans-unit id="Wallet" xml:space="preserve"> <trans-unit id="Wallet" xml:space="preserve">
<source>Wallet</source> <source>Wallet</source>
<target>Billetera</target> <target>Billetera</target>
<note>Sidebar menu label for Wallet view.</note> <note>Sidebar menu label for Wallet view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
<source>Wallet Of Satoshi</source>
<target>Wallet Of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
</trans-unit>
<trans-unit id="Wallet Selector" xml:space="preserve"> <trans-unit id="Wallet Selector" xml:space="preserve">
<source>Wallet Selector</source> <source>Wallet Selector</source>
<target>Selección de billetera</target> <target>Selección de billetera</target>
<note>Section title for selection of wallet.</note> <note>Section title for selection of wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet of Satoshi" xml:space="preserve">
<source>Wallet of Satoshi</source>
<target>Wallet of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet of Satoshi.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve"> <trans-unit id="Website" xml:space="preserve">
<source>Website</source> <source>Website</source>
<target>Sitio web</target> <target>Sitio web</target>
@@ -1303,40 +914,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to welcome user.</note> <note>Text to welcome user.</note>
</trans-unit> </trans-unit>
<trans-unit id="What do you want to report?" xml:space="preserve">
<source>What do you want to report?</source>
<target>¿Qué quieres reportar?</target>
<note>Header text to prompt user what issue they want to report.</note>
</trans-unit>
<trans-unit id="Yes, Overwrite" xml:space="preserve">
<source>Yes, Overwrite</source>
<target>Sí, sobrescribir</target>
<note>Text of button that confirms to overwrite the existing mutelist.</note>
</trans-unit>
<trans-unit id="Yes, Post with Private Key" xml:space="preserve">
<source>Yes, Post with Private Key</source>
<note>Button to proceed with posting a note even though it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve"> <trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source> <source>Your Name</source>
<target>Tu nombre</target> <target>Tu nombre</target>
<note>Label for Your Name section of user profile form.</note> <note>Label for Your Name section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="Your report will be sent to the relays you are connected to" xml:space="preserve">
<source>Your report will be sent to the relays you are connected to</source>
<target>El reporte se enviará a los relés con los que tengas conexión</target>
<note>Footer text to inform user what will happen when the report is submitted.</note>
</trans-unit>
<trans-unit id="Zap" xml:space="preserve">
<source>Zap</source>
<note>Accessibility label for zap button</note>
</trans-unit>
<trans-unit id="Zebedee" xml:space="preserve"> <trans-unit id="Zebedee" xml:space="preserve">
<source>Zebedee</source> <source>Zebedee</source>
<target>Zebedee</target> <target>Zebedee</target>
@@ -1351,13 +934,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve"> <trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve">
<source>collapsed_event_view_other_notes</source> <source>collapsed_event_view_other_notes</source>
<target>collapsed_event_view_other_notes</target> <target>evento_colapsado_ver_otras_notas</target>
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped. (Key in .stringsdict)</note> <note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="followers_count" translate="no" xml:space="preserve"> <trans-unit id="followers_count" translate="no" xml:space="preserve">
<source>followers_count</source> <source>followers_count</source>
<target>followers_count</target> <target>recuento_de_seguidores</target>
<note>Part of a larger sentence to describe how many people are following a user. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many people are following a user. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
@@ -1405,13 +988,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="reactions_count" translate="no" xml:space="preserve"> <trans-unit id="reactions_count" translate="no" xml:space="preserve">
<source>reactions_count</source> <source>reactions_count</source>
<target>reactions_count</target> <target>recuento_de_reacciones</target>
<note>Part of a larger sentence to describe how many reactions there are on a post. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many reactions there are on a post. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="relays_count" translate="no" xml:space="preserve"> <trans-unit id="relays_count" translate="no" xml:space="preserve">
<source>relays_count</source> <source>relays_count</source>
<target>relays_count</target> <target>recuento_de_relés</target>
<note>Part of a larger sentence to describe how many relay servers a user is connected. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many relay servers a user is connected. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
@@ -1445,6 +1028,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats. (Key in .stringsdict)</note> <note>Amount of sats. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="tips_count" translate="no" xml:space="preserve">
<source>tips_count</source>
<target>tips_count</target>
<note>Part of a larger sentence to describe how many tip payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="u{00A0}" xml:space="preserve"> <trans-unit id="u{00A0}" xml:space="preserve">
<source>u{00A0}</source> <source>u{00A0}</source>
<target>u{00A0}</target> <target>u{00A0}</target>
@@ -1453,7 +1042,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="wss://some.relay.com" xml:space="preserve"> <trans-unit id="wss://some.relay.com" xml:space="preserve">
<source>wss://some.relay.com</source> <source>wss://some.relay.com</source>
<target>wss://algun.rele.com</target> <target>wss://algún.relé.com</target>
<note>Placeholder example for relay server address.</note> <note>Placeholder example for relay server address.</note>
</trans-unit> </trans-unit>
@@ -1463,18 +1052,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note> <note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
</trans-unit> </trans-unit>
<trans-unit id="zaps_count" translate="no" xml:space="preserve">
<source>zaps_count</source>
<target>zaps_count</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="⚡️ %@" xml:space="preserve">
<source>⚡️ %@</source>
<target>⚡️ %@</target>
<note>Text indicating the zap amount. i.e. number of satoshis that were tipped to a user</note>
</trans-unit>
</body> </body>
</file> </file>
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="es-419" datatype="plaintext"> <file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="es-419" datatype="plaintext">
@@ -1634,21 +1211,23 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats.</note> <note>Amount of sats.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@ZAPS@</source> <source>%#@TIPS@</source>
<target>%#@ZAPS@</target> <target>%#@TIPS@</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/one:dict/:string" xml:space="preserve">
<source>Zap</source> <source>Tip</source>
<target>Propina</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/other:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/other:dict/:string" xml:space="preserve">
<source>Zaps</source> <source>Tips</source>
<target>Propinas</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>
@@ -16,11 +16,6 @@
<note>Bundle name</note> <note>Bundle name</note>
</trans-unit> </trans-unit>
<trans-unit id="NSFaceIDUsageDescription" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Privacy - Face ID Usage Description</note>
</trans-unit>
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve"> <trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
<source>Granting Damus access to your photos allows you to save images.</source> <source>Granting Damus access to your photos allows you to save images.</source>
<target>A concessão de acesso da Damus às suas fotografias permite-lhe guardar imagens.</target> <target>A concessão de acesso da Damus às suas fotografias permite-lhe guardar imagens.</target>
@@ -43,14 +38,15 @@
<source>%@</source> <source>%@</source>
<target>%@</target> <target>%@</target>
<note>Abbreviated version of a nostr public key.</note> <note>Amount of time that has passed since reply quote event occurred.
Abbreviated version of a nostr public key.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ %@" xml:space="preserve"> <trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source> <source>%@ %@</source>
<target>%@ %@</target> <target>%@ %@</target>
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'. <note>Sentence composed of 2 variables to describe how many tip payments there are on a post. In source English, the first variable is the number of tip payments, and the second variable is 'Tip' or 'Tips'.
Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.</note> Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ has been blocked" xml:space="preserve"> <trans-unit id="%@ has been blocked" xml:space="preserve">
<source>%@ has been blocked</source> <source>%@ has been blocked</source>
@@ -80,8 +76,8 @@ Sentence composed of 2 variables to describe how many relay servers a user is co
<source>%lld</source> <source>%lld</source>
<target>%lld</target> <target>%lld</target>
<note>Number of zap payments on a post. <note>Number of tip payments on a post.
Number of relay servers a user is connected.</note> Number of profiles a user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld/%lld" xml:space="preserve"> <trans-unit id="%lld/%lld" xml:space="preserve">
<source>%lld/%lld</source> <source>%lld/%lld</source>
@@ -95,9 +91,9 @@ Number of relay servers a user is connected.</note>
<note>Description of how the nip05 identifier would be used for verification.</note> <note>Description of how the nip05 identifier would be used for verification.</note>
</trans-unit> </trans-unit>
<trans-unit id="'%@' is an invalid NIP-05 identifier. It should look like an email." xml:space="preserve"> <trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
<source>'%@' is an invalid NIP-05 identifier. It should look like an email.</source> <source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
<target>'%@' é um identificador NIP-05 inválido. Deve parecer-se com um e-mail.</target> <target>'%@' é um identificador nip05 inválido. Deve parecer-se com um e-mail.</target>
<note>Description of why the nip05 identifier is invalid.</note> <note>Description of why the nip05 identifier is invalid.</note>
</trans-unit> </trans-unit>
@@ -125,17 +121,6 @@ Number of relay servers a user is connected.</note>
<note>Prefix character to username.</note> <note>Prefix character to username.</note>
</trans-unit> </trans-unit>
<trans-unit id="API Key (optional)" xml:space="preserve">
<source>API Key (optional)</source>
<target>Chave API (opcional)</target>
<note>Prompt for optional entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="API Key (required)" xml:space="preserve">
<source>API Key (required)</source>
<note>Prompt for required entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="About" xml:space="preserve"> <trans-unit id="About" xml:space="preserve">
<source>About</source> <source>About</source>
<target>Acerca de</target> <target>Acerca de</target>
@@ -191,11 +176,6 @@ Number of relay servers a user is connected.</note>
<note>Button label to re-add all original participants as profiles to reply to in a note</note> <note>Button label to re-add all original participants as profiles to reply to in a note</note>
</trans-unit> </trans-unit>
<trans-unit id="Admin" xml:space="preserve">
<source>Admin</source>
<note>Label to display relay contact user.</note>
</trans-unit>
<trans-unit id="Any" xml:space="preserve"> <trans-unit id="Any" xml:space="preserve">
<source>Any</source> <source>Any</source>
<target>Qualquer</target> <target>Qualquer</target>
@@ -276,11 +256,6 @@ Number of relay servers a user is connected.</note>
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note> <note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Boosts" xml:space="preserve">
<source>Boosts</source>
<note>Accessibility label for boosts button</note>
</trans-unit>
<trans-unit id="Breez" xml:space="preserve"> <trans-unit id="Breez" xml:space="preserve">
<source>Breez</source> <source>Breez</source>
<target>Breez</target> <target>Breez</target>
@@ -302,7 +277,6 @@ Number of relay servers a user is connected.</note>
Button to cancel out of posting a note. Button to cancel out of posting a note.
Button to cancel out of reposting a post. Button to cancel out of reposting a post.
Button to cancel out of view adding user inputted relay. Button to cancel out of view adding user inputted relay.
Cancel deleting the user.
Cancel out of logging out the user.</note> Cancel out of logging out the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cash App" xml:space="preserve"> <trans-unit id="Cash App" xml:space="preserve">
@@ -329,11 +303,6 @@ Number of relay servers a user is connected.</note>
<note>Section title for clearing cached data.</note> <note>Section title for clearing cached data.</note>
</trans-unit> </trans-unit>
<trans-unit id="Contact" xml:space="preserve">
<source>Contact</source>
<note>Label to display relay contact information.</note>
</trans-unit>
<trans-unit id="Copied" xml:space="preserve"> <trans-unit id="Copied" xml:space="preserve">
<source>Copied</source> <source>Copied</source>
<target>Copiado</target> <target>Copiado</target>
@@ -437,11 +406,11 @@ Number of relay servers a user is connected.</note>
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note> <note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
</trans-unit> </trans-unit>
<trans-unit id="Custom" xml:space="preserve"> <trans-unit id="DM Type" xml:space="preserve">
<source>Custom</source> <source>DM Type</source>
<target>Personalizado</target> <target>TIpo de MPs</target>
<note>Dropdown option for selecting a custom translation server.</note> <note>DM selector for seeing either DMs or message requests, which are messages that have not been responded to yet. DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="DMs" xml:space="preserve"> <trans-unit id="DMs" xml:space="preserve">
<source>DMs</source> <source>DMs</source>
@@ -456,11 +425,6 @@ Number of relay servers a user is connected.</note>
<note>Name of the app, shown on the first screen when user is not logged in.</note> <note>Name of the app, shown on the first screen when user is not logged in.</note>
</trans-unit> </trans-unit>
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
<source>DeepL (Proprietary, Higher Accuracy)</source>
<note>Dropdown option for selecting DeepL as the translation service.</note>
</trans-unit>
<trans-unit id="Default Wallet" xml:space="preserve"> <trans-unit id="Default Wallet" xml:space="preserve">
<source>Default Wallet</source> <source>Default Wallet</source>
<target>Carteira Padrão</target> <target>Carteira Padrão</target>
@@ -471,26 +435,8 @@ Number of relay servers a user is connected.</note>
<source>Delete</source> <source>Delete</source>
<target>Apagar</target> <target>Apagar</target>
<note>Button for deleting the users account. <note>Button to delete a relay server that the user connects to.
Button to delete a relay server that the user connects to. Button to remove a user from their blocklist.</note>
Button to remove a user from their blocklist.
Section title for deleting the user</note>
</trans-unit>
<trans-unit id="Delete Account" xml:space="preserve">
<source>Delete Account</source>
<note>Alert for deleting the users account.
Button to delete the user's account.</note>
</trans-unit>
<trans-unit id="Deleted Account" xml:space="preserve">
<source>Deleted Account</source>
<note>Alert message to indicate this is a deleted account</note>
</trans-unit>
<trans-unit id="Description" xml:space="preserve">
<source>Description</source>
<note>Label to display relay description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Dismiss" xml:space="preserve"> <trans-unit id="Dismiss" xml:space="preserve">
<source>Dismiss</source> <source>Dismiss</source>
@@ -552,22 +498,18 @@ Number of relay servers a user is connected.</note>
<note>Error message indicating why saving keys failed.</note> <note>Error message indicating why saving keys failed.</note>
</trans-unit> </trans-unit>
<trans-unit id="Filter State" xml:space="preserve">
<source>Filter State</source>
<target>Estado do filtro</target>
<note>Filter state for seeing either only posts, or posts &amp; replies.</note>
</trans-unit>
<trans-unit id="Follow" xml:space="preserve"> <trans-unit id="Follow" xml:space="preserve">
<source>Follow</source> <source>Follow</source>
<target>Seguir</target> <target>Seguir</target>
<note>Button to follow a user.</note> <note>Button to follow a user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follow Back" xml:space="preserve">
<source>Follow Back</source>
<note>Button to follow a user back.</note>
</trans-unit>
<trans-unit id="Follow me on nostr" xml:space="preserve">
<source>Follow me on nostr</source>
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve"> <trans-unit id="Followers" xml:space="preserve">
<source>Followers</source> <source>Followers</source>
<target>Seguidores</target> <target>Seguidores</target>
@@ -593,21 +535,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note> <note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follows you" xml:space="preserve">
<source>Follows you</source>
<note>Text to indicate that a user is following your profile.</note>
</trans-unit>
<trans-unit id="Free" xml:space="preserve">
<source>Free</source>
<note>Dropdown option for selecting Free plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Get API Key" xml:space="preserve">
<source>Get API Key</source>
<note>Button to navigate to DeepL website to get a translation API key.</note>
</trans-unit>
<trans-unit id="Global" xml:space="preserve"> <trans-unit id="Global" xml:space="preserve">
<source>Global</source> <source>Global</source>
<target>Global</target> <target>Global</target>
@@ -626,19 +553,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation link to go to profile.</note> <note>Navigation link to go to profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Hide" xml:space="preserve">
<source>Hide</source>
<target>Ocultar</target>
<note>Button to hide a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Hide API Key" xml:space="preserve">
<source>Hide API Key</source>
<target>Ocultar Chave API</target>
<note>Button to hide the DeepL translation API key.
Button to hide the LibreTranslate server API key.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve"> <trans-unit id="Home" xml:space="preserve">
<source>Home</source> <source>Home</source>
<target>Iniciar</target> <target>Iniciar</target>
@@ -681,11 +595,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Button to complete account creation and start using the app.</note> <note>Button to complete account creation and start using the app.</note>
</trans-unit> </trans-unit>
<trans-unit id="LibreTranslate (Open Source)" xml:space="preserve">
<source>LibreTranslate (Open Source)</source>
<note>Dropdown option for selecting LibreTranslate as the translation service.</note>
</trans-unit>
<trans-unit id="Lightning Address or LNURL" xml:space="preserve"> <trans-unit id="Lightning Address or LNURL" xml:space="preserve">
<source>Lightning Address or LNURL</source> <source>Lightning Address or LNURL</source>
<target>Endereço Lightning ou LNURL</target> <target>Endereço Lightning ou LNURL</target>
@@ -698,16 +607,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Indicates that the view is for paying a Lightning invoice.</note> <note>Indicates that the view is for paying a Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="Like" xml:space="preserve">
<source>Like</source>
<note>Accessibility Label for Like button</note>
</trans-unit>
<trans-unit id="Local authentication to access private key" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Face ID usage description shown when trying to access private key</note>
</trans-unit>
<trans-unit id="Local default" xml:space="preserve"> <trans-unit id="Local default" xml:space="preserve">
<source>Local default</source> <source>Local default</source>
<target>Padrão Local</target> <target>Padrão Local</target>
@@ -727,7 +626,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Alert for logging out the user. <note>Alert for logging out the user.
Button for logging out the user. Button for logging out the user.
Button to close the alert that informs that the current account has been deleted.</note> Button to logout the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve"> <trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source> <source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
@@ -747,28 +646,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for NIP-05 Verification section of user profile form.</note> <note>Label for NIP-05 Verification section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<note>Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve"> <trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve">
<source>No block list found, create a new one? This will overwrite any previous block lists.</source> <source>No block list found, create a new one? This will overwrite any previous block lists.</source>
<target>Não foi encontrada nenhuma lista de bloqueios, criar uma nova lista? Isto irá substituir qualquer lista de bloqueios anterior.</target> <target>Não foi encontrada nenhuma lista de bloqueios, criar uma nova lista? Isto irá substituir qualquer lista de bloqueios anterior.</target>
<note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note> <note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note>
</trans-unit> </trans-unit>
<trans-unit id="None" xml:space="preserve">
<source>None</source>
<target>Nenhum</target>
<note>Dropdown option for selecting no translation service.</note>
</trans-unit>
<trans-unit id="Note contains &quot;nsec1&quot; private key. Are you sure?" xml:space="preserve">
<source>Note contains &quot;nsec1&quot; private key. Are you sure?</source>
<note>Alert user that they might be attempting to paste a private key and ask them to confirm.</note>
</trans-unit>
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve"> <trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
<source>Nothing to see here. Check back later!</source> <source>Nothing to see here. Check back later!</source>
<target>Não há nada para ver aqui. Volte mais tarde!</target> <target>Não há nada para ver aqui. Volte mais tarde!</target>
@@ -805,23 +688,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Dropdown option label for Lightning wallet, Phoenix.</note> <note>Dropdown option label for Lightning wallet, Phoenix.</note>
</trans-unit> </trans-unit>
<trans-unit id="Plan" xml:space="preserve">
<source>Plan</source>
<note>Prompt selection of DeepL subscription plan to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Post" xml:space="preserve"> <trans-unit id="Post" xml:space="preserve">
<source>Post</source> <source>Post</source>
<target>Post</target> <target>Post</target>
<note>Button to post a note.</note> <note>Button to post a note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Post from a user you've blocked" xml:space="preserve">
<source>Post from a user you've blocked</source>
<target>Post de um utilizador que bloqueou</target>
<note>Text to indicate that what is being shown is a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve"> <trans-unit id="Posts" xml:space="preserve">
<source>Posts</source> <source>Posts</source>
<target>Posts</target> <target>Posts</target>
@@ -846,11 +718,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Title of the secure field that holds the user's private key.</note> <note>Title of the secure field that holds the user's private key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Pro" xml:space="preserve">
<source>Pro</source>
<note>Dropdown option for selecting Pro plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Profile" xml:space="preserve"> <trans-unit id="Profile" xml:space="preserve">
<source>Profile</source> <source>Profile</source>
<target>Perfil</target> <target>Perfil</target>
@@ -909,15 +776,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Relay</source> <source>Relay</source>
<target>Relay</target> <target>Relay</target>
<note>Label to display relay address. <note>Text field for relay server. Used for testing purposes.</note>
Text field for relay server. Used for testing purposes.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays" xml:space="preserve"> <trans-unit id="Relays" xml:space="preserve">
<source>Relays</source> <source>Relays</source>
<target>Relays</target> <target>Relays</target>
<note>Sidebar menu label for Relay servers view <note>Sidebar menu label for Relay servers view</note>
Sidebar menu label for Relays view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve"> <trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve">
<source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source> <source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source>
@@ -931,11 +796,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Button label to remove all participants from a note reply.</note> <note>Button label to remove all participants from a note reply.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<note>Accessibility label for reply button</note>
</trans-unit>
<trans-unit id="Reply to self" xml:space="preserve"> <trans-unit id="Reply to self" xml:space="preserve">
<source>Reply to self</source> <source>Reply to self</source>
<target>Responder ao próprio</target> <target>Responder ao próprio</target>
@@ -998,6 +858,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note> <note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>Repôr</target>
<note>Section title for resetting the user</note>
</trans-unit>
<trans-unit id="Retry" xml:space="preserve"> <trans-unit id="Retry" xml:space="preserve">
<source>Retry</source> <source>Retry</source>
<target>Tentar Novamente</target> <target>Tentar Novamente</target>
@@ -1028,11 +894,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Context menu option to save an image.</note> <note>Context menu option to save an image.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan the code" xml:space="preserve">
<source>Scan the code</source>
<note>Text on QR code view to prompt viewer to scan the QR code on screen with their device camera.</note>
</trans-unit>
<trans-unit id="Search hashtag: #%@" xml:space="preserve"> <trans-unit id="Search hashtag: #%@" xml:space="preserve">
<source>Search hashtag: #%@</source> <source>Search hashtag: #%@</source>
<target>Procurar hashtag: #%@</target> <target>Procurar hashtag: #%@</target>
@@ -1069,17 +930,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text prompt for user to send a message to the other user.</note> <note>Text prompt for user to send a message to the other user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<target>Servidor</target>
<note>Prompt selection of LibreTranslate server to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Service" xml:space="preserve">
<source>Service</source>
<note>Prompt selection of translation service provider.</note>
</trans-unit>
<trans-unit id="Settings" xml:space="preserve"> <trans-unit id="Settings" xml:space="preserve">
<source>Settings</source> <source>Settings</source>
<target>Configurações</target> <target>Configurações</target>
@@ -1091,23 +941,14 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Share</source> <source>Share</source>
<target>Partilhar</target> <target>Partilhar</target>
<note>Button to share a post <note>Button to share an image.
Button to share an image.
Button to share the link to a profile.</note> Button to share the link to a profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show" xml:space="preserve"> <trans-unit id="Show" xml:space="preserve">
<source>Show</source> <source>Show</source>
<target>Ver</target> <target>Ver</target>
<note>Button to show a post from a user who has been blocked. <note>Toggle to show or hide user's secret account login key.</note>
Toggle to show or hide user's secret account login key.</note>
</trans-unit>
<trans-unit id="Show API Key" xml:space="preserve">
<source>Show API Key</source>
<target>Mostrar Chave API</target>
<note>Button to show the DeepL translation API key.
Button to show the LibreTranslate server API key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve"> <trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source> <source>Show wallet selector</source>
@@ -1121,22 +962,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Sidebar menu label to sign out of the account.</note> <note>Sidebar menu label to sign out of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Software" xml:space="preserve">
<source>Software</source>
<note>Label to display relay software.</note>
</trans-unit>
<trans-unit id="Strike" xml:space="preserve"> <trans-unit id="Strike" xml:space="preserve">
<source>Strike</source> <source>Strike</source>
<target>Strike</target> <target>Strike</target>
<note>Dropdown option label for Lightning wallet, Strike.</note> <note>Dropdown option label for Lightning wallet, Strike.</note>
</trans-unit> </trans-unit>
<trans-unit id="Supported NIPs" xml:space="preserve">
<source>Supported NIPs</source>
<note>Label to display relay's supported NIPs.</note>
</trans-unit>
<trans-unit id="Thanks!" xml:space="preserve"> <trans-unit id="Thanks!" xml:space="preserve">
<source>Thanks!</source> <source>Thanks!</source>
<target>Obrigado!</target> <target>Obrigado!</target>
@@ -1180,45 +1011,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation bar title for note thread. <note>Navigation bar title for note thread.
Navigation bar title for threaded event detail view.</note> Navigation bar title for threaded event detail view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Translate Note" xml:space="preserve">
<source>Translate Note</source>
<target>Traduzir Note</target>
<note>Button to translate note from different language.</note>
</trans-unit>
<trans-unit id="Translated from (lang)" xml:space="preserve">
<source>Translated from (lang)</source>
<target>Traduzido de (lang)</target>
<note>Button to indicate that the note has been translated from a different language.</note>
</trans-unit>
<trans-unit id="Translating from (lang)..." xml:space="preserve">
<source>Translating from (lang)...</source>
<note>Button to indicate that the note is in the process of being translated from a different language.</note>
</trans-unit>
<trans-unit id="Translations" xml:space="preserve">
<source>Translations</source>
<note>Section title for selecting the translation service.</note>
</trans-unit>
<trans-unit id="Type DELETE to delete" xml:space="preserve">
<source>Type DELETE to delete</source>
<note>Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should.</note>
</trans-unit>
<trans-unit id="Type your post here..." xml:space="preserve"> <trans-unit id="Type your post here..." xml:space="preserve">
<source>Type your post here...</source> <source>Type your post here...</source>
<target>Escreva aqui o seu post...</target> <target>Escreva aqui o seu post...</target>
<note>Text box prompt to ask user to type their post.</note> <note>Text box prompt to ask user to type their post.</note>
</trans-unit> </trans-unit>
<trans-unit id="URL" xml:space="preserve">
<source>URL</source>
<target>URL</target>
<note>Example URL to LibreTranslate server</note>
</trans-unit>
<trans-unit id="Unfollow" xml:space="preserve"> <trans-unit id="Unfollow" xml:space="preserve">
<source>Unfollow</source> <source>Unfollow</source>
<target>Deixar de Seguir</target> <target>Deixar de Seguir</target>
@@ -1247,7 +1045,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>User blocked</source> <source>User blocked</source>
<target>Utilizador bloqueado</target> <target>Utilizador bloqueado</target>
<note>Alert message to indicate the user has been blocked</note> <note>Alert message to indicate</note>
</trans-unit> </trans-unit>
<trans-unit id="User has been blocked" xml:space="preserve"> <trans-unit id="User has been blocked" xml:space="preserve">
<source>User has been blocked</source> <source>User has been blocked</source>
@@ -1262,29 +1060,24 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for Username section of user profile form. <note>Label for Username section of user profile form.
Label to prompt username entry.</note> Label to prompt username entry.</note>
</trans-unit> </trans-unit>
<trans-unit id="Version" xml:space="preserve">
<source>Version</source>
<note>Label to display relay software version.</note>
</trans-unit>
<trans-unit id="Wallet" xml:space="preserve"> <trans-unit id="Wallet" xml:space="preserve">
<source>Wallet</source> <source>Wallet</source>
<target>Carteira</target> <target>Carteira</target>
<note>Sidebar menu label for Wallet view.</note> <note>Sidebar menu label for Wallet view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
<source>Wallet Of Satoshi</source>
<target>Wallet Of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
</trans-unit>
<trans-unit id="Wallet Selector" xml:space="preserve"> <trans-unit id="Wallet Selector" xml:space="preserve">
<source>Wallet Selector</source> <source>Wallet Selector</source>
<target>Selector de Carteira</target> <target>Selector de Carteira</target>
<note>Section title for selection of wallet.</note> <note>Section title for selection of wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet of Satoshi" xml:space="preserve">
<source>Wallet of Satoshi</source>
<target>Wallet of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet of Satoshi.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve"> <trans-unit id="Website" xml:space="preserve">
<source>Website</source> <source>Website</source>
<target>Página Web</target> <target>Página Web</target>
@@ -1315,11 +1108,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text of button that confirms to overwrite the existing mutelist.</note> <note>Text of button that confirms to overwrite the existing mutelist.</note>
</trans-unit> </trans-unit>
<trans-unit id="Yes, Post with Private Key" xml:space="preserve">
<source>Yes, Post with Private Key</source>
<note>Button to proceed with posting a note even though it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve"> <trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source> <source>Your Name</source>
<target>O Seu Nome</target> <target>O Seu Nome</target>
@@ -1332,11 +1120,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Footer text to inform user what will happen when the report is submitted.</note> <note>Footer text to inform user what will happen when the report is submitted.</note>
</trans-unit> </trans-unit>
<trans-unit id="Zap" xml:space="preserve">
<source>Zap</source>
<note>Accessibility label for zap button</note>
</trans-unit>
<trans-unit id="Zebedee" xml:space="preserve"> <trans-unit id="Zebedee" xml:space="preserve">
<source>Zebedee</source> <source>Zebedee</source>
<target>Zebedee</target> <target>Zebedee</target>
@@ -1351,13 +1134,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve"> <trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve">
<source>collapsed_event_view_other_notes</source> <source>collapsed_event_view_other_notes</source>
<target>collapsed_event_view_other_notes</target> <target>colapso_evento_visualizar_outras_notas</target>
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped. (Key in .stringsdict)</note> <note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="followers_count" translate="no" xml:space="preserve"> <trans-unit id="followers_count" translate="no" xml:space="preserve">
<source>followers_count</source> <source>followers_count</source>
<target>followers_count</target> <target>contagem_de_seguidores</target>
<note>Part of a larger sentence to describe how many people are following a user. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many people are following a user. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
@@ -1381,7 +1164,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="none" xml:space="preserve"> <trans-unit id="none" xml:space="preserve">
<source>none</source> <source>none</source>
<target>nada</target> <target> nada</target>
<note>No search results.</note> <note>No search results.</note>
</trans-unit> </trans-unit>
@@ -1405,32 +1188,27 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="reactions_count" translate="no" xml:space="preserve"> <trans-unit id="reactions_count" translate="no" xml:space="preserve">
<source>reactions_count</source> <source>reactions_count</source>
<target>reactions_count</target> <target>contagem_reações</target>
<note>Part of a larger sentence to describe how many reactions there are on a post. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many reactions there are on a post. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="relays_count" translate="no" xml:space="preserve"> <trans-unit id="relays_count" translate="no" xml:space="preserve">
<source>relays_count</source> <source>relays_count</source>
<target>relays_count</target> <target>contagem_relays</target>
<note>Part of a larger sentence to describe how many relay servers a user is connected. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many relay servers a user is connected. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="replying_to_one_and_others" translate="no" xml:space="preserve"> <trans-unit id="replying_to_one_and_others" translate="no" xml:space="preserve">
<source>replying_to_one_and_others</source> <source>replying_to_one_and_others</source>
<target>replying_to_one_and_others</target> <target>respondendo_a_um_e_outros</target>
<note>Label to indicate that the user is replying to 1 user and others. (Key in .stringsdict)</note> <note>Label to indicate that the user is replying to 1 user and others. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="replying_to_two_and_others" translate="no" xml:space="preserve"> <trans-unit id="replying_to_two_and_others" translate="no" xml:space="preserve">
<source>replying_to_two_and_others</source> <source>replying_to_two_and_others</source>
<target>replying_to_two_and_others</target> <target>respondendo_a_dois_e_outros</target>
<note>Label to indicate that the user is replying to 2 users and others. (Key in .stringsdict)</note> <note>Label to indicate that the user is replying to 2 users and others. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="reposts_count" translate="no" xml:space="preserve"> <trans-unit id="reposts_count" translate="no" xml:space="preserve">
<source>reposts_count</source> <source>reposts_count</source>
<target>reposts_count</target> <target>contagem_reposts</target>
<note>Part of a larger sentence to describe how many reposts there are. (Key in .stringsdict)</note> <note>Part of a larger sentence to describe how many reposts there are. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="satoshi" xml:space="preserve"> <trans-unit id="satoshi" xml:space="preserve">
@@ -1441,10 +1219,14 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="sats_count" translate="no" xml:space="preserve"> <trans-unit id="sats_count" translate="no" xml:space="preserve">
<source>sats_count</source> <source>sats_count</source>
<target>sats_count</target> <target>contagem_sats</target>
<note>Amount of sats. (Key in .stringsdict)</note> <note>Amount of sats. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="tips_count" translate="no" xml:space="preserve">
<source>tips_count</source>
<target>contagem_grojetas</target>
<note>Part of a larger sentence to describe how many tip payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="u{00A0}" xml:space="preserve"> <trans-unit id="u{00A0}" xml:space="preserve">
<source>u{00A0}</source> <source>u{00A0}</source>
<target>u{00A0}</target> <target>u{00A0}</target>
@@ -1463,18 +1245,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note> <note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
</trans-unit> </trans-unit>
<trans-unit id="zaps_count" translate="no" xml:space="preserve">
<source>zaps_count</source>
<target>zaps_count</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="⚡️ %@" xml:space="preserve">
<source>⚡️ %@</source>
<target>⚡️ %@</target>
<note>Text indicating the zap amount. i.e. number of satoshis that were tipped to a user</note>
</trans-unit>
</body> </body>
</file> </file>
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="pt-PT" datatype="plaintext"> <file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="pt-PT" datatype="plaintext">
@@ -1514,13 +1284,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="/followers_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/followers_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@FOLLOWERS@</source> <source>%#@FOLLOWERS@</source>
<target>%#@FOLLOWERS@</target> <target>%#@SEGUIDORES@</target>
<note>Part of a larger sentence to describe how many people are following a user.</note> <note>Part of a larger sentence to describe how many people are following a user.</note>
</trans-unit> </trans-unit>
<trans-unit id="/reactions_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/reactions_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@REACTIONS@</source> <source>%#@REACTIONS@</source>
<target>%#@REACTIONS@</target> <target>%#@REAÇÕES@</target>
<note>Part of a larger sentence to describe how many reactions there are on a post.</note> <note>Part of a larger sentence to describe how many reactions there are on a post.</note>
</trans-unit> </trans-unit>
@@ -1556,8 +1326,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>Replying to %@%#@OTHERS@</source> <source>Replying to %@%#@OTHERS@</source>
<target>Respondendo a %@%#@OTHERS@</target> <target>Respondendo a %@%#@OUTROS@</target>
<note>Label to indicate that the user is replying to 1 user and others.</note> <note>Label to indicate that the user is replying to 1 user and others.</note>
</trans-unit> </trans-unit>
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
@@ -1578,8 +1347,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="/replying_to_two_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/replying_to_two_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>Replying to %@, %@%#@OTHERS@</source> <source>Replying to %@, %@%#@OTHERS@</source>
<target>Respondendo a %@, %@%#@OTHERS@</target> <target>Respondendo a %@, %@%#@OUTROS@</target>
<note>Label to indicate that the user is replying to 2 users and others.</note> <note>Label to indicate that the user is replying to 2 users and others.</note>
</trans-unit> </trans-unit>
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
@@ -1634,22 +1402,21 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats.</note> <note>Amount of sats.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@ZAPS@</source> <source>%#@TIPS@</source>
<target>%#@ZAPS@</target> <target>%#@GROJETAS@</target>
<note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/one:dict/:string" xml:space="preserve">
<source>Zap</source> <source>Tip</source>
<target>Grojeta</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/other:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/other:dict/:string" xml:space="preserve">
<source>Zaps</source> <source>Tips</source>
<target>Gorjetas</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>
@@ -16,13 +16,9 @@
<note>Bundle name</note> <note>Bundle name</note>
</trans-unit> </trans-unit>
<trans-unit id="NSFaceIDUsageDescription" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Privacy - Face ID Usage Description</note>
</trans-unit>
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve"> <trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
<source>Granting Damus access to your photos allows you to save images.</source> <source>&quot;Granting Damus access to your photo library allows you to save photos.</source>
<target>Damus'a fotoğraf kitaplığınıza erişim izni vermek, fotoğrafları kaydetmenize olanak tanır.</target>
<note>Privacy - Photo Library Additions Usage Description</note> <note>Privacy - Photo Library Additions Usage Description</note>
</trans-unit> </trans-unit>
@@ -42,19 +38,15 @@
<source>%@</source> <source>%@</source>
<target>%@</target> <target>%@</target>
<note>Abbreviated version of a nostr public key.</note> <note>Amount of time that has passed since reply quote event occurred.
Abbreviated version of a nostr public key.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ %@" xml:space="preserve"> <trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source> <source>%@ %@</source>
<target>%@ %@</target> <target>%@ %@</target>
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'. <note>Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.
Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.</note> Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.</note>
</trans-unit>
<trans-unit id="%@ has been blocked" xml:space="preserve">
<source>%@ has been blocked</source>
<note>Alert message that informs a user was blocked.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." xml:space="preserve"> <trans-unit id="%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." xml:space="preserve">
<source>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</source> <source>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</source>
@@ -78,8 +70,8 @@ Sentence composed of 2 variables to describe how many relay servers a user is co
<source>%lld</source> <source>%lld</source>
<target>%lld</target> <target>%lld</target>
<note>Number of zap payments on a post. <note>Number of reposts.
Number of relay servers a user is connected.</note> Number of profiles a user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld/%lld" xml:space="preserve"> <trans-unit id="%lld/%lld" xml:space="preserve">
<source>%lld/%lld</source> <source>%lld/%lld</source>
@@ -93,8 +85,9 @@ Number of relay servers a user is connected.</note>
<note>Description of how the nip05 identifier would be used for verification.</note> <note>Description of how the nip05 identifier would be used for verification.</note>
</trans-unit> </trans-unit>
<trans-unit id="'%@' is an invalid NIP-05 identifier. It should look like an email." xml:space="preserve"> <trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
<source>'%@' is an invalid NIP-05 identifier. It should look like an email.</source> <source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
<target>'%@' geçersiz bir nip05 tanımlayıcısı. Tanımlayıcı, e-posta gibi gözükmeli. </target>
<note>Description of why the nip05 identifier is invalid.</note> <note>Description of why the nip05 identifier is invalid.</note>
</trans-unit> </trans-unit>
@@ -122,16 +115,6 @@ Number of relay servers a user is connected.</note>
<note>Prefix character to username.</note> <note>Prefix character to username.</note>
</trans-unit> </trans-unit>
<trans-unit id="API Key (optional)" xml:space="preserve">
<source>API Key (optional)</source>
<note>Prompt for optional entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="API Key (required)" xml:space="preserve">
<source>API Key (required)</source>
<note>Prompt for required entry of API Key to use translation server.</note>
</trans-unit>
<trans-unit id="About" xml:space="preserve"> <trans-unit id="About" xml:space="preserve">
<source>About</source> <source>About</source>
<target>Hakkında</target> <target>Hakkında</target>
@@ -150,22 +133,12 @@ Number of relay servers a user is connected.</note>
<note>Placeholder text for About Me description.</note> <note>Placeholder text for About Me description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Accept" xml:space="preserve">
<source>Accept</source>
<note>Button to accept the end user license agreement before being allowed into the app.</note>
</trans-unit>
<trans-unit id="Account ID" xml:space="preserve"> <trans-unit id="Account ID" xml:space="preserve">
<source>Account ID</source> <source>Account ID</source>
<target>Hesap Kimliği</target> <target>Hesap Kimliği</target>
<note>Label to indicate the public ID of the account.</note> <note>Label to indicate the public ID of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Actions" xml:space="preserve">
<source>Actions</source>
<note>Title for confirmation dialog to either share, report, or block a profile.</note>
</trans-unit>
<trans-unit id="Add" xml:space="preserve"> <trans-unit id="Add" xml:space="preserve">
<source>Add</source> <source>Add</source>
<target>Ekle</target> <target>Ekle</target>
@@ -179,16 +152,6 @@ Number of relay servers a user is connected.</note>
<note>Label for section for adding a relay server.</note> <note>Label for section for adding a relay server.</note>
</trans-unit> </trans-unit>
<trans-unit id="Add all" xml:space="preserve">
<source>Add all</source>
<note>Button label to re-add all original participants as profiles to reply to in a note</note>
</trans-unit>
<trans-unit id="Admin" xml:space="preserve">
<source>Admin</source>
<note>Label to display relay contact user.</note>
</trans-unit>
<trans-unit id="Any" xml:space="preserve"> <trans-unit id="Any" xml:space="preserve">
<source>Any</source> <source>Any</source>
<target>Herhangi</target> <target>Herhangi</target>
@@ -231,44 +194,12 @@ Number of relay servers a user is connected.</note>
<note>Dropdown option label for Lightning wallet, Blixt Wallet</note> <note>Dropdown option label for Lightning wallet, Blixt Wallet</note>
</trans-unit> </trans-unit>
<trans-unit id="Block" xml:space="preserve">
<source>Block</source>
<note>Alert button to block a user.
Button to block a profile.
Context menu option for blocking users.</note>
</trans-unit>
<trans-unit id="Block %@?" xml:space="preserve">
<source>Block %@?</source>
<note>Alert message prompt to ask if a user should be blocked.</note>
</trans-unit>
<trans-unit id="Block User" xml:space="preserve">
<source>Block User</source>
<note>Title of alert for blocking a user.</note>
</trans-unit>
<trans-unit id="Blocked" xml:space="preserve">
<source>Blocked</source>
<note>Sidebar menu label for Profile view.</note>
</trans-unit>
<trans-unit id="Blocked Users" xml:space="preserve">
<source>Blocked Users</source>
<note>Navigation title of view to see list of blocked users.</note>
</trans-unit>
<trans-unit id="Blue Wallet" xml:space="preserve"> <trans-unit id="Blue Wallet" xml:space="preserve">
<source>Blue Wallet</source> <source>Blue Wallet</source>
<target>Blue Wallet</target> <target>Blue Wallet</target>
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note> <note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Boosts" xml:space="preserve">
<source>Boosts</source>
<note>Accessibility label for boosts button</note>
</trans-unit>
<trans-unit id="Breez" xml:space="preserve"> <trans-unit id="Breez" xml:space="preserve">
<source>Breez</source> <source>Breez</source>
<target>Breez</target> <target>Breez</target>
@@ -285,12 +216,9 @@ Number of relay servers a user is connected.</note>
<source>Cancel</source> <source>Cancel</source>
<target>İptal Et</target> <target>İptal Et</target>
<note>Alert button to cancel out of alert for blocking a user. <note>Button to cancel out of posting a note.
Button to cancel out of alert that creates a new mutelist.
Button to cancel out of posting a note.
Button to cancel out of reposting a post. Button to cancel out of reposting a post.
Button to cancel out of view adding user inputted relay. Button to cancel out of view adding user inputted relay.
Cancel deleting the user.
Cancel out of logging out the user.</note> Cancel out of logging out the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cash App" xml:space="preserve"> <trans-unit id="Cash App" xml:space="preserve">
@@ -317,11 +245,6 @@ Number of relay servers a user is connected.</note>
<note>Section title for clearing cached data.</note> <note>Section title for clearing cached data.</note>
</trans-unit> </trans-unit>
<trans-unit id="Contact" xml:space="preserve">
<source>Contact</source>
<note>Label to display relay contact information.</note>
</trans-unit>
<trans-unit id="Copied" xml:space="preserve"> <trans-unit id="Copied" xml:space="preserve">
<source>Copied</source> <source>Copied</source>
<target>Kopyalandı</target> <target>Kopyalandı</target>
@@ -371,19 +294,15 @@ Number of relay servers a user is connected.</note>
<note>Context menu option for copying the JSON text from the note.</note> <note>Context menu option for copying the JSON text from the note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Copy Report ID" xml:space="preserve">
<source>Copy Report ID</source>
<note>Button to copy report ID.</note>
</trans-unit>
<trans-unit id="Copy Text" xml:space="preserve"> <trans-unit id="Copy Text" xml:space="preserve">
<source>Copy Text</source> <source>Copy Text</source>
<target>Metni Kopyala</target> <target>Metni Kopyala</target>
<note>Context menu option for copying the text from an note.</note> <note>Context menu option for copying the text from an note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Copy User Pubkey" xml:space="preserve"> <trans-unit id="Copy User ID" xml:space="preserve">
<source>Copy User Pubkey</source> <source>Copy User ID</source>
<target>Hesap Kimliğini Kopyala</target>
<note>Context menu option for copying the ID of the user who created the note.</note> <note>Context menu option for copying the ID of the user who created the note.</note>
</trans-unit> </trans-unit>
@@ -393,11 +312,6 @@ Number of relay servers a user is connected.</note>
<note>Title of section for copying a Lightning invoice identifier.</note> <note>Title of section for copying a Lightning invoice identifier.</note>
</trans-unit> </trans-unit>
<trans-unit id="Could not find user to block..." xml:space="preserve">
<source>Could not find user to block...</source>
<note>Alert message to indicate that the blocked user could not be found.</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve"> <trans-unit id="Create" xml:space="preserve">
<source>Create</source> <source>Create</source>
<target>Yarat</target> <target>Yarat</target>
@@ -410,27 +324,17 @@ Number of relay servers a user is connected.</note>
<note>Button to create an account.</note> <note>Button to create an account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create new mutelist" xml:space="preserve">
<source>Create new mutelist</source>
<note>Title of alert prompting the user to create a new mutelist.</note>
</trans-unit>
<trans-unit id="Creator(s) of Bitcoin. Absolute legend." xml:space="preserve"> <trans-unit id="Creator(s) of Bitcoin. Absolute legend." xml:space="preserve">
<source>Creator(s) of Bitcoin. Absolute legend.</source> <source>Creator(s) of Bitcoin. Absolute legend.</source>
<target>Bitcoin'in Yaratıcısı. Kral Adam.</target> <target>Bitcoin'in Yaratıcısı. Kral Adam.</target>
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note> <note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
</trans-unit> </trans-unit>
<trans-unit id="Custom" xml:space="preserve"> <trans-unit id="DM" xml:space="preserve">
<source>Custom</source> <source>DM</source>
<target>DM</target>
<note>Dropdown option for selecting a custom translation server.</note> <note>Navigation title for DM view, which is the English abbreviation for Direct Message.</note>
</trans-unit>
<trans-unit id="DMs" xml:space="preserve">
<source>DMs</source>
<note>Navigation title for DMs view, where DM is the English abbreviation for Direct Message.
Navigation title for view of DMs, where DM is an English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Damus" xml:space="preserve"> <trans-unit id="Damus" xml:space="preserve">
<source>Damus</source> <source>Damus</source>
@@ -438,11 +342,6 @@ Number of relay servers a user is connected.</note>
<note>Name of the app, shown on the first screen when user is not logged in.</note> <note>Name of the app, shown on the first screen when user is not logged in.</note>
</trans-unit> </trans-unit>
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
<source>DeepL (Proprietary, Higher Accuracy)</source>
<note>Dropdown option for selecting DeepL as the translation service.</note>
</trans-unit>
<trans-unit id="Default Wallet" xml:space="preserve"> <trans-unit id="Default Wallet" xml:space="preserve">
<source>Default Wallet</source> <source>Default Wallet</source>
<target>Varsayılan Cüzdan</target> <target>Varsayılan Cüzdan</target>
@@ -453,26 +352,7 @@ Number of relay servers a user is connected.</note>
<source>Delete</source> <source>Delete</source>
<target>Sil</target> <target>Sil</target>
<note>Button for deleting the users account. <note>Button to delete a relay server that the user connects to.</note>
Button to delete a relay server that the user connects to.
Button to remove a user from their blocklist.
Section title for deleting the user</note>
</trans-unit>
<trans-unit id="Delete Account" xml:space="preserve">
<source>Delete Account</source>
<note>Alert for deleting the users account.
Button to delete the user's account.</note>
</trans-unit>
<trans-unit id="Deleted Account" xml:space="preserve">
<source>Deleted Account</source>
<note>Alert message to indicate this is a deleted account</note>
</trans-unit>
<trans-unit id="Description" xml:space="preserve">
<source>Description</source>
<note>Label to display relay description.</note>
</trans-unit> </trans-unit>
<trans-unit id="Dismiss" xml:space="preserve"> <trans-unit id="Dismiss" xml:space="preserve">
<source>Dismiss</source> <source>Dismiss</source>
@@ -492,11 +372,6 @@ Number of relay servers a user is connected.</note>
<note>Button to dismiss wallet selection view for paying Lightning invoice.</note> <note>Button to dismiss wallet selection view for paying Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="EULA" xml:space="preserve">
<source>EULA</source>
<note>Label indicating that the below text is the EULA, an acronym for End User License Agreement.</note>
</trans-unit>
<trans-unit id="Earn Money" xml:space="preserve"> <trans-unit id="Earn Money" xml:space="preserve">
<source>Earn Money</source> <source>Earn Money</source>
<target>Para Kazan</target> <target>Para Kazan</target>
@@ -509,17 +384,18 @@ Number of relay servers a user is connected.</note>
<note>Button to edit user's profile.</note> <note>Button to edit user's profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Edit participants" xml:space="preserve">
<source>Edit participants</source>
<note>Text indicating that the view is used for editing which participants are replied to in a note.</note>
</trans-unit>
<trans-unit id="Encrypted" xml:space="preserve"> <trans-unit id="Encrypted" xml:space="preserve">
<source>Encrypted</source> <source>Encrypted</source>
<target>Şifreli</target> <target>Şifreli</target>
<note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note> <note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note>
</trans-unit> </trans-unit>
<trans-unit id="Encrypted DMs" xml:space="preserve">
<source>Encrypted DMs</source>
<target>Şifreli DM'ler</target>
<note>Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message.</note>
</trans-unit>
<trans-unit id="Enter your account key to login:" xml:space="preserve"> <trans-unit id="Enter your account key to login:" xml:space="preserve">
<source>Enter your account key to login:</source> <source>Enter your account key to login:</source>
<target>Giriş yapmak için hesap anahtarını gir:</target> <target>Giriş yapmak için hesap anahtarını gir:</target>
@@ -532,22 +408,18 @@ Number of relay servers a user is connected.</note>
<note>Error message indicating why saving keys failed.</note> <note>Error message indicating why saving keys failed.</note>
</trans-unit> </trans-unit>
<trans-unit id="Filter State" xml:space="preserve">
<source>Filter State</source>
<target>Filtre Durumu</target>
<note>Filter state for seeing either only posts, or posts &amp; replies.</note>
</trans-unit>
<trans-unit id="Follow" xml:space="preserve"> <trans-unit id="Follow" xml:space="preserve">
<source>Follow</source> <source>Follow</source>
<target>Takip Et</target> <target>Takip Et</target>
<note>Button to follow a user.</note> <note>Button to follow a user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follow Back" xml:space="preserve">
<source>Follow Back</source>
<note>Button to follow a user back.</note>
</trans-unit>
<trans-unit id="Follow me on nostr" xml:space="preserve">
<source>Follow me on nostr</source>
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve"> <trans-unit id="Followers" xml:space="preserve">
<source>Followers</source> <source>Followers</source>
<target>Takipçiler</target> <target>Takipçiler</target>
@@ -573,21 +445,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note> <note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="Follows you" xml:space="preserve">
<source>Follows you</source>
<note>Text to indicate that a user is following your profile.</note>
</trans-unit>
<trans-unit id="Free" xml:space="preserve">
<source>Free</source>
<note>Dropdown option for selecting Free plan for DeepL translation service.</note>
</trans-unit>
<trans-unit id="Get API Key" xml:space="preserve">
<source>Get API Key</source>
<note>Button to navigate to DeepL website to get a translation API key.</note>
</trans-unit>
<trans-unit id="Global" xml:space="preserve"> <trans-unit id="Global" xml:space="preserve">
<source>Global</source> <source>Global</source>
<target>Küresel</target> <target>Küresel</target>
@@ -606,39 +463,18 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation link to go to profile.</note> <note>Navigation link to go to profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Hide" xml:space="preserve">
<source>Hide</source>
<note>Button to hide a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Hide API Key" xml:space="preserve">
<source>Hide API Key</source>
<note>Button to hide the DeepL translation API key.
Button to hide the LibreTranslate server API key.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve"> <trans-unit id="Home" xml:space="preserve">
<source>Home</source> <source>Home</source>
<target>Ev</target> <target>Ev</target>
<note>Navigation bar title for Home view where posts and replies appear from those who the user is following.</note> <note>Navigation bar title for Home view where posts and replies appear from those who the user is following.</note>
</trans-unit> </trans-unit>
<trans-unit id="Illegal content" xml:space="preserve">
<source>Illegal content</source>
<note>Button for user to report that the account or content has illegal content.</note>
</trans-unit>
<trans-unit id="Invalid key" xml:space="preserve"> <trans-unit id="Invalid key" xml:space="preserve">
<source>Invalid key</source> <source>Invalid key</source>
<target>Hatalı anahtar</target> <target>Hatalı anahtar</target>
<note>Error message indicating that an invalid account key was entered for login.</note> <note>Error message indicating that an invalid account key was entered for login.</note>
</trans-unit> </trans-unit>
<trans-unit id="It's spam" xml:space="preserve">
<source>It's spam</source>
<note>Button for user to report that the account or content has spam.</note>
</trans-unit>
<trans-unit id="LNLink" xml:space="preserve"> <trans-unit id="LNLink" xml:space="preserve">
<source>LNLink</source> <source>LNLink</source>
<target>LNLink</target> <target>LNLink</target>
@@ -657,11 +493,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Button to complete account creation and start using the app.</note> <note>Button to complete account creation and start using the app.</note>
</trans-unit> </trans-unit>
<trans-unit id="LibreTranslate (Open Source)" xml:space="preserve">
<source>LibreTranslate (Open Source)</source>
<note>Dropdown option for selecting LibreTranslate as the translation service.</note>
</trans-unit>
<trans-unit id="Lightning Address or LNURL" xml:space="preserve"> <trans-unit id="Lightning Address or LNURL" xml:space="preserve">
<source>Lightning Address or LNURL</source> <source>Lightning Address or LNURL</source>
<target>Lightning Adresi ya da LNURL</target> <target>Lightning Adresi ya da LNURL</target>
@@ -674,16 +505,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Indicates that the view is for paying a Lightning invoice.</note> <note>Indicates that the view is for paying a Lightning invoice.</note>
</trans-unit> </trans-unit>
<trans-unit id="Like" xml:space="preserve">
<source>Like</source>
<note>Accessibility Label for Like button</note>
</trans-unit>
<trans-unit id="Local authentication to access private key" xml:space="preserve">
<source>Local authentication to access private key</source>
<note>Face ID usage description shown when trying to access private key</note>
</trans-unit>
<trans-unit id="Local default" xml:space="preserve"> <trans-unit id="Local default" xml:space="preserve">
<source>Local default</source> <source>Local default</source>
<target>Cihaz varsayılanı</target> <target>Cihaz varsayılanı</target>
@@ -703,7 +524,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Alert for logging out the user. <note>Alert for logging out the user.
Button for logging out the user. Button for logging out the user.
Button to close the alert that informs that the current account has been deleted.</note> Button to logout the user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve"> <trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source> <source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
@@ -723,26 +544,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for NIP-05 Verification section of user profile form.</note> <note>Label for NIP-05 Verification section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<note>Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="No block list found, create a new one? This will overwrite any previous block lists." xml:space="preserve">
<source>No block list found, create a new one? This will overwrite any previous block lists.</source>
<note>Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists.</note>
</trans-unit>
<trans-unit id="None" xml:space="preserve">
<source>None</source>
<note>Dropdown option for selecting no translation service.</note>
</trans-unit>
<trans-unit id="Note contains &quot;nsec1&quot; private key. Are you sure?" xml:space="preserve">
<source>Note contains &quot;nsec1&quot; private key. Are you sure?</source>
<note>Alert user that they might be attempting to paste a private key and ask them to confirm.</note>
</trans-unit>
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve"> <trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
<source>Nothing to see here. Check back later!</source> <source>Nothing to see here. Check back later!</source>
<target>Görülecek bir şey yok. Daha sonra tekrar kontrol et!</target> <target>Görülecek bir şey yok. Daha sonra tekrar kontrol et!</target>
@@ -755,11 +556,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation title for notifications.</note> <note>Navigation title for notifications.</note>
</trans-unit> </trans-unit>
<trans-unit id="Nudity or explicit content" xml:space="preserve">
<source>Nudity or explicit content</source>
<note>Button for user to report that the account or content has nudity or explicit content.</note>
</trans-unit>
<trans-unit id="Pay" xml:space="preserve"> <trans-unit id="Pay" xml:space="preserve">
<source>Pay</source> <source>Pay</source>
<target>Öde</target> <target>Öde</target>
@@ -778,22 +574,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Dropdown option label for Lightning wallet, Phoenix.</note> <note>Dropdown option label for Lightning wallet, Phoenix.</note>
</trans-unit> </trans-unit>
<trans-unit id="Plan" xml:space="preserve">
<source>Plan</source>
<note>Prompt selection of DeepL subscription plan to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Post" xml:space="preserve"> <trans-unit id="Post" xml:space="preserve">
<source>Post</source> <source>Post</source>
<target>Gönder</target> <target>Gönder</target>
<note>Button to post a note.</note> <note>Button to post a note.</note>
</trans-unit> </trans-unit>
<trans-unit id="Post from a user you've blocked" xml:space="preserve">
<source>Post from a user you've blocked</source>
<note>Text to indicate that what is being shown is a post from a user who has been blocked.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve"> <trans-unit id="Posts" xml:space="preserve">
<source>Posts</source> <source>Posts</source>
<target>Gönderiler</target> <target>Gönderiler</target>
@@ -816,12 +602,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Private Key</source> <source>Private Key</source>
<target>Özel Anahtar</target> <target>Özel Anahtar</target>
<note>Title of the secure field that holds the user's private key.</note> <note>Label to indicate that the text below is the user's private key used by only the user themself as a secret to login to access their account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Pro" xml:space="preserve"> <trans-unit id="PrivateKey" xml:space="preserve">
<source>Pro</source> <source>PrivateKey</source>
<target>Özel Anahtar</target>
<note>Dropdown option for selecting Pro plan for DeepL translation service.</note> <note>Title of the secure field that holds the user's private key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Profile" xml:space="preserve"> <trans-unit id="Profile" xml:space="preserve">
<source>Profile</source> <source>Profile</source>
@@ -871,39 +658,17 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Section title for recommend relay servers that could be added as part of configuration</note> <note>Section title for recommend relay servers that could be added as part of configuration</note>
</trans-unit> </trans-unit>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<note>Button to reject the end user license agreement, which disallows the user from being let into the app.</note>
</trans-unit>
<trans-unit id="Relay" xml:space="preserve"> <trans-unit id="Relay" xml:space="preserve">
<source>Relay</source> <source>Relay</source>
<target>Röle</target> <target>Röle</target>
<note>Label to display relay address. <note>Text field for relay server. Used for testing purposes.</note>
Text field for relay server. Used for testing purposes.</note>
</trans-unit> </trans-unit>
<trans-unit id="Relays" xml:space="preserve"> <trans-unit id="Relays" xml:space="preserve">
<source>Relays</source> <source>Relays</source>
<target>Röleler</target> <target>Röleler</target>
<note>Sidebar menu label for Relay servers view <note>Sidebar menu label for Relay servers view</note>
Sidebar menu label for Relays view.</note>
</trans-unit>
<trans-unit id="Relays have been notified and clients will be able to use this information to filter content. Thank you!" xml:space="preserve">
<source>Relays have been notified and clients will be able to use this information to filter content. Thank you!</source>
<note>Description of what was done as a result of sending a report to relay servers.</note>
</trans-unit>
<trans-unit id="Remove all" xml:space="preserve">
<source>Remove all</source>
<note>Button label to remove all participants from a note reply.</note>
</trans-unit>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<note>Accessibility label for reply button</note>
</trans-unit> </trans-unit>
<trans-unit id="Reply to self" xml:space="preserve"> <trans-unit id="Reply to self" xml:space="preserve">
<source>Reply to self</source> <source>Reply to self</source>
@@ -923,22 +688,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Indicating that the user is replying to the following listed people.</note> <note>Indicating that the user is replying to the following listed people.</note>
</trans-unit> </trans-unit>
<trans-unit id="Report" xml:space="preserve">
<source>Report</source>
<note>Button to report a profile.
Context menu option for reporting content.</note>
</trans-unit>
<trans-unit id="Report ID:" xml:space="preserve">
<source>Report ID:</source>
<note>Label indicating that the text underneath is the identifier of the report that was sent to relay servers.</note>
</trans-unit>
<trans-unit id="Report sent!" xml:space="preserve">
<source>Report sent!</source>
<note>Message indicating that a report was successfully sent to relay servers.</note>
</trans-unit>
<trans-unit id="Repost" xml:space="preserve"> <trans-unit id="Repost" xml:space="preserve">
<source>Repost</source> <source>Repost</source>
<target>Yinele</target> <target>Yinele</target>
@@ -952,15 +701,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text indicating that the post was reposted (i.e. re-shared).</note> <note>Text indicating that the post was reposted (i.e. re-shared).</note>
</trans-unit> </trans-unit>
<trans-unit id="Reposts" xml:space="preserve"> <trans-unit id="Reset" xml:space="preserve">
<source>Reposts</source> <source>Reset</source>
<target>Sıfırla</target>
<note>Navigation bar title for Reposts view.</note> <note>Section title for resetting the user</note>
</trans-unit>
<trans-unit id="Requests" xml:space="preserve">
<source>Requests</source>
<note>Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.</note>
</trans-unit> </trans-unit>
<trans-unit id="Retry" xml:space="preserve"> <trans-unit id="Retry" xml:space="preserve">
<source>Retry</source> <source>Retry</source>
@@ -992,11 +737,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Context menu option to save an image.</note> <note>Context menu option to save an image.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan the code" xml:space="preserve">
<source>Scan the code</source>
<note>Text on QR code view to prompt viewer to scan the QR code on screen with their device camera.</note>
</trans-unit>
<trans-unit id="Search hashtag: #%@" xml:space="preserve"> <trans-unit id="Search hashtag: #%@" xml:space="preserve">
<source>Search hashtag: #%@</source> <source>Search hashtag: #%@</source>
<target>Etiketi ara: #%@</target> <target>Etiketi ara: #%@</target>
@@ -1033,16 +773,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text prompt for user to send a message to the other user.</note> <note>Text prompt for user to send a message to the other user.</note>
</trans-unit> </trans-unit>
<trans-unit id="Server" xml:space="preserve">
<source>Server</source>
<note>Prompt selection of LibreTranslate server to perform machine translations on notes</note>
</trans-unit>
<trans-unit id="Service" xml:space="preserve">
<source>Service</source>
<note>Prompt selection of translation service provider.</note>
</trans-unit>
<trans-unit id="Settings" xml:space="preserve"> <trans-unit id="Settings" xml:space="preserve">
<source>Settings</source> <source>Settings</source>
<target>Ayarlar</target> <target>Ayarlar</target>
@@ -1054,22 +784,13 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<source>Share</source> <source>Share</source>
<target>Paylaş</target> <target>Paylaş</target>
<note>Button to share a post <note>Button to share an image.</note>
Button to share an image.
Button to share the link to a profile.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show" xml:space="preserve"> <trans-unit id="Show" xml:space="preserve">
<source>Show</source> <source>Show</source>
<target>Göster</target> <target>Göster</target>
<note>Button to show a post from a user who has been blocked. <note>Toggle to show or hide user's secret account login key.</note>
Toggle to show or hide user's secret account login key.</note>
</trans-unit>
<trans-unit id="Show API Key" xml:space="preserve">
<source>Show API Key</source>
<note>Button to show the DeepL translation API key.
Button to show the LibreTranslate server API key.</note>
</trans-unit> </trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve"> <trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source> <source>Show wallet selector</source>
@@ -1083,32 +804,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Sidebar menu label to sign out of the account.</note> <note>Sidebar menu label to sign out of the account.</note>
</trans-unit> </trans-unit>
<trans-unit id="Software" xml:space="preserve">
<source>Software</source>
<note>Label to display relay software.</note>
</trans-unit>
<trans-unit id="Strike" xml:space="preserve"> <trans-unit id="Strike" xml:space="preserve">
<source>Strike</source> <source>Strike</source>
<target>Strike</target> <target>Strike</target>
<note>Dropdown option label for Lightning wallet, Strike.</note> <note>Dropdown option label for Lightning wallet, Strike.</note>
</trans-unit> </trans-unit>
<trans-unit id="Supported NIPs" xml:space="preserve">
<source>Supported NIPs</source>
<note>Label to display relay's supported NIPs.</note>
</trans-unit>
<trans-unit id="Thanks!" xml:space="preserve">
<source>Thanks!</source>
<note>Button to close out of alert that informs that the action to block a user was successful.</note>
</trans-unit>
<trans-unit id="They are impersonating someone" xml:space="preserve">
<source>They are impersonating someone</source>
<note>Button for user to report that the account is impersonating someone.</note>
</trans-unit>
<trans-unit id="This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." xml:space="preserve"> <trans-unit id="This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." xml:space="preserve">
<source>This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective.</source> <source>This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective.</source>
<target>Bu herkese açık bir anahtardır, herhangi bir şekilde gönderi yapamaz veya etkileşimde bulunamazsınız. Bu, hesapları kendi bakış açılarından görüntülemek için kullanılır.</target> <target>Bu herkese açık bir anahtardır, herhangi bir şekilde gönderi yapamaz veya etkileşimde bulunamazsınız. Bu, hesapları kendi bakış açılarından görüntülemek için kullanılır.</target>
@@ -1140,42 +841,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Navigation bar title for note thread. <note>Navigation bar title for note thread.
Navigation bar title for threaded event detail view.</note> Navigation bar title for threaded event detail view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Translate Note" xml:space="preserve">
<source>Translate Note</source>
<note>Button to translate note from different language.</note>
</trans-unit>
<trans-unit id="Translated from (lang)" xml:space="preserve">
<source>Translated from (lang)</source>
<note>Button to indicate that the note has been translated from a different language.</note>
</trans-unit>
<trans-unit id="Translating from (lang)..." xml:space="preserve">
<source>Translating from (lang)...</source>
<note>Button to indicate that the note is in the process of being translated from a different language.</note>
</trans-unit>
<trans-unit id="Translations" xml:space="preserve">
<source>Translations</source>
<note>Section title for selecting the translation service.</note>
</trans-unit>
<trans-unit id="Type DELETE to delete" xml:space="preserve">
<source>Type DELETE to delete</source>
<note>Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should.</note>
</trans-unit>
<trans-unit id="Type your post here..." xml:space="preserve"> <trans-unit id="Type your post here..." xml:space="preserve">
<source>Type your post here...</source> <source>Type your post here...</source>
<target>Gönderinizi buraya yazın...</target> <target>Gönderinizi buraya yazın...</target>
<note>Text box prompt to ask user to type their post.</note> <note>Text box prompt to ask user to type their post.</note>
</trans-unit> </trans-unit>
<trans-unit id="URL" xml:space="preserve">
<source>URL</source>
<note>Example URL to LibreTranslate server</note>
</trans-unit>
<trans-unit id="Unfollow" xml:space="preserve"> <trans-unit id="Unfollow" xml:space="preserve">
<source>Unfollow</source> <source>Unfollow</source>
<target>Takipten Çık</target> <target>Takipten Çık</target>
@@ -1200,16 +871,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.</note> <note>Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.</note>
</trans-unit> </trans-unit>
<trans-unit id="User blocked" xml:space="preserve">
<source>User blocked</source>
<note>Alert message to indicate the user has been blocked</note>
</trans-unit>
<trans-unit id="User has been blocked" xml:space="preserve">
<source>User has been blocked</source>
<note>Alert message that informs a user was blocked.</note>
</trans-unit>
<trans-unit id="Username" xml:space="preserve"> <trans-unit id="Username" xml:space="preserve">
<source>Username</source> <source>Username</source>
<target>Kullanıcı Adı</target> <target>Kullanıcı Adı</target>
@@ -1217,29 +878,24 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Label for Username section of user profile form. <note>Label for Username section of user profile form.
Label to prompt username entry.</note> Label to prompt username entry.</note>
</trans-unit> </trans-unit>
<trans-unit id="Version" xml:space="preserve">
<source>Version</source>
<note>Label to display relay software version.</note>
</trans-unit>
<trans-unit id="Wallet" xml:space="preserve"> <trans-unit id="Wallet" xml:space="preserve">
<source>Wallet</source> <source>Wallet</source>
<target>Cüzdan</target> <target>Cüzdan</target>
<note>Sidebar menu label for Wallet view.</note> <note>Sidebar menu label for Wallet view.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
<source>Wallet Of Satoshi</source>
<target>Wallet Of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
</trans-unit>
<trans-unit id="Wallet Selector" xml:space="preserve"> <trans-unit id="Wallet Selector" xml:space="preserve">
<source>Wallet Selector</source> <source>Wallet Selector</source>
<target>Cüzdan Seçici</target> <target>Cüzdan Seçici</target>
<note>Section title for selection of wallet.</note> <note>Section title for selection of wallet.</note>
</trans-unit> </trans-unit>
<trans-unit id="Wallet of Satoshi" xml:space="preserve">
<source>Wallet of Satoshi</source>
<target>Wallet of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet of Satoshi.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve"> <trans-unit id="Website" xml:space="preserve">
<source>Website</source> <source>Website</source>
<target>Websitesi</target> <target>Websitesi</target>
@@ -1258,37 +914,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Text to welcome user.</note> <note>Text to welcome user.</note>
</trans-unit> </trans-unit>
<trans-unit id="What do you want to report?" xml:space="preserve">
<source>What do you want to report?</source>
<note>Header text to prompt user what issue they want to report.</note>
</trans-unit>
<trans-unit id="Yes, Overwrite" xml:space="preserve">
<source>Yes, Overwrite</source>
<note>Text of button that confirms to overwrite the existing mutelist.</note>
</trans-unit>
<trans-unit id="Yes, Post with Private Key" xml:space="preserve">
<source>Yes, Post with Private Key</source>
<note>Button to proceed with posting a note even though it looks like they might be posting a private key.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve"> <trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source> <source>Your Name</source>
<target>İsminiz</target> <target>İsminiz</target>
<note>Label for Your Name section of user profile form.</note> <note>Label for Your Name section of user profile form.</note>
</trans-unit> </trans-unit>
<trans-unit id="Your report will be sent to the relays you are connected to" xml:space="preserve">
<source>Your report will be sent to the relays you are connected to</source>
<note>Footer text to inform user what will happen when the report is submitted.</note>
</trans-unit>
<trans-unit id="Zap" xml:space="preserve">
<source>Zap</source>
<note>Accessibility label for zap button</note>
</trans-unit>
<trans-unit id="Zebedee" xml:space="preserve"> <trans-unit id="Zebedee" xml:space="preserve">
<source>Zebedee</source> <source>Zebedee</source>
<target>Zebedee</target> <target>Zebedee</target>
@@ -1315,7 +946,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="https://example.com/pic.jpg" xml:space="preserve"> <trans-unit id="https://example.com/pic.jpg" xml:space="preserve">
<source>https://example.com/pic.jpg</source> <source>https://example.com/pic.jpg</source>
<target>https://ornek.com/resim.jpg</target> <target>https://example.com/pic.jpg</target>
<note>Placeholder example text for profile picture URL.</note> <note>Placeholder example text for profile picture URL.</note>
</trans-unit> </trans-unit>
@@ -1397,6 +1028,12 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats. (Key in .stringsdict)</note> <note>Amount of sats. (Key in .stringsdict)</note>
</trans-unit> </trans-unit>
<trans-unit id="tips_count" translate="no" xml:space="preserve">
<source>tips_count</source>
<target>tips_count</target>
<note>Part of a larger sentence to describe how many tip payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="u{00A0}" xml:space="preserve"> <trans-unit id="u{00A0}" xml:space="preserve">
<source>u{00A0}</source> <source>u{00A0}</source>
<target>u{00A0}</target> <target>u{00A0}</target>
@@ -1405,7 +1042,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="wss://some.relay.com" xml:space="preserve"> <trans-unit id="wss://some.relay.com" xml:space="preserve">
<source>wss://some.relay.com</source> <source>wss://some.relay.com</source>
<target>wss://örnek.role.com</target> <target>wss://some.relay.com</target>
<note>Placeholder example for relay server address.</note> <note>Placeholder example for relay server address.</note>
</trans-unit> </trans-unit>
@@ -1415,18 +1052,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note> <note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
</trans-unit> </trans-unit>
<trans-unit id="zaps_count" translate="no" xml:space="preserve">
<source>zaps_count</source>
<target>zaps_count</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post. (Key in .stringsdict)</note>
</trans-unit>
<trans-unit id="⚡️ %@" xml:space="preserve">
<source>⚡️ %@</source>
<target>⚡️ %@</target>
<note>Text indicating the zap amount. i.e. number of satoshis that were tipped to a user</note>
</trans-unit>
</body> </body>
</file> </file>
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="tr-TR" datatype="plaintext"> <file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="tr-TR" datatype="plaintext">
@@ -1436,7 +1061,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<body> <body>
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/one:dict/:string" xml:space="preserve">
<source>%d other note</source> <source>%d other note</source>
<target>%d diğer not</target> <target>1%d diğer not</target>
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note> <note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
</trans-unit> </trans-unit>
@@ -1508,7 +1133,7 @@ Part of a larger sentence to describe how many profiles a user is following.</no
</trans-unit> </trans-unit>
<trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>Replying to %@%#@OTHERS@</source> <source>Replying to %@%#@OTHERS@</source>
<target>%@%#@OTHERS@'lara yanıt</target> <target>%@%#@OTHERS@'lara yanıt </target>
<note>Label to indicate that the user is replying to 1 user and others.</note> <note>Label to indicate that the user is replying to 1 user and others.</note>
</trans-unit> </trans-unit>
@@ -1586,21 +1211,23 @@ Part of a larger sentence to describe how many profiles a user is following.</no
<note>Amount of sats.</note> <note>Amount of sats.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@ZAPS@</source> <source>%#@TIPS@</source>
<target>%#@ZAPS@</target> <target>%#@TIPS@</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/one:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/one:dict/:string" xml:space="preserve">
<source>Zap</source> <source>Tip</source>
<target>Bahşiş</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
<trans-unit id="/zaps_count:dict/ZAPS:dict/other:dict/:string" xml:space="preserve"> <trans-unit id="/tips_count:dict/TIPS:dict/other:dict/:string" xml:space="preserve">
<source>Zaps</source> <source>Tips</source>
<target>Bahşişler</target>
<note>Part of a larger sentence to describe how many zap payments there are on a post.</note> <note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
</trans-unit> </trans-unit>
</body> </body>
</file> </file>
+22 -155
View File
@@ -15,13 +15,9 @@
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 */; };
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 */; };
@@ -31,6 +27,7 @@
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 */; }; 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 */; };
@@ -72,8 +69,6 @@
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 */; };
@@ -90,7 +85,6 @@
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 */; };
@@ -122,8 +116,6 @@
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 */; };
@@ -136,16 +128,7 @@
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 */; };
@@ -190,7 +173,6 @@
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; }; 4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */; };
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; }; 4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CF0ABF52985CD5500D66079 /* UserSearch.swift */; };
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; }; 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FCB2984ACA60072348F /* QRCodeView.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 */; };
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; }; 647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
@@ -205,8 +187,6 @@
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 */; };
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 */
@@ -236,48 +216,29 @@
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>"; };
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>"; };
3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 3A5EA10F297CCF6C00569477 /* de-AT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-AT"; path = "de-AT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
3A5CAE1E298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Localizable.strings"; sourceTree = "<group>"; }; 3A5EA110297CCF6C00569477 /* de-AT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-AT"; path = "de-AT.lproj/Localizable.strings"; sourceTree = "<group>"; };
3A5CAE1F298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-CN"; path = "zh-CN.lproj/Localizable.stringsdict"; sourceTree = "<group>"; }; 3A5EA111297CCF6C00569477 /* de-AT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "de-AT"; path = "de-AT.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>"; };
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>"; };
3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibreTranslateServer.swift; sourceTree = "<group>"; }; 3AEABD20297CCFA8003F2975 /* de-DE */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de-DE"; path = "de-DE.lproj/InfoPlist.strings"; 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>"; };
@@ -289,6 +250,7 @@
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>"; }; 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>"; };
@@ -330,8 +292,6 @@
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>"; };
@@ -377,7 +337,6 @@
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>"; };
@@ -410,8 +369,6 @@
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>"; };
@@ -424,16 +381,7 @@
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>"; };
@@ -481,7 +429,6 @@
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; }; 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bech32Object.swift; sourceTree = "<group>"; };
4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; }; 4CF0ABF52985CD5500D66079 /* UserSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSearch.swift; sourceTree = "<group>"; };
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>"; };
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>"; }; 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>"; };
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>"; };
@@ -495,8 +442,6 @@
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>"; };
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 */
@@ -637,9 +582,6 @@
7C45AE70297353390031D7BC /* KFImageModel.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 */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -647,8 +589,6 @@
4C75EFA227FA576C0006080F /* Views */ = { 4C75EFA227FA576C0006080F /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4CB9D4A52992D01900A9A7E4 /* Profile */,
4CAAD8AE29888A9B00060CEA /* Relays */,
4CF0ABF42985CD4200D66079 /* Posting */, 4CF0ABF42985CD4200D66079 /* Posting */,
4CF0ABDF2981A83000D66079 /* Muting */, 4CF0ABDF2981A83000D66079 /* Muting */,
4CC7AAEE297F11B300430951 /* Events */, 4CC7AAEE297F11B300430951 /* Events */,
@@ -683,6 +623,9 @@
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 */,
@@ -731,9 +674,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
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 */,
@@ -750,26 +691,10 @@
4CF0ABD72981980C00D66079 /* Lists.swift */, 4CF0ABD72981980C00D66079 /* Lists.swift */,
4CF0ABEF29857E9200D66079 /* Bech32Object.swift */, 4CF0ABEF29857E9200D66079 /* Bech32Object.swift */,
7C60CAEE298471A1009C80D6 /* CoreSVG.swift */, 7C60CAEE298471A1009C80D6 /* CoreSVG.swift */,
4CAAD8AC298851D000060CEA /* AccountDeletion.swift */,
4CB883A52975F83C00DC99E7 /* LNUrlPayRequest.swift */,
4CB883A72975FC1800DC99E7 /* Zaps.swift */,
4CB883B5297730E400DC99E7 /* LNUrls.swift */,
3AB72AB8298ECF30004BB58C /* Translator.swift */,
); );
path = Util; path = Util;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
4CAAD8AE29888A9B00060CEA /* Relays */ = {
isa = PBXGroup;
children = (
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
4C06670028FC7C5900038D2A /* RelayView.swift */,
4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */,
F7908E91298B0F0700AB113A /* RelayDetailView.swift */,
);
path = Relays;
sourceTree = "<group>";
};
4CB88387296AF97C00DC99E7 /* ActionBar */ = { 4CB88387296AF97C00DC99E7 /* ActionBar */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -787,15 +712,6 @@
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 = (
@@ -807,8 +723,6 @@
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>";
@@ -826,11 +740,8 @@
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 */,
); );
path = Components; path = Components;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -899,8 +810,6 @@
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */, 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */,
4CB88399297322D200DC99E7 /* DMTests.swift */, 4CB88399297322D200DC99E7 /* DMTests.swift */,
4CF0ABDB2981A19E00D66079 /* ListTests.swift */, 4CF0ABDB2981A19E00D66079 /* ListTests.swift */,
4CB883A9297612FF00DC99E7 /* ZapTests.swift */,
4CB883AD2976FA9300DC99E7 /* FormatTests.swift */,
); );
path = damusTests; path = damusTests;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1050,17 +959,12 @@
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",
); );
mainGroup = 4CE6DEDA27F7A08100C66700; mainGroup = 4CE6DEDA27F7A08100C66700;
packageReferences = ( packageReferences = (
@@ -1141,25 +1045,20 @@
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 */,
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 */,
4CF0ABF029857E9200D66079 /* Bech32Object.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 */,
@@ -1168,7 +1067,6 @@
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 */,
@@ -1179,12 +1077,10 @@
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 */,
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */, 4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */,
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */, 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
@@ -1194,7 +1090,6 @@
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 */,
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */, BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
@@ -1214,21 +1109,18 @@
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 */,
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 */, 5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */,
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */, 4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
@@ -1258,9 +1150,8 @@
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 */,
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */, 4C0A3F95280F6C78000448DE /* ReplyQuoteView.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 */,
@@ -1273,17 +1164,13 @@
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 */,
4CF0ABD42980996B00D66079 /* Report.swift in Sources */, 4CF0ABD42980996B00D66079 /* Report.swift in Sources */,
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */, 4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */,
4C06670B28FDE64700038D2A /* damus.c in Sources */, 4C06670B28FDE64700038D2A /* damus.c 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 */,
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 */,
@@ -1293,7 +1180,6 @@
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */, 4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */,
4C3AC7A52836987600E1F516 /* MainTabView.swift in Sources */, 4C3AC7A52836987600E1F516 /* MainTabView.swift in Sources */,
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */, 3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */,
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1305,9 +1191,7 @@
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */, DD597CBD2963D85A00C64D32 /* MarkdownTests.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 */,
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */, 4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */,
@@ -1345,17 +1229,12 @@
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 */,
); );
name = Localizable.stringsdict; name = Localizable.stringsdict;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1364,17 +1243,12 @@
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 */,
); );
name = InfoPlist.strings; name = InfoPlist.strings;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1383,17 +1257,12 @@
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 */,
); );
name = Localizable.strings; name = Localizable.strings;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1529,7 +1398,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 = 14; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D; DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1537,7 +1406,6 @@
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;
@@ -1571,7 +1439,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 = 14; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D; DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1579,7 +1447,6 @@
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;
-60
View File
@@ -1,60 +0,0 @@
//
// 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")
}
}
+59 -81
View File
@@ -7,84 +7,6 @@
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)
@@ -106,12 +28,68 @@ 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: .description("this is a description"), amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119) let test_invoice = Invoice(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(our_pubkey: "", invoice: test_invoice) InvoiceView(invoice: test_invoice)
.frame(width: 300, height: 200) .frame(width: 200, height: 200)
} }
} }
+2 -3
View File
@@ -8,7 +8,6 @@
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
@@ -17,7 +16,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(our_pubkey: our_pubkey, invoice: invoice) InvoiceView(invoice: invoice)
.tabItem { .tabItem {
Text(invoice.string) Text(invoice.string)
} }
@@ -31,7 +30,7 @@ struct InvoicesView: View {
struct InvoicesView_Previews: PreviewProvider { struct InvoicesView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
InvoicesView(our_pubkey: "", invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)]) InvoicesView(invoices: [Invoice.init(description: "description", amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)])
.frame(width: 300) .frame(width: 300)
} }
} }
-135
View File
@@ -1,135 +0,0 @@
//
// 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
let size: EventViewKind
@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()
Text(artifacts.content)
.font(eventviewsize_to_font(size))
.fixedSize(horizontal: false, vertical: true)
}
}
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.
let content = event.get_content(damus_state.keypair.privkey)
noteLanguage = NLLanguageRecognizer.dominantLanguage(for: content)?.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)
translated_note = try await translator.translate(content, from: note_lang, to: currentLanguage)
} 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 blocks = event.get_blocks(content: translated)
translated_artifacts = render_blocks(blocks: blocks, 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, size: .selected)
}
}
-131
View File
@@ -1,131 +0,0 @@
//
// 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 tip_amount = get_default_tip_amount(pubkey: damus_state.pubkey)
guard let inv = await fetch_zap_invoice(payreq, zapreq: zapreq, amount: tip_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 {
ZStack {
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"))
Text("\(bar.zap_total > 0 ? "\(format_msats_abbrev(bar.zap_total))" : "")")
.offset(x: 22)
.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)
}
}
+22 -34
View File
@@ -12,10 +12,10 @@ 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://nostr.bitcoiner.social",
"wss://nos.lol",
"wss://relay.current.fyi",
"wss://brb.io", "wss://brb.io",
] ]
@@ -73,7 +73,6 @@ 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
@@ -89,6 +88,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 +111,7 @@ struct ContentView: View {
.tabViewStyle(.page(indexDisplayMode: .never)) .tabViewStyle(.page(indexDisplayMode: .never))
if privkey != nil { if privkey != nil {
PostButtonContainer(is_left_handed: damus_state?.settings.left_handed ?? false) { PostButtonContainer(userSettings: user_settings) {
self.active_sheet = .post self.active_sheet = .post
} }
} }
@@ -119,10 +119,9 @@ struct ContentView: View {
} }
.safeAreaInset(edge: .top, spacing: 0) { .safeAreaInset(edge: .top, spacing: 0) {
VStack(spacing: 0) { VStack(spacing: 0) {
CustomPicker(selection: $filter_state, content: { FiltersView
Text("Posts", comment: "Label for filter for seeing only posts (instead of posts and replies).").tag(FilterState.posts) //.frame(maxWidth: 275)
Text("Posts & Replies", comment: "Label for filter for seeing posts and replies (instead of only posts).").tag(FilterState.posts_and_replies) .padding()
})
Divider() Divider()
.frame(height: 1) .frame(height: 1)
} }
@@ -138,10 +137,14 @@ struct ContentView: View {
} }
} }
func popToRoot() { var FiltersView: some View {
profile_open = false VStack{
thread_open = false Picker(NSLocalizedString("Filter State", comment: "Filter state for seeing either only posts, or posts & replies."), selection: $filter_state) {
search_open = false Text("Posts", comment: "Label for filter for seeing only posts (instead of posts and replies).").tag(FilterState.posts)
Text("Posts & Replies", comment: "Label for filter for seeing posts and replies (instead of only posts).").tag(FilterState.posts_and_replies)
}
.pickerStyle(.segmented)
}
} }
func MainContent(damus: DamusState) -> some View { func MainContent(damus: DamusState) -> some View {
@@ -203,7 +206,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(contacts: damus_state!.contacts, pool: damus_state!.pool, search: search)) SearchView(appstate: damus_state!, search: SearchModel(pool: damus_state!.pool, search: search))
} else { } else {
EmptyView() EmptyView()
} }
@@ -265,11 +268,9 @@ struct ContentView: View {
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 {
NavigationLink(destination: RelayConfigView(state: damus_state!)) { Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.") .font(.callout)
.font(.callout) .foregroundColor(.gray)
.foregroundColor(.gray)
}
} }
} }
@@ -347,9 +348,6 @@ 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)
@@ -436,13 +434,7 @@ 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("Deleted Account", comment: "Alert message to indicate this is a deleted account"), isPresented: $is_deleted_account) { .alert(NSLocalizedString("User blocked", comment: "Alert message to indicate "), isPresented: $user_blocked_confirm, actions: {
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
} }
@@ -526,7 +518,6 @@ 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 {
@@ -566,10 +557,7 @@ struct ContentView: View {
tips: TipCounter(our_pubkey: pubkey), tips: TipCounter(our_pubkey: pubkey),
profiles: Profiles(), profiles: Profiles(),
dms: home.dms, dms: home.dms,
previews: PreviewCache(), previews: PreviewCache()
zaps: Zaps(our_pubkey: pubkey),
lnurls: LNUrls(),
settings: UserSettingsStore()
) )
home.damus_state = self.damus_state! home.damus_state = self.damus_state!
+1
View File
@@ -24,6 +24,7 @@
<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>
+9 -11
View File
@@ -11,32 +11,30 @@ 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_zap: Zap? @Published var our_tip: NostrEvent?
@Published var likes: Int @Published var likes: Int
@Published var boosts: Int @Published var boosts: Int
@Published var zaps: Int @Published var tips: Int64
@Published var zap_total: Int64
static func empty() -> ActionBarModel { static func empty() -> ActionBarModel {
return ActionBarModel(likes: 0, boosts: 0, zaps: 0, zap_total: 0, our_like: nil, our_boost: nil, our_zap: nil) return ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
} }
init(likes: Int, boosts: Int, zaps: Int, zap_total: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_zap: Zap?) { init(likes: Int, boosts: Int, tips: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_tip: NostrEvent?) {
self.likes = likes self.likes = likes
self.boosts = boosts self.boosts = boosts
self.zaps = zaps self.tips = tips
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_zap = our_zap self.our_tip = our_tip
} }
var is_empty: Bool { var is_empty: Bool {
return likes == 0 && boosts == 0 && zaps == 0 return likes == 0 && boosts == 0 && tips == 0
} }
var zapped: Bool { var tipped: Bool {
return our_zap != nil return our_tip != nil
} }
var liked: Bool { var liked: Bool {
+2 -5
View File
@@ -18,20 +18,17 @@ 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
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(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore()) 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())
} }
} }
-35
View File
@@ -1,35 +0,0 @@
//
// 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 }
}
}
+1 -1
View File
@@ -86,7 +86,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(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev) process_metadata_event(profiles: damus_state.profiles, ev: ev)
} }
case .notice(let msg): case .notice(let msg):
+1 -1
View File
@@ -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(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev) process_metadata_event(profiles: damus_state.profiles, ev: ev)
} }
case .notice(let msg): case .notice(let msg):
print("followingmodel notice: \(msg)") print("followingmodel notice: \(msg)")
+9 -75
View File
@@ -112,61 +112,9 @@ 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, zapper: String) {
guard let zap = Zap.from_zap_event(zap_ev: ev, zapper: zapper) else {
return
}
damus_state.zaps.add_zap(zap: zap)
if !insert_uniq_sorted_event(events: &notifications, new_ev: ev, cmp: { $0.created_at > $1.created_at }) {
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
}
guard ptag == damus_state.pubkey else {
return
}
if let local_zapper = damus_state.profiles.lookup_zapper(pubkey: damus_state.pubkey) {
handle_zap_event_with_zapper(ev, zapper: local_zapper)
return
}
guard let profile = damus_state.profiles.lookup(id: damus_state.pubkey) 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.handle_zap_event_with_zapper(ev, 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
@@ -286,7 +234,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 == .zap || 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 == .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
@@ -369,7 +317,6 @@ 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
@@ -425,7 +372,7 @@ class HomeModel: ObservableObject {
} }
func handle_metadata_event(_ ev: NostrEvent) { func handle_metadata_event(_ ev: NostrEvent) {
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev) process_metadata_event(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? {
@@ -464,7 +411,7 @@ class HomeModel: ObservableObject {
} }
func handle_text_event(sub_id: String, _ ev: NostrEvent) { func handle_text_event(sub_id: String, _ ev: NostrEvent) {
guard should_show_event(contacts: damus_state.contacts, ev: ev) else { if should_hide_event(contacts: damus_state.contacts, ev: ev) {
return return
} }
@@ -476,7 +423,7 @@ class HomeModel: ObservableObject {
} }
func handle_dm(_ ev: NostrEvent) { func handle_dm(_ ev: NostrEvent) {
if let notifs = handle_incoming_dm(contacts: damus_state.contacts, prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, ev: ev) { if let notifs = handle_incoming_dm(prev_events: self.new_events, dms: self.dms, our_pubkey: self.damus_state.pubkey, ev: ev) {
self.new_events = notifs self.new_events = notifs
} }
} }
@@ -583,17 +530,10 @@ func print_filters(relay_id: String?, filters groups: [[NostrFilter]]) {
print("-----") print("-----")
} }
func process_metadata_event(our_pubkey: String, profiles: Profiles, ev: NostrEvent) { func process_metadata_event(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) {
@@ -707,12 +647,7 @@ func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_
} }
} }
func handle_incoming_dm(contacts: Contacts, prev_events: NewEventsBits, dms: DirectMessagesModel, our_pubkey: String, ev: NostrEvent) -> NewEventsBits? { func handle_incoming_dm(prev_events: NewEventsBits, dms: DirectMessagesModel, our_pubkey: String, ev: NostrEvent) -> NewEventsBits? {
// hide blocked users
guard should_show_event(contacts: contacts, ev: ev) else {
return prev_events
}
var inserted = false var inserted = false
var found = false var found = false
let ours = ev.pubkey == our_pubkey let ours = ev.pubkey == our_pubkey
@@ -786,10 +721,9 @@ func event_has_our_pubkey(_ ev: NostrEvent, our_pubkey: String) -> Bool {
} }
func should_show_event(contacts: Contacts, ev: NostrEvent) -> Bool { func should_hide_event(contacts: Contacts, ev: NostrEvent) -> Bool {
if contacts.is_muted(ev.pubkey) { if contacts.is_muted(ev.pubkey) {
return false return true
} }
return ev.should_show_event return !ev.should_show_event
} }
-41
View File
@@ -1,41 +0,0 @@
//
// 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 }
}
}
+5 -4
View File
@@ -7,10 +7,6 @@
import Foundation import Foundation
enum CountResult {
case already_counted
case success(Int)
}
class EventCounter { class EventCounter {
var counts: [String: Int] = [:] var counts: [String: Int] = [:]
@@ -18,6 +14,11 @@ 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
} }
+16 -74
View File
@@ -32,30 +32,13 @@ struct IdBlock: Identifiable {
let block: Block let block: Block
} }
typealias Invoice = LightningInvoice<Amount> struct Invoice {
typealias ZapInvoice = LightningInvoice<Int64> let description: String
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 {
@@ -206,50 +189,20 @@ 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):
return format_msats(amt) let numberFormatter = NumberFormatter()
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_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) -> String {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.minimumFractionDigits = 0
numberFormatter.maximumFractionDigits = 3
numberFormatter.roundingMode = .down
let sats = NSNumber(value: (Double(msat) / 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 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
@@ -259,8 +212,9 @@ func convert_invoice_block(_ b: invoice_block) -> Block? {
return nil return nil
} }
guard let description = convert_invoice_description(b11: b11) else { var description = ""
return nil if b11.description != 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
@@ -271,18 +225,6 @@ 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)
+1 -19
View File
@@ -20,24 +20,6 @@ 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)
@@ -115,7 +97,7 @@ class ProfileModel: ObservableObject, Equatable {
} 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(our_pubkey: damus.pubkey, profiles: damus.profiles, ev: ev) process_metadata_event(profiles: damus.profiles, ev: ev)
} }
seen_event.insert(ev.id) seen_event.insert(ev.id)
} }
+3 -3
View File
@@ -31,7 +31,7 @@ class SearchHomeModel: ObservableObject {
} }
func filter_muted() { func filter_muted() {
events = events.filter { should_show_event(contacts: damus_state.contacts, ev: $0) } events = events.filter { !should_hide_event(contacts: damus_state.contacts, ev: $0) }
} }
func subscribe() { func subscribe() {
@@ -54,7 +54,7 @@ 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_show_event(contacts: damus_state.contacts, ev: ev) && !ev.is_reply(nil) { if ev.is_textlike && !should_hide_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
} }
@@ -129,7 +129,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(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev) process_metadata_event(profiles: damus_state.profiles, ev: ev)
} }
} }
+1 -11
View File
@@ -15,20 +15,14 @@ class SearchModel: ObservableObject {
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(contacts: Contacts, pool: RelayPool, search: NostrFilter) { init(pool: RelayPool, search: NostrFilter) {
self.contacts = contacts
self.pool = pool self.pool = pool
self.search = search self.search = search
} }
func filter_muted() {
self.events = self.events.filter { should_show_event(contacts: contacts, ev: $0) }
}
func subscribe() { func subscribe() {
// since 1 month // since 1 month
search.limit = self.limit search.limit = self.limit
@@ -53,10 +47,6 @@ class SearchModel: ObservableObject {
return return
} }
guard should_show_event(contacts: contacts, ev: ev) else {
return
}
if insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at } ) { if insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at } ) {
objectWillChange.send() objectWillChange.send()
} }
+1 -1
View File
@@ -190,7 +190,7 @@ class ThreadModel: ObservableObject {
} }
if ev.known_kind == .metadata { if ev.known_kind == .metadata {
process_metadata_event(our_pubkey: damus_state.pubkey, profiles: damus_state.profiles, ev: ev) process_metadata_event(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 {
-37
View File
@@ -1,37 +0,0 @@
//
// 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 }
}
}
+8 -209
View File
@@ -6,69 +6,6 @@
// //
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)"
}
let tip_amount_key = "default_tip_amount"
func set_default_tip_amount(pubkey: String, amount: Int64) {
let key = pk_setting_key(pubkey, key: tip_amount_key)
UserDefaults.standard.setValue(amount, forKey: key)
}
func get_default_tip_amount(pubkey: String) -> Int64 {
let key = "\(pubkey)_\(tip_amount_key)"
return UserDefaults.standard.object(forKey: key) as? Int64 ?? 1000000
}
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 {
@@ -89,154 +26,16 @@ class UserSettingsStore: ObservableObject {
} }
} }
@Published var translation_service: TranslationService {
didSet {
UserDefaults.standard.set(translation_service.rawValue, forKey: "translation_service")
}
}
@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() { init() {
// TODO: pubkey-scoped settings if let defaultWalletName = UserDefaults.standard.string(forKey: "default_wallet"),
let pubkey = "" let default_wallet = Wallet(rawValue: defaultWalletName)
self.default_wallet = get_default_wallet(pubkey) {
show_wallet_selector = should_show_wallet_selector(pubkey) self.default_wallet = default_wallet
} else {
default_wallet = .system_default_wallet
}
show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
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"
}
+2 -2
View File
@@ -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: "https://cash.app/launch/lightning/", return .init(index: 1, tag: "cashapp", displayName: NSLocalizedString("Cash App", comment: "Dropdown option label for Lightning wallet, Cash App."), link: "squarecash://",
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:",
+3 -24
View File
@@ -24,22 +24,18 @@ struct Profile: Codable {
} }
private func str(_ str: String) -> String? { private func str(_ str: String) -> String? {
return get_val(str) guard let val = self.value[str] else{
}
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? T else { guard let s = val.value as? String else {
return nil return nil
} }
return s return s
} }
private mutating func set_val<T>(_ key: String, _ val: T?) { private mutating func set_str(_ key: String, _ val: String?) {
if val == nil { if val == nil {
self.value.removeValue(forKey: key) self.value.removeValue(forKey: key)
return return
@@ -48,15 +44,6 @@ 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) }
@@ -110,10 +97,6 @@ struct Profile: Codable {
return lnaddress_to_lnurl(addr); return lnaddress_to_lnurl(addr);
} }
if !addr.lowercased().hasPrefix("lnurl") {
return nil
}
return addr; return addr;
} }
@@ -126,10 +109,6 @@ 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)
+2 -33
View File
@@ -11,8 +11,6 @@ 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
@@ -81,7 +79,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
} }
var too_big: Bool { var too_big: Bool {
return self.content.count > 16000 return self.content.count > 32000
} }
var should_show_event: Bool { var should_show_event: Bool {
@@ -105,15 +103,11 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
if let bs = _blocks { if let bs = _blocks {
return bs return bs
} }
let blocks = get_blocks(content: self.get_content(privkey)) let blocks = parse_mentions(content: self.get_content(privkey), tags: self.tags)
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 {
@@ -370,14 +364,9 @@ 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))
} }
@@ -578,26 +567,6 @@ 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] } ?? []
-1
View File
@@ -20,5 +20,4 @@ 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
} }
-9
View File
@@ -12,20 +12,11 @@ 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
} }
+4 -14
View File
@@ -7,16 +7,16 @@
import Foundation import Foundation
public struct RelayInfo: Codable { 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)
} }
public struct RelayDescriptor: Codable { struct RelayDescriptor: Codable {
public let url: URL let url: URL
public let info: RelayInfo let info: RelayInfo
} }
enum RelayFlags: Int { enum RelayFlags: Int {
@@ -24,16 +24,6 @@ enum RelayFlags: Int {
case broken = 1 case broken = 1
} }
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?
}
class Relay: Identifiable { class Relay: Identifiable {
let descriptor: RelayDescriptor let descriptor: RelayDescriptor
let connection: RelayConnection let connection: RelayConnection
+4 -3
View File
@@ -118,9 +118,10 @@ func make_nostr_req(_ req: NostrRequest) -> String? {
} }
func make_nostr_push_event(ev: NostrEvent) -> String? { func make_nostr_push_event(ev: NostrEvent) -> String? {
guard let event = encode_json(ev) else { let encoder = JSONEncoder()
return nil encoder.outputFormatting = .withoutEscapingSlashes
} 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
-22
View File
@@ -1,22 +0,0 @@
//
// AccountDeletion.swift
// damus
//
// Created by William Casarin on 2023-01-30.
//
import Foundation
func created_deleted_account_profile(keypair: FullKeypair) -> NostrEvent {
var profile = Profile()
profile.deleted = true
profile.about = "account deleted"
profile.name = "nobody"
let content = encode_json(profile)!
let ev = NostrEvent(content: content, pubkey: keypair.pubkey, kind: 0)
ev.id = calculate_event_id(ev: ev)
ev.sig = sign_event(privkey: keypair.privkey, ev: ev)
return ev
}
+1 -1
View File
@@ -147,7 +147,7 @@ public func bech32_decode(_ str: String) throws -> (hrp: String, data: Data)? {
guard let strBytes = str.data(using: .utf8) else { guard let strBytes = str.data(using: .utf8) else {
throw Bech32Error.nonUTF8String throw Bech32Error.nonUTF8String
} }
guard strBytes.count <= 2024 else { guard strBytes.count <= 90 else {
throw Bech32Error.stringLengthExceeded throw Bech32Error.stringLengthExceeded
} }
var lower: Bool = false var lower: Bool = false
-20
View File
@@ -38,26 +38,6 @@ func insert_uniq_by_pubkey(events: inout [NostrEvent], new_ev: NostrEvent, cmp:
return true return true
} }
func insert_uniq_sorted_zap(zaps: inout [Zap], new_zap: Zap) -> Bool {
var i: Int = 0
for zap in zaps {
// don't insert duplicate events
if new_zap.event.id == zap.event.id {
return false
}
if new_zap.invoice.amount > zap.invoice.amount {
zaps.insert(new_zap, at: i)
return true
}
i += 1
}
zaps.append(new_zap)
return true
}
func insert_uniq_sorted_event(events: inout [NostrEvent], new_ev: NostrEvent, cmp: (NostrEvent, NostrEvent) -> Bool) -> Bool { func insert_uniq_sorted_event(events: inout [NostrEvent], new_ev: NostrEvent, cmp: (NostrEvent, NostrEvent) -> Bool) -> Bool {
var i: Int = 0 var i: Int = 0
-14
View File
@@ -158,20 +158,6 @@ func get_saved_privkey() -> String? {
return mkey.map { $0.trimmingCharacters(in: .whitespaces) } return mkey.map { $0.trimmingCharacters(in: .whitespaces) }
} }
/**
Detects whether a string might contain an nsec1 prefixed private key.
It does not determine if it's the current user's private key and does not verify if it is properly encoded or has the right length.
*/
func contentContainsPrivateKey(_ content: String) -> Bool {
if #available(iOS 16.0, *) {
return content.contains(/nsec1[02-9ac-z]+/)
} else {
let regex = try! NSRegularExpression(pattern: "nsec1[02-9ac-z]+")
return (regex.firstMatch(in: content, range: NSRange(location: 0, length: content.count)) != nil)
}
}
fileprivate func removePrivateKeyFromUserDefaults() throws { fileprivate func removePrivateKeyFromUserDefaults() throws {
guard let privKey = UserDefaults.standard.string(forKey: "privkey") else { return } guard let privKey = UserDefaults.standard.string(forKey: "privkey") else { return }
try save_privkey(privkey: privKey) try save_privkey(privkey: privKey)
-24
View File
@@ -1,24 +0,0 @@
//
// LNUrl.swift
// damus
//
// Created by William Casarin on 2023-01-16.
//
import Foundation
struct LNUrlPayRequest: Decodable {
let allowsNostr: Bool?
let nostrPubkey: String?
let minSendable: Int64?
let maxSendable: Int64?
let status: String?
let callback: String?
}
struct LNUrlPayResponse: Decodable {
let pr: String
}
-20
View File
@@ -1,20 +0,0 @@
//
// LNUrls.swift
// damus
//
// Created by William Casarin on 2023-01-17.
//
import Foundation
class LNUrls {
var endpoints: [String: LNUrlPayRequest]
init() {
self.endpoints = [:]
}
func lookup(_ id: String) -> LNUrlPayRequest? {
return self.endpoints[id]
}
}
-26
View File
@@ -1,26 +0,0 @@
//
// NIPURLBuilder.swift
// damus
//
// Created by Honk on 2/1/23.
//
import Foundation
struct NIPURLBuilder {
static private let baseURL = "https://github.com/nostr-protocol/nips/blob/master/"
static func url(forNIP nip: Int) -> URL? {
let urlString = baseURL + "\(formatNipNumber(nip: nip)).md"
return URL(string: urlString)
}
static func formatNipNumber(nip: Int) -> String {
let formatted: String
if nip < 10 {
formatted = "0\(nip)"
} else {
formatted = "\(nip)"
}
return formatted
}
}
-3
View File
@@ -95,9 +95,6 @@ extension Notification.Name {
static var new_unmutes: Notification.Name { static var new_unmutes: Notification.Name {
return Notification.Name("new_unmutes") return Notification.Name("new_unmutes")
} }
static var deleted_account: Notification.Name {
return Notification.Name("deleted_account")
}
} }
func handle_notify(_ name: Notification.Name) -> NotificationCenter.Publisher { func handle_notify(_ name: Notification.Name) -> NotificationCenter.Publisher {
-127
View File
@@ -1,127 +0,0 @@
//
// Translator.swift
// damus
//
// Created by Terry Yiu on 2/4/23.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct Translator {
private let userSettingsStore: UserSettingsStore
private let session = URLSession.shared
private let encoder = JSONEncoder()
private let decoder = JSONDecoder()
init(_ userSettingsStore: UserSettingsStore) {
self.userSettingsStore = userSettingsStore
}
public func translate(_ text: String, from sourceLanguage: String, to targetLanguage: String) async throws -> String? {
switch userSettingsStore.translation_service {
case .libretranslate:
return try await translateWithLibreTranslate(text, from: sourceLanguage, to: targetLanguage)
case .deepl:
return try await translateWithDeepL(text, from: sourceLanguage, to: targetLanguage)
case .none:
return text
}
}
private func translateWithLibreTranslate(_ text: String, from sourceLanguage: String, to targetLanguage: String) async throws -> String? {
let url = try makeURL(userSettingsStore.libretranslate_url, path: "/translate")
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
struct RequestBody: Encodable {
let q: String
let source: String
let target: String
let api_key: String?
}
let body = RequestBody(q: text, source: sourceLanguage, target: targetLanguage, api_key: userSettingsStore.libretranslate_api_key)
request.httpBody = try encoder.encode(body)
struct Response: Decodable {
let translatedText: String
}
let response: Response = try await decodedData(for: request)
return response.translatedText
}
private func translateWithDeepL(_ text: String, from sourceLanguage: String, to targetLanguage: String) async throws -> String? {
if userSettingsStore.deepl_api_key == "" {
return nil
}
let url = try makeURL(userSettingsStore.deepl_plan.model.url, path: "/v2/translate")
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("DeepL-Auth-Key \(userSettingsStore.deepl_api_key)", forHTTPHeaderField: "Authorization")
struct RequestBody: Encodable {
let text: [String]
let source_lang: String
let target_lang: String
}
let body = RequestBody(text: [text], source_lang: sourceLanguage.uppercased(), target_lang: targetLanguage.uppercased())
request.httpBody = try encoder.encode(body)
struct Response: Decodable {
let translations: [DeepLTranslations]
}
struct DeepLTranslations: Decodable {
let detected_source_language: String
let text: String
}
let response: Response = try await decodedData(for: request)
return response.translations.map { $0.text }.joined(separator: " ")
}
private func makeURL(_ baseUrl: String, path: String) throws -> URL {
guard var components = URLComponents(string: baseUrl) else {
throw URLError(.badURL)
}
components.path = path
guard let url = components.url else {
throw URLError(.badURL)
}
return url
}
private func decodedData<Output: Decodable>(for request: URLRequest) async throws -> Output {
let data = try await session.data(for: request)
let result = try decoder.decode(Output.self, from: data)
return result
}
}
private extension URLSession {
func data(for request: URLRequest) async throws -> Data {
var task: URLSessionDataTask?
let onCancel = { task?.cancel() }
return try await withTaskCancellationHandler(
operation: {
try await withCheckedThrowingContinuation { continuation in
task = dataTask(with: request) { data, _, error in
guard let data = data else {
let error = error ?? URLError(.badServerResponse)
return continuation.resume(throwing: error)
}
continuation.resume(returning: data)
}
task?.resume()
}
},
onCancel: { onCancel() }
)
}
}
-323
View File
@@ -1,323 +0,0 @@
//
// Zap.swift
// damus
//
// Created by William Casarin on 2023-01-15.
//
import Foundation
enum ZapSource {
case author(String)
// TODO: anonymous
//case anonymous
}
public struct NoteZapTarget: Equatable {
public let note_id: String
public let author: String
}
public enum ZapTarget: Equatable {
case profile(String)
case note(NoteZapTarget)
public static func note(id: String, author: String) -> ZapTarget {
return .note(NoteZapTarget(note_id: id, author: author))
}
var pubkey: String {
switch self {
case .profile(let pk):
return pk
case .note(let note_target):
return note_target.author
}
}
var id: String {
switch self {
case .note(let note_target):
return note_target.note_id
case .profile(let pk):
return pk
}
}
}
struct ZapRequest {
let ev: NostrEvent
}
struct Zap {
public let event: NostrEvent
public let invoice: ZapInvoice
public let zapper: String /// zap authorizer
public let target: ZapTarget
public let request: ZapRequest
public static func from_zap_event(zap_ev: NostrEvent, zapper: String) -> Zap? {
/// Make sure that we only create a zap event if it is authorized by the profile or event
guard zapper == zap_ev.pubkey else {
return nil
}
guard let bolt11_str = event_tag(zap_ev, name: "bolt11") else {
return nil
}
guard let bolt11 = decode_bolt11(bolt11_str) else {
return nil
}
/// Any amount invoices are not allowed
guard let zap_invoice = invoice_to_zap_invoice(bolt11) else {
return nil
}
// Some endpoints don't have this, let's skip the check for now. We're mostly trusting the zapper anyways
/*
guard let preimage = event_tag(zap_ev, name: "preimage") else {
return nil
}
guard preimage_matches_invoice(preimage, inv: zap_invoice) else {
return nil
}
*/
guard let desc = get_zap_description(zap_ev, inv_desc: zap_invoice.description) else {
return nil
}
guard let zap_req = decode_nostr_event_json(desc) else {
return nil
}
guard let target = determine_zap_target(zap_req) else {
return nil
}
return Zap(event: zap_ev, invoice: zap_invoice, zapper: zapper, target: target, request: ZapRequest(ev: zap_req))
}
}
/// Fetches the description from either the invoice, or tags, depending on the type of invoice
func get_zap_description(_ ev: NostrEvent, inv_desc: InvoiceDescription) -> String? {
switch inv_desc {
case .description(let string):
return string
case .description_hash(let deschash):
guard let desc = event_tag(ev, name: "description") else {
return nil
}
guard let data = desc.data(using: .utf8) else {
return nil
}
guard sha256(data) == deschash else {
return nil
}
return desc
}
}
func invoice_to_zap_invoice(_ invoice: Invoice) -> ZapInvoice? {
guard case .specific(let amt) = invoice.amount else {
return nil
}
return ZapInvoice(description: invoice.description, amount: amt, string: invoice.string, expiry: invoice.expiry, payment_hash: invoice.payment_hash, created_at: invoice.created_at)
}
func preimage_matches_invoice<T>(_ preimage: String, inv: LightningInvoice<T>) -> Bool {
guard let raw_preimage = hex_decode(preimage) else {
return false
}
let hashed = sha256(Data(raw_preimage))
return inv.payment_hash == hashed
}
func determine_zap_target(_ ev: NostrEvent) -> ZapTarget? {
guard let ptag = event_tag(ev, name: "p") else {
return nil
}
if let etag = event_tag(ev, name: "e") {
return ZapTarget.note(id: etag, author: ptag)
}
return .profile(ptag)
}
func decode_bolt11(_ s: String) -> Invoice? {
var bs = blocks()
bs.num_blocks = 0
blocks_init(&bs)
let bytes = s.utf8CString
let _ = bytes.withUnsafeBufferPointer { p in
damus_parse_content(&bs, p.baseAddress)
}
guard bs.num_blocks == 1 else {
blocks_free(&bs)
return nil
}
let block = bs.blocks[0]
guard let converted = convert_block(block, tags: []) else {
blocks_free(&bs)
return nil
}
guard case .invoice(let invoice) = converted else {
blocks_free(&bs)
return nil
}
blocks_free(&bs)
return invoice
}
func event_tag(_ ev: NostrEvent, name: String) -> String? {
for tag in ev.tags {
if tag.count >= 2 && tag[0] == name {
return tag[1]
}
}
return nil
}
func decode_nostr_event_json(_ desc: String) -> NostrEvent? {
let decoder = JSONDecoder()
guard let dat = desc.data(using: .utf8) else {
return nil
}
guard let ev = try? decoder.decode(NostrEvent.self, from: dat) else {
return nil
}
return ev
}
func decode_zap_request(_ desc: String) -> ZapRequest? {
let decoder = JSONDecoder()
guard let jsonData = desc.data(using: .utf8) else {
return nil
}
guard let jsonArray = try? JSONSerialization.jsonObject(with: jsonData) as? [[Any]] else {
return nil
}
for array in jsonArray {
guard array.count == 2 else {
continue
}
let mkey = array.first.flatMap { $0 as? String }
if let key = mkey, key == "application/nostr" {
guard let dat = try? JSONSerialization.data(withJSONObject: array[1], options: []) else {
return nil
}
guard let zap_req = try? decoder.decode(NostrEvent.self, from: dat) else {
return nil
}
guard zap_req.kind == 9734 else {
return nil
}
/// Ensure the signature on the zap request is correct
guard case .ok = validate_event(ev: zap_req) else {
return nil
}
return ZapRequest(ev: zap_req)
}
}
return nil
}
func fetch_zapper_from_lnurl(_ lnurl: String) async -> String? {
guard let endpoint = await fetch_static_payreq(lnurl) else {
return nil
}
guard let allows = endpoint.allowsNostr, allows else {
return nil
}
guard let key = endpoint.nostrPubkey, key.count == 64 else {
return nil
}
return endpoint.nostrPubkey
}
func decode_lnurl(_ lnurl: String) -> URL? {
guard let decoded = try? bech32_decode(lnurl) else {
return nil
}
guard decoded.hrp == "lnurl" else {
return nil
}
guard let url = URL(string: String(decoding: decoded.data, as: UTF8.self)) else {
return nil
}
return url
}
func fetch_static_payreq(_ lnurl: String) async -> LNUrlPayRequest? {
guard let url = decode_lnurl(lnurl) else {
return nil
}
guard let ret = try? await URLSession.shared.data(from: url) else {
return nil
}
let json_str = String(decoding: ret.0, as: UTF8.self)
guard let endpoint: LNUrlPayRequest = decode_json(json_str) else {
return nil
}
return endpoint
}
func fetch_zap_invoice(_ payreq: LNUrlPayRequest, zapreq: NostrEvent, amount: Int64) async -> String? {
guard var base_url = payreq.callback.flatMap({ URLComponents(string: $0) }) else {
return nil
}
let zappable = payreq.allowsNostr ?? false
var query = [URLQueryItem(name: "amount", value: "\(amount)")]
if zappable {
if let json = encode_json(zapreq) {
print("zapreq json: \(json)")
query.append(URLQueryItem(name: "nostr", value: json))
}
}
base_url.queryItems = query
guard let url = base_url.url else {
return nil
}
print("url \(url)")
guard let ret = try? await URLSession.shared.data(from: url) else {
return nil
}
let json_str = String(decoding: ret.0, as: UTF8.self)
guard let result: LNUrlPayResponse = decode_json(json_str) else {
print("fetch_zap_invoice error: \(json_str)")
return nil
}
return result.pr
}
-65
View File
@@ -1,65 +0,0 @@
//
// Zaps.swift
// damus
//
// Created by William Casarin on 2023-01-16.
//
import Foundation
class Zaps {
var zaps: [String: Zap]
let our_pubkey: String
var our_zaps: [String: [Zap]]
var event_counts: [String: Int]
var event_totals: [String: Int64]
init(our_pubkey: String) {
self.zaps = [:]
self.our_pubkey = our_pubkey
self.our_zaps = [:]
self.event_counts = [:]
self.event_totals = [:]
}
func add_zap(zap: Zap) {
if zaps[zap.event.id] != nil {
return
}
self.zaps[zap.event.id] = zap
// record our zaps for an event
if zap.request.ev.pubkey == our_pubkey {
switch zap.target {
case .note(let note_target):
if our_zaps[note_target.note_id] == nil {
our_zaps[note_target.note_id] = [zap]
} else {
let _ = insert_uniq_sorted_zap(zaps: &(our_zaps[note_target.note_id]!), new_zap: zap)
}
case .profile(_):
break
}
}
// don't count tips to self. lame.
guard zap.request.ev.pubkey != zap.target.pubkey else {
return
}
let id = zap.target.id
if event_counts[id] == nil {
event_counts[id] = 0
}
if event_totals[id] == nil {
event_totals[id] = 0
}
event_counts[id] = event_counts[id]! + 1
event_totals[id] = event_totals[id]! + zap.invoice.amount
return
}
}
+4 -32
View File
@@ -21,33 +21,18 @@ enum ActionBarSheet: Identifiable {
struct EventActionBar: View { struct EventActionBar: View {
let damus_state: DamusState let damus_state: DamusState
let event: NostrEvent let event: NostrEvent
let test_lnurl: String?
let generator = UIImpactFeedbackGenerator(style: .medium) let generator = UIImpactFeedbackGenerator(style: .medium)
// just used for previews
@State var sheet: ActionBarSheet? = nil @State var sheet: ActionBarSheet? = nil
@State var confirm_boost: Bool = false @State var confirm_boost: Bool = false
@State var show_share_sheet: Bool = false @State var show_share_sheet: Bool = false
@StateObject var bar: ActionBarModel @StateObject var bar: ActionBarModel
init(damus_state: DamusState, event: NostrEvent, bar: ActionBarModel, test_lnurl: String? = nil) {
self.damus_state = damus_state
self.event = event
self.test_lnurl = test_lnurl
_bar = StateObject.init(wrappedValue: bar)
}
var lnurl: String? {
test_lnurl ?? damus_state.profiles.lookup(id: event.pubkey)?.lnurl
}
var body: some View { var body: some View {
HStack { HStack {
if damus_state.keypair.privkey != nil { if damus_state.keypair.privkey != nil {
EventActionButton(img: "bubble.left", col: nil) { EventActionButton(img: "bubble.left", col: nil) {
notify(.reply, event) notify(.reply, event)
} }
.accessibilityLabel(NSLocalizedString("Reply", comment: "Accessibility label for reply button"))
} }
Spacer() Spacer()
ZStack { ZStack {
@@ -59,14 +44,12 @@ struct EventActionBar: View {
self.confirm_boost = true self.confirm_boost = true
} }
} }
.accessibilityLabel(NSLocalizedString("Boosts", comment: "Accessibility label for boosts button"))
Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")") Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
.offset(x: 18) .offset(x: 18)
.font(.footnote.weight(.medium)) .font(.footnote.weight(.medium))
.foregroundColor(bar.boosted ? Color.green : Color.gray) .foregroundColor(bar.boosted ? Color.green : Color.gray)
} }
Spacer() Spacer()
ZStack { ZStack {
LikeButton(liked: bar.liked) { LikeButton(liked: bar.liked) {
if bar.liked { if bar.liked {
@@ -81,17 +64,10 @@ struct EventActionBar: View {
.foregroundColor(bar.liked ? Color.accentColor : Color.gray) .foregroundColor(bar.liked ? Color.accentColor : Color.gray)
} }
if let lnurl = self.lnurl {
Spacer()
ZapButton(damus_state: damus_state, event: event, lnurl: lnurl, bar: bar)
}
Spacer() Spacer()
EventActionButton(img: "square.and.arrow.up", col: Color.gray) { EventActionButton(img: "square.and.arrow.up", col: Color.gray) {
show_share_sheet = true show_share_sheet = true
} }
.accessibilityLabel(NSLocalizedString("Share", comment: "Button to share a post"))
} }
.sheet(isPresented: $show_share_sheet) { .sheet(isPresented: $show_share_sheet) {
if let note_id = bech32_note_id(event.id) { if let note_id = bech32_note_id(event.id) {
@@ -169,7 +145,6 @@ struct LikeButton: View {
Image(liked ? "shaka-full" : "shaka-line") Image(liked ? "shaka-full" : "shaka-line")
.foregroundColor(liked ? .accentColor : .gray) .foregroundColor(liked ? .accentColor : .gray)
} }
.accessibilityLabel(NSLocalizedString("Like", comment: "Accessibility Label for Like button"))
} }
} }
@@ -180,11 +155,10 @@ struct EventActionBar_Previews: PreviewProvider {
let ds = test_damus_state() let ds = test_damus_state()
let ev = NostrEvent(content: "hi", pubkey: pk) let ev = NostrEvent(content: "hi", pubkey: pk)
let bar = ActionBarModel.empty() let bar = ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
let likedbar = ActionBarModel(likes: 10, boosts: 0, zaps: 0, zap_total: 0, our_like: nil, our_boost: nil, our_zap: nil) let likedbar = ActionBarModel(likes: 10, boosts: 10, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
let likedbar_ours = ActionBarModel(likes: 10, boosts: 0, zaps: 0, zap_total: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: nil, our_zap: nil) let likedbar_ours = ActionBarModel(likes: 100, boosts: 100, tips: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: nil, our_tip: nil)
let maxed_bar = ActionBarModel(likes: 999, boosts: 999, zaps: 999, zap_total: 99999999, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: NostrEvent(id: "", content: "", pubkey: ""), our_zap: nil) let maxed_bar = ActionBarModel(likes: 999, boosts: 999, tips: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: NostrEvent(id: "", content: "", pubkey: ""), our_tip: nil)
let zapbar = ActionBarModel(likes: 0, boosts: 0, zaps: 5, zap_total: 10000000, our_like: nil, our_boost: nil, our_zap: nil)
VStack(spacing: 50) { VStack(spacing: 50) {
EventActionBar(damus_state: ds, event: ev, bar: bar) EventActionBar(damus_state: ds, event: ev, bar: bar)
@@ -194,8 +168,6 @@ struct EventActionBar_Previews: PreviewProvider {
EventActionBar(damus_state: ds, event: ev, bar: likedbar_ours) EventActionBar(damus_state: ds, event: ev, bar: likedbar_ours)
EventActionBar(damus_state: ds, event: ev, bar: maxed_bar) EventActionBar(damus_state: ds, event: ev, bar: maxed_bar)
EventActionBar(damus_state: ds, event: ev, bar: zapbar, test_lnurl: "lnurl")
} }
.padding(20) .padding(20)
} }
+2 -2
View File
@@ -28,8 +28,8 @@ struct EventDetailBar: View {
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
} }
if bar.zaps > 0 { if bar.tips > 0 {
Text("\(Text("\(bar.zaps)", comment: "Number of zap payments on a post.").font(.body.bold())) \(Text(String(format: NSLocalizedString("zaps_count", comment: "Part of a larger sentence to describe how many zap payments there are on a post."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.") Text("\(Text("\(bar.tips)", comment: "Number of tip payments on a post.").font(.body.bold())) \(Text(String(format: NSLocalizedString("tips_count", comment: "Part of a larger sentence to describe how many tip payments there are on a post."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many tip payments there are on a post. In source English, the first variable is the number of tip payments, and the second variable is 'Tip' or 'Tips'.")
} }
} }
} }
-1
View File
@@ -55,7 +55,6 @@ struct CarouselItemView: View {
.font(.title2) .font(.title2)
.foregroundColor(Color.white) .foregroundColor(Color.white)
.padding([.leading,.trailing], 50.0) .padding([.leading,.trailing], 50.0)
.minimumScaleFactor(0.5)
} }
} }
} }
+2 -9
View File
@@ -96,24 +96,17 @@ struct ChatView: View {
if let ref_id = thread.replies.lookup(event.id) { if let ref_id = thread.replies.lookup(event.id) {
if !is_reply_to_prev() { if !is_reply_to_prev() {
/* ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews)
ReplyQuoteView(keypair: damus_state.keypair, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews)
.frame(maxHeight: expand_reply ? nil : 100) .frame(maxHeight: expand_reply ? nil : 100)
.environmentObject(thread) .environmentObject(thread)
.onTapGesture { .onTapGesture {
expand_reply = !expand_reply expand_reply = !expand_reply
} }
*/
ReplyDescription ReplyDescription
} }
} }
let show_images = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey) NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
NoteContentView(damus_state: damus_state,
event: event,
show_images: show_images,
artifacts: .just_content(event.content),
size: .normal)
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey { if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
let bar = make_actionbar_model(ev: event, damus: damus_state) let bar = make_actionbar_model(ev: event, damus: damus_state)
+1 -1
View File
@@ -24,7 +24,7 @@ struct ChatroomView: View {
next_ev: ind == count-1 ? nil : thread.events[ind+1], next_ev: ind == count-1 ? nil : thread.events[ind+1],
damus_state: damus damus_state: damus
) )
.event_context_menu(ev, keypair: damus.keypair, target_pubkey: ev.pubkey) .event_context_menu(ev, keypair: damus.keypair)
.onTapGesture { .onTapGesture {
if thread.initial_event.id == ev.id { if thread.initial_event.id == ev.id {
//dismiss() //dismiss()
+97 -207
View File
@@ -5,119 +5,110 @@
// Created by William Casarin on 2022-06-09. // Created by William Casarin on 2022-06-09.
// //
import AVFoundation import AVFoundation
import Kingfisher
import SwiftUI import SwiftUI
import LocalAuthentication import Kingfisher
struct ConfigView: View { struct ConfigView: View {
let state: DamusState let state: DamusState
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@State var show_add_relay: Bool = false
@State var confirm_logout: Bool = false @State var confirm_logout: Bool = false
@State var confirm_delete_account: Bool = false @State var new_relay: String = ""
@State var show_privkey: Bool = false @State var show_privkey: Bool = false
@State var has_authenticated_locally: Bool = false
@State var show_api_key: Bool = false
@State var privkey: String @State var privkey: String
@State var privkey_copied: Bool = false @State var privkey_copied: Bool = false
@State var pubkey_copied: Bool = false @State var pubkey_copied: Bool = false
@State var delete_text: String = "" @State var relays: [RelayDescriptor]
@EnvironmentObject var user_settings: UserSettingsStore
@ObservedObject var settings: UserSettingsStore
let generator = UIImpactFeedbackGenerator(style: .light) let generator = UIImpactFeedbackGenerator(style: .light)
init(state: DamusState) { init(state: DamusState) {
self.state = state self.state = state
_privkey = State(initialValue: self.state.keypair.privkey_bech32 ?? "") _privkey = State(initialValue: self.state.keypair.privkey_bech32 ?? "")
_settings = ObservedObject(initialValue: state.settings) _relays = State(initialValue: state.pool.descriptors)
}
func authenticateLocally(completion: @escaping (Bool) -> Void) {
// Need to authenticate only once while ConfigView is presented
guard !has_authenticated_locally else {
completion(true)
return
}
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: NSLocalizedString("Local authentication to access private key", comment: "Face ID usage description shown when trying to access private key")) { success, error in
DispatchQueue.main.async {
has_authenticated_locally = success
completion(success)
}
}
} else {
// If there's no authentication set up on the device, let the user copy the key without it
has_authenticated_locally = true
completion(true)
}
} }
// TODO: (jb55) could be more general but not gonna worry about it atm // TODO: (jb55) could be more general but not gonna worry about it atm
func CopyButton(is_pk: Bool) -> some View { func CopyButton(is_pk: Bool) -> some View {
return Button(action: { return Button(action: {
let copyKey = { UIPasteboard.general.string = is_pk ? self.state.keypair.pubkey_bech32 : self.privkey
UIPasteboard.general.string = is_pk ? self.state.keypair.pubkey_bech32 : self.privkey self.privkey_copied = !is_pk
self.privkey_copied = !is_pk self.pubkey_copied = is_pk
self.pubkey_copied = is_pk generator.impactOccurred()
generator.impactOccurred()
}
if has_authenticated_locally {
copyKey()
} else {
authenticateLocally { success in
if success {
copyKey()
}
}
}
}) { }) {
let copied = is_pk ? self.pubkey_copied : self.privkey_copied let copied = is_pk ? self.pubkey_copied : self.privkey_copied
Image(systemName: copied ? "checkmark.circle" : "doc.on.doc") Image(systemName: copied ? "checkmark.circle" : "doc.on.doc")
} }
} }
var recommended: [RelayDescriptor] {
let rs: [RelayDescriptor] = []
return BOOTSTRAP_RELAYS.reduce(into: rs) { (xs, x) in
if let _ = state.pool.get_relay(x) {
} else {
xs.append(RelayDescriptor(url: URL(string: x)!, info: .rw))
}
}
}
var body: some View { var body: some View {
ZStack(alignment: .leading) { ZStack(alignment: .leading) {
Form { Form {
Section {
List(Array(relays), id: \.url) { relay in
RelayView(state: state, relay: relay.url.absoluteString)
}
} header: {
HStack {
Text("Relays", comment: "Header text for relay server list for configuration.")
Spacer()
Button(action: { show_add_relay = true }) {
Image(systemName: "plus")
.foregroundColor(.accentColor)
}
}
}
if recommended.count > 0 {
Section(NSLocalizedString("Recommended Relays", comment: "Section title for recommend relay servers that could be added as part of configuration")) {
List(recommended, id: \.url) { r in
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
}
}
}
Section(NSLocalizedString("Public Account ID", comment: "Section title for the user's public account ID.")) { Section(NSLocalizedString("Public Account ID", comment: "Section title for the user's public account ID.")) {
HStack { HStack {
Text(state.keypair.pubkey_bech32) Text(state.keypair.pubkey_bech32)
CopyButton(is_pk: true) CopyButton(is_pk: true)
} }
.clipShape(RoundedRectangle(cornerRadius: 5)) .clipShape(RoundedRectangle(cornerRadius: 5))
} }
if let sec = state.keypair.privkey_bech32 { if let sec = state.keypair.privkey_bech32 {
Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) { Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) {
HStack { HStack {
if show_privkey == false || !has_authenticated_locally { if show_privkey == false {
SecureField(NSLocalizedString("Private Key", comment: "Title of the secure field that holds the user's private key."), text: $privkey) SecureField(NSLocalizedString("Private Key", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
.disabled(true) .disabled(true)
} else { } else {
Text(sec) Text(sec)
.clipShape(RoundedRectangle(cornerRadius: 5)) .clipShape(RoundedRectangle(cornerRadius: 5))
} }
CopyButton(is_pk: false) CopyButton(is_pk: false)
} }
Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey) Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey)
.onChange(of: show_privkey) { newValue in
if newValue {
authenticateLocally { success in
show_privkey = success
}
}
}
} }
} }
Section(NSLocalizedString("Wallet Selector", comment: "Section title for selection of wallet.")) { Section(NSLocalizedString("Wallet Selector", comment: "Section title for selection of wallet.")) {
Toggle(NSLocalizedString("Show wallet selector", comment: "Toggle to show or hide selection of wallet."), isOn: $settings.show_wallet_selector).toggleStyle(.switch) Toggle(NSLocalizedString("Show wallet selector", comment: "Toggle to show or hide selection of wallet."), isOn: $user_settings.show_wallet_selector).toggleStyle(.switch)
Picker(NSLocalizedString("Select default wallet", comment: "Prompt selection of user's default wallet"), Picker(NSLocalizedString("Select default wallet", comment: "Prompt selection of user's default wallet"),
selection: $settings.default_wallet) { selection: $user_settings.default_wallet) {
ForEach(Wallet.allCases, id: \.self) { wallet in ForEach(Wallet.allCases, id: \.self) { wallet in
Text(wallet.model.displayName) Text(wallet.model.displayName)
.tag(wallet.model.tag) .tag(wallet.model.tag)
@@ -125,55 +116,8 @@ struct ConfigView: View {
} }
} }
Section(NSLocalizedString("Translations", comment: "Section title for selecting the translation service.")) {
Picker(NSLocalizedString("Service", comment: "Prompt selection of translation service provider."), selection: $settings.translation_service) {
ForEach(TranslationService.allCases, id: \.self) { server in
Text(server.model.displayName)
.tag(server.model.tag)
}
}
if settings.translation_service == .libretranslate {
Picker(NSLocalizedString("Server", comment: "Prompt selection of LibreTranslate server to perform machine translations on notes"), selection: $settings.libretranslate_server) {
ForEach(LibreTranslateServer.allCases, id: \.self) { server in
Text(server.model.displayName)
.tag(server.model.tag)
}
}
if settings.libretranslate_server == .custom {
TextField(NSLocalizedString("URL", comment: "Example URL to LibreTranslate server"), text: $settings.libretranslate_url)
.disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none)
}
SecureField(NSLocalizedString("API Key (optional)", comment: "Prompt for optional entry of API Key to use translation server."), text: $settings.libretranslate_api_key)
.disableAutocorrection(true)
.disabled(settings.translation_service != .libretranslate)
.autocapitalization(UITextAutocapitalizationType.none)
}
if settings.translation_service == .deepl {
Picker(NSLocalizedString("Plan", comment: "Prompt selection of DeepL subscription plan to perform machine translations on notes"), selection: $settings.deepl_plan) {
ForEach(DeepLPlan.allCases, id: \.self) { server in
Text(server.model.displayName)
.tag(server.model.tag)
}
}
SecureField(NSLocalizedString("API Key (required)", comment: "Prompt for required entry of API Key to use translation server."), text: $settings.deepl_api_key)
.disableAutocorrection(true)
.disabled(settings.translation_service != .deepl)
.autocapitalization(UITextAutocapitalizationType.none)
if settings.deepl_api_key == "" {
Link(NSLocalizedString("Get API Key", comment: "Button to navigate to DeepL website to get a translation API key."), destination: URL(string: "https://www.deepl.com/pro-api")!)
}
}
}
Section(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen")) { Section(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen")) {
Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $settings.left_handed) Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $user_settings.left_handed)
.toggleStyle(.switch) .toggleStyle(.switch)
} }
@@ -185,36 +129,15 @@ struct ConfigView: View {
} }
} }
if state.is_privkey_user { Section(NSLocalizedString("Reset", comment: "Section title for resetting the user")) {
Section(NSLocalizedString("Delete", comment: "Section title for deleting the user")) { Button(NSLocalizedString("Logout", comment: "Button to logout the user.")) {
Button(NSLocalizedString("Delete Account", comment: "Button to delete the user's account."), role: .destructive) { confirm_logout = true
confirm_delete_account = true
}
} }
} }
} }
} }
.navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view.")) .navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view."))
.navigationBarTitleDisplayMode(.large) .navigationBarTitleDisplayMode(.large)
.alert(NSLocalizedString("Delete Account", comment: "Alert for deleting the users account."), isPresented: $confirm_delete_account) {
TextField(NSLocalizedString("Type DELETE to delete", comment: "Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should."), text: $delete_text)
Button(NSLocalizedString("Cancel", comment: "Cancel deleting the user."), role: .cancel) {
confirm_delete_account = false
}
Button(NSLocalizedString("Delete", comment: "Button for deleting the users account."), role: .destructive) {
guard let full_kp = state.keypair.to_full() else {
return
}
guard delete_text == "DELETE" else {
return
}
let ev = created_deleted_account_profile(keypair: full_kp)
state.pool.send(.event(ev))
notify(.logout, ())
}
}
.alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) { .alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) {
Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) { Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user."), role: .cancel) {
confirm_logout = false confirm_logout = false
@@ -225,83 +148,50 @@ struct ConfigView: View {
} message: { } message: {
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.") Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.")
} }
.sheet(isPresented: $show_add_relay) {
AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in
guard var relay = m_relay else {
return
}
if relay.starts(with: "wss://") == false && relay.starts(with: "ws://") == false {
relay = "wss://" + relay
}
guard let url = URL(string: relay) else {
return
}
guard let ev = state.contacts.event else {
return
}
guard let privkey = state.keypair.privkey else {
return
}
let info = RelayInfo.rw
guard (try? state.pool.add_relay(url, info: info)) != nil else {
return
}
state.pool.connect(to: [relay])
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: info) else {
return
}
process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: ev)
state.pool.send(.event(new_ev))
}
}
.onReceive(handle_notify(.switched_timeline)) { _ in .onReceive(handle_notify(.switched_timeline)) { _ in
dismiss() dismiss()
} }
} .onReceive(handle_notify(.relays_changed)) { _ in
self.relays = state.pool.descriptors
var libretranslate_view: some View {
VStack {
Picker(NSLocalizedString("Server", comment: "Prompt selection of LibreTranslate server to perform machine translations on notes"), selection: $settings.libretranslate_server) {
ForEach(LibreTranslateServer.allCases, id: \.self) { server in
Text(server.model.displayName)
.tag(server.model.tag)
}
}
TextField(NSLocalizedString("URL", comment: "Example URL to LibreTranslate server"), text: $settings.libretranslate_url)
.disableAutocorrection(true)
.disabled(settings.libretranslate_server != .custom)
.autocapitalization(UITextAutocapitalizationType.none)
HStack {
let libretranslate_api_key_placeholder = NSLocalizedString("API Key (optional)", comment: "Prompt for optional entry of API Key to use translation server.")
if show_api_key {
TextField(libretranslate_api_key_placeholder, text: $settings.libretranslate_api_key)
.disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none)
if settings.libretranslate_api_key != "" {
Button(NSLocalizedString("Hide API Key", comment: "Button to hide the LibreTranslate server API key.")) {
show_api_key = false
}
}
} else {
SecureField(libretranslate_api_key_placeholder, text: $settings.libretranslate_api_key)
.disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none)
if settings.libretranslate_api_key != "" {
Button(NSLocalizedString("Show API Key", comment: "Button to show the LibreTranslate server API key.")) {
show_api_key = true
}
}
}
}
}
}
var deepl_view: some View {
VStack {
Picker(NSLocalizedString("Plan", comment: "Prompt selection of DeepL subscription plan to perform machine translations on notes"), selection: $settings.deepl_plan) {
ForEach(DeepLPlan.allCases, id: \.self) { server in
Text(server.model.displayName)
.tag(server.model.tag)
}
}
HStack {
let deepl_api_key_placeholder = NSLocalizedString("API Key (required)", comment: "Prompt for required entry of API Key to use translation server.")
if show_api_key {
TextField(deepl_api_key_placeholder, text: $settings.deepl_api_key)
.disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none)
if settings.deepl_api_key != "" {
Button(NSLocalizedString("Hide API Key", comment: "Button to hide the DeepL translation API key.")) {
show_api_key = false
}
}
} else {
SecureField(deepl_api_key_placeholder, text: $settings.deepl_api_key)
.disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none)
if settings.deepl_api_key != "" {
Button(NSLocalizedString("Show API Key", comment: "Button to show the DeepL translation API key.")) {
show_api_key = true
}
}
}
if settings.deepl_api_key == "" {
Link(NSLocalizedString("Get API Key", comment: "Button to navigate to DeepL website to get a translation API key."), destination: URL(string: "https://www.deepl.com/pro-api")!)
}
}
} }
} }
} }
+22 -36
View File
@@ -12,7 +12,6 @@ struct DMChatView: View {
let pubkey: String let pubkey: String
@EnvironmentObject var dms: DirectMessageModel @EnvironmentObject var dms: DirectMessageModel
@State var message: String = "" @State var message: String = ""
@State var showPrivateKeyWarning: Bool = false
var Messages: some View { var Messages: some View {
ScrollViewReader { scroller in ScrollViewReader { scroller in
@@ -20,7 +19,7 @@ struct DMChatView: View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in
DMView(event: dms.events[ind], damus_state: damus_state) DMView(event: dms.events[ind], damus_state: damus_state)
.event_context_menu(ev, keypair: damus_state.keypair, target_pubkey: ev.pubkey) .event_context_menu(ev, keypair: damus_state.keypair)
} }
EndBlock(height: 80) EndBlock(height: 80)
} }
@@ -64,8 +63,6 @@ struct DMChatView: View {
) )
.padding(16) .padding(16)
.foregroundColor(Color.primary) .foregroundColor(Color.primary)
.frame(minHeight: 70, maxHeight: 150, alignment: .bottom)
.fixedSize(horizontal: false, vertical: true)
} }
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@@ -94,30 +91,28 @@ struct DMChatView: View {
InputField InputField
if !message.isEmpty { if !message.isEmpty {
Button( Button(role: .none, action: send_message) {
role: .none,
action: {
showPrivateKeyWarning = contentContainsPrivateKey(message)
if !showPrivateKeyWarning {
send_message()
}
}
) {
Label("", systemImage: "arrow.right.circle") Label("", systemImage: "arrow.right.circle")
.font(.title) .font(.title)
} }
} }
} }
.fixedSize(horizontal: false, vertical: true)
.frame(minHeight: 70, maxHeight: 150, alignment: .bottom)
Text(message).opacity(0).padding(.all, 8)
.fixedSize(horizontal: false, vertical: true)
.frame(minHeight: 70, maxHeight: 150, alignment: .bottom)
} }
.fixedSize(horizontal: false, vertical: true) .frame(height: 50 + 20 * CGFloat(text_lines))
.frame(minHeight: 70, maxHeight: 150, alignment: .bottom) }
var text_lines: Int {
var lines = 1
for c in message {
if lines > 4 {
return lines
}
if c.isNewline {
lines += 1
}
}
return lines
} }
func send_message() { func send_message() {
@@ -147,24 +142,15 @@ struct DMChatView: View {
Footer Footer
} }
Text("Send a message to start the conversation...", comment: "Text prompt for user to send a message to the other user.") Text("Send a message to start the conversation...", comment: "Text prompt for user to send a message to the other user.")
.lineLimit(nil) .lineLimit(nil)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.padding(.horizontal, 40) .padding(.horizontal, 40)
.opacity(((dms.events.count == 0) ? 1.0 : 0.0)) .opacity(((dms.events.count == 0) ? 1.0 : 0.0))
.foregroundColor(.gray) .foregroundColor(.gray)
} }
.navigationTitle(NSLocalizedString("DMs", comment: "Navigation title for DMs view, where DM is the English abbreviation for Direct Message.")) .navigationTitle(NSLocalizedString("DMs", comment: "Navigation title for DMs view, where DM is the English abbreviation for Direct Message."))
.toolbar { Header } .toolbar { Header }
.alert(NSLocalizedString("Note contains \"nsec1\" private key. Are you sure?", comment: "Alert user that they might be attempting to paste a private key and ask them to confirm."), isPresented: $showPrivateKeyWarning, actions: {
Button(NSLocalizedString("No", comment: "Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key."), role: .cancel) {
showPrivateKeyWarning = false
}
Button(NSLocalizedString("Yes, Post with Private Key", comment: "Button to proceed with posting a note even though it looks like they might be posting a private key."), role: .destructive) {
send_message()
}
})
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ struct DMView: View {
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey) let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal) NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
.foregroundColor(is_ours ? Color.white : Color.primary) .foregroundColor(is_ours ? Color.white : Color.primary)
.padding(10) .padding(10)
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15)) .background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))
+8 -7
View File
@@ -43,7 +43,6 @@ struct DirectMessagesView: View {
} }
} }
} }
.padding(.horizontal)
} }
} }
@@ -63,16 +62,16 @@ struct DirectMessagesView: View {
} }
var body: some View { var body: some View {
VStack(spacing: 0) { VStack {
CustomPicker(selection: $dm_type, content: { Picker(NSLocalizedString("DM Type", comment: "DM selector for seeing either DMs or message requests, which are messages that have not been responded to yet. DM is the English abbreviation for Direct Message."), selection: $dm_type) {
Text("DMs", comment: "Picker option for DM selector for seeing only DMs that have been responded to. DM is the English abbreviation for Direct Message.") Text("DMs", comment: "Picker option for DM selector for seeing only DMs that have been responded to. DM is the English abbreviation for Direct Message.")
.tag(DMType.friend) .tag(DMType.friend)
Text("Requests", comment: "Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.") Text("Requests", comment: "Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message.")
.tag(DMType.rando) .tag(DMType.rando)
})
}
Divider() .pickerStyle(.segmented)
.frame(height: 1)
TabView(selection: $dm_type) { TabView(selection: $dm_type) {
MainContent(requests: false) MainContent(requests: false)
@@ -83,6 +82,8 @@ struct DirectMessagesView: View {
} }
.tabViewStyle(.page(indexDisplayMode: .never)) .tabViewStyle(.page(indexDisplayMode: .never))
} }
.padding(.horizontal)
.padding(.top)
.navigationTitle(NSLocalizedString("DMs", comment: "Navigation title for view of DMs, where DM is an English abbreviation for Direct Message.")) .navigationTitle(NSLocalizedString("DMs", comment: "Navigation title for view of DMs, where DM is an English abbreviation for Direct Message."))
} }
} }
+4 -11
View File
@@ -8,9 +8,8 @@
import SwiftUI import SwiftUI
struct EULAView: View { struct EULAView: View {
var state: SetupState?
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@State var accepted = false @State var creating_account = false
var body: some View { var body: some View {
ZStack { ZStack {
@@ -70,17 +69,11 @@ By using our Application, you signify your acceptance of this EULA. If you do no
""")) """))
.padding() .padding()
if state == .create_account { NavigationLink(destination: CreateAccountView(), isActive: $creating_account) {
NavigationLink(destination: CreateAccountView(), isActive: $accepted) { EmptyView()
EmptyView()
}
} else {
NavigationLink(destination: LoginView(), isActive: $accepted) {
EmptyView()
}
} }
DamusWhiteButton(NSLocalizedString("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.")) { DamusWhiteButton(NSLocalizedString("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.")) {
accepted = true creating_account = true
} }
DamusWhiteButton(NSLocalizedString("Reject", comment: "Button to reject the end user license agreement, which disallows the user from being let into the app.")) { DamusWhiteButton(NSLocalizedString("Reject", comment: "Button to reject the end user license agreement, which disallows the user from being let into the app.")) {
+1 -1
View File
@@ -196,7 +196,7 @@ struct EditMetadataView: View {
if let parts = nip05_parts { if let parts = nip05_parts {
Text("'\(parts.username)' at '\(parts.host)' will be used for verification", comment: "Description of how the nip05 identifier would be used for verification.") Text("'\(parts.username)' at '\(parts.host)' will be used for verification", comment: "Description of how the nip05 identifier would be used for verification.")
} else { } else {
Text("'\(nip05)' is an invalid NIP-05 identifier. It should look like an email.", comment: "Description of why the nip05 identifier is invalid.") Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.", comment: "Description of why the nip05 identifier is invalid.")
} }
}) })
+73 -35
View File
@@ -57,40 +57,80 @@ struct EventView: View {
} }
var body: some View { var body: some View {
VStack { return Group {
if event.known_kind == .boost { if event.known_kind == .boost, let inner_ev = event.inner_event {
if let inner_ev = event.inner_event { VStack(alignment: .leading) {
VStack(alignment: .leading) { let prof_model = ProfileModel(pubkey: event.pubkey, damus: damus)
let prof_model = ProfileModel(pubkey: event.pubkey, damus: damus) let follow_model = FollowersModel(damus_state: damus, target: event.pubkey)
let follow_model = FollowersModel(damus_state: damus, target: event.pubkey) let prof = damus.profiles.lookup(id: event.pubkey)
let prof = damus.profiles.lookup(id: event.pubkey) let booster_profile = ProfileView(damus_state: damus, profile: prof_model, followers: follow_model)
let booster_profile = ProfileView(damus_state: damus, profile: prof_model, followers: follow_model)
NavigationLink(destination: booster_profile) {
NavigationLink(destination: booster_profile) { Reposted(damus: damus, pubkey: event.pubkey, profile: prof)
Reposted(damus: damus, pubkey: event.pubkey, profile: prof)
}
.buttonStyle(PlainButtonStyle())
TextEvent(damus: damus, event: inner_ev, pubkey: inner_ev.pubkey, has_action_bar: has_action_bar, booster_pubkey: event.pubkey)
.padding([.top], 1)
} }
} else { .buttonStyle(PlainButtonStyle())
EmptyView() TextEvent(inner_ev, pubkey: inner_ev.pubkey, booster_pubkey: event.pubkey)
} .padding([.top], 1)
} else if event.known_kind == .zap {
if let zap = damus.zaps.zaps[event.id] {
ZapEvent(damus: damus, zap: zap)
} else {
EmptyView()
} }
} else { } else {
TextEvent(damus: damus, event: event, pubkey: pubkey, has_action_bar: has_action_bar, booster_pubkey: nil) TextEvent(event, pubkey: pubkey)
.padding([.top], 6) .padding([.top], 6)
} }
Divider()
.padding([.top], 4)
} }
} }
func TextEvent(_ event: NostrEvent, pubkey: String, booster_pubkey: String? = nil) -> some View {
return HStack(alignment: .top) {
let profile = damus.profiles.lookup(id: pubkey)
VStack {
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: .none, profiles: damus.profiles)
}
Spacer()
}
VStack(alignment: .leading) {
HStack(alignment: .center) {
EventProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true, size: .normal)
Text("\(format_relative_time(event.created_at))")
.foregroundColor(.gray)
Spacer()
}
EventBody(damus_state: damus, event: event, size: .normal)
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
BuilderEventView(damus: damus, event_id: mention.ref.id)
}
if has_action_bar {
Rectangle().frame(height: 2).opacity(0)
let bar = make_actionbar_model(ev: event, damus: damus)
EventActionBar(damus_state: damus, event: event, bar: bar)
.padding([.top], 4)
}
Divider()
.padding([.top], 4)
}
.padding([.leading], 2)
}
.contentShape(Rectangle())
.background(event_validity_color(event.validity))
.id(event.id)
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
.padding([.bottom], 2)
.event_context_menu(event, keypair: damus.keypair)
}
} }
// blame the porn bots for this code // blame the porn bots for this code
@@ -131,9 +171,9 @@ extension View {
} }
} }
func event_context_menu(_ event: NostrEvent, keypair: Keypair, target_pubkey: String) -> some View { func event_context_menu(_ event: NostrEvent, keypair: Keypair) -> some View {
return self.contextMenu { return self.contextMenu {
EventMenuContext(event: event, keypair: keypair, target_pubkey: target_pubkey) EventMenuContext(event: event, keypair: keypair)
} }
} }
@@ -157,19 +197,17 @@ func format_date(_ created_at: Int64) -> String {
func make_actionbar_model(ev: NostrEvent, damus: DamusState) -> ActionBarModel { func make_actionbar_model(ev: NostrEvent, damus: DamusState) -> ActionBarModel {
let likes = damus.likes.counts[ev.id] let likes = damus.likes.counts[ev.id]
let boosts = damus.boosts.counts[ev.id] let boosts = damus.boosts.counts[ev.id]
let zaps = damus.zaps.event_counts[ev.id] let tips = damus.tips.tips[ev.id]
let zap_total = damus.zaps.event_totals[ev.id]
let our_like = damus.likes.our_events[ev.id] let our_like = damus.likes.our_events[ev.id]
let our_boost = damus.boosts.our_events[ev.id] let our_boost = damus.boosts.our_events[ev.id]
let our_zap = damus.zaps.our_zaps[ev.id] let our_tip = damus.tips.our_tips[ev.id]
return ActionBarModel(likes: likes ?? 0, return ActionBarModel(likes: likes ?? 0,
boosts: boosts ?? 0, boosts: boosts ?? 0,
zaps: zaps ?? 0, tips: tips ?? 0,
zap_total: zap_total ?? 0,
our_like: our_like, our_like: our_like,
our_boost: our_boost, our_boost: our_boost,
our_zap: our_zap?.first our_tip: our_tip
) )
} }
+11 -18
View File
@@ -31,30 +31,23 @@ struct BuilderEventView: View {
return return
} }
guard id == subscription_uuid else { // Is current event
return if id == subscription_uuid {
if event != nil {
return
}
event = nostr_event
unsubscribe()
} }
guard nostr_event.known_kind == .text else {
return
}
if event != nil {
return
}
event = nostr_event
unsubscribe()
} }
func load() { func load() {
subscribe(filters: [ subscribe(filters: [
NostrFilter(ids: [self.event_id], limit: 1),
NostrFilter( NostrFilter(
kinds: [NostrKind.zap.rawValue], ids: [self.event_id],
referenced_ids: [self.event_id], limit: 1
limit: 500
) )
]) ])
} }
+1 -1
View File
@@ -23,7 +23,7 @@ struct EmbeddedEventView: View {
EventBody(damus_state: damus_state, event: event, size: .small) EventBody(damus_state: damus_state, event: event, size: .small)
} }
.event_context_menu(event, keypair: damus_state.keypair, target_pubkey: pubkey) .event_context_menu(event, keypair: damus_state.keypair)
} }
} }
+1 -1
View File
@@ -23,7 +23,7 @@ struct EventBody: View {
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey, booster_pubkey: nil) let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey, booster_pubkey: nil)
NoteContentView(damus_state: damus_state, event: event, show_images: should_show_img, artifacts: .just_content(content), size: size) NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_img, artifacts: .just_content(content), size: size)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
} }
} }
+4 -5
View File
@@ -10,7 +10,6 @@ import SwiftUI
struct EventMenuContext: View { struct EventMenuContext: View {
let event: NostrEvent let event: NostrEvent
let keypair: Keypair let keypair: Keypair
let target_pubkey: String
var body: some View { var body: some View {
@@ -21,7 +20,7 @@ struct EventMenuContext: View {
} }
Button { Button {
UIPasteboard.general.string = bech32_pubkey(target_pubkey) UIPasteboard.general.string = bech32_pubkey(event.pubkey)
} label: { } label: {
Label(NSLocalizedString("Copy User Pubkey", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "person") Label(NSLocalizedString("Copy User Pubkey", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "person")
} }
@@ -45,16 +44,16 @@ struct EventMenuContext: View {
} }
// Only allow reporting if logged in with private key and the currently viewed profile is not the logged in profile. // Only allow reporting if logged in with private key and the currently viewed profile is not the logged in profile.
if keypair.pubkey != target_pubkey && keypair.privkey != nil { if keypair.pubkey != event.pubkey && keypair.privkey != nil {
Button(role: .destructive) { Button(role: .destructive) {
let target: ReportTarget = .note(ReportNoteTarget(pubkey: target_pubkey, note_id: event.id)) let target: ReportTarget = .note(ReportNoteTarget(pubkey: event.pubkey, note_id: event.id))
notify(.report, target) notify(.report, target)
} label: { } label: {
Label(NSLocalizedString("Report", comment: "Context menu option for reporting content."), systemImage: "exclamationmark.bubble") Label(NSLocalizedString("Report", comment: "Context menu option for reporting content."), systemImage: "exclamationmark.bubble")
} }
Button(role: .destructive) { Button(role: .destructive) {
notify(.block, target_pubkey) notify(.block, event.pubkey)
} label: { } label: {
Label(NSLocalizedString("Block", comment: "Context menu option for blocking users."), systemImage: "exclamationmark.octagon") Label(NSLocalizedString("Block", comment: "Context menu option for blocking users."), systemImage: "exclamationmark.octagon")
} }
+4 -4
View File
@@ -25,11 +25,11 @@ struct MutedEventView: View {
self.selected = selected self.selected = selected
self._nav_target = nav_target self._nav_target = nav_target
self._navigating = navigating self._navigating = navigating
self._shown = State(initialValue: should_show_event(contacts: damus_state.contacts, ev: event)) self._shown = State(initialValue: !should_hide_event(contacts: damus_state.contacts, ev: event))
} }
var should_mute: Bool { var should_mute: Bool {
return !should_show_event(contacts: damus_state.contacts, ev: event) return should_hide_event(contacts: damus_state.contacts, ev: event)
} }
var FillColor: Color { var FillColor: Color {
@@ -42,9 +42,9 @@ struct MutedEventView: View {
.foregroundColor(FillColor) .foregroundColor(FillColor)
HStack { HStack {
Text("Post from a user you've blocked", comment: "Text to indicate that what is being shown is a post from a user who has been blocked.") Text("Post from a user you've blocked")
Spacer() Spacer()
Button(shown ? NSLocalizedString("Hide", comment: "Button to hide a post from a user who has been blocked.") : NSLocalizedString("Show", comment: "Button to show a post from a user who has been blocked.")) { Button(shown ? "Hide" : "Show") {
shown.toggle() shown.toggle()
} }
} }
+1 -1
View File
@@ -49,7 +49,7 @@ struct SelectedEventView: View {
.padding([.top], 4) .padding([.top], 4)
} }
.padding([.leading], 2) .padding([.leading], 2)
.event_context_menu(event, keypair: damus.keypair, target_pubkey: event.pubkey) .event_context_menu(event, keypair: damus.keypair)
} }
} }
} }
-72
View File
@@ -1,72 +0,0 @@
//
// TextEvent.swift
// damus
//
// Created by William Casarin on 2023-02-03.
//
import SwiftUI
struct TextEvent: View {
let damus: DamusState
let event: NostrEvent
let pubkey: String
let has_action_bar: Bool
let booster_pubkey: String?
var body: some View {
HStack(alignment: .top) {
let profile = damus.profiles.lookup(id: pubkey)
VStack {
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: .none, profiles: damus.profiles)
}
Spacer()
}
VStack(alignment: .leading) {
HStack(alignment: .center) {
EventProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true, size: .normal)
Text("\(format_relative_time(event.created_at))")
.foregroundColor(.gray)
Spacer()
}
EventBody(damus_state: damus, event: event, size: .normal)
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
BuilderEventView(damus: damus, event_id: mention.ref.id)
}
if has_action_bar {
Rectangle().frame(height: 2).opacity(0)
let bar = make_actionbar_model(ev: event, damus: damus)
EventActionBar(damus_state: damus, event: event, bar: bar)
.padding([.top], 4)
}
}
.padding([.leading], 2)
}
.contentShape(Rectangle())
.background(event_validity_color(event.validity))
.id(event.id)
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
.padding([.bottom], 2)
.event_context_menu(event, keypair: damus.keypair, target_pubkey: pubkey)
}
}
struct TextEvent_Previews: PreviewProvider {
static var previews: some View {
TextEvent(damus: test_damus_state(), event: test_event, pubkey: "pk", has_action_bar: true, booster_pubkey: nil)
}
}
-33
View File
@@ -1,33 +0,0 @@
//
// ZapEvent.swift
// damus
//
// Created by William Casarin on 2023-02-03.
//
import SwiftUI
struct ZapEvent: View {
let damus: DamusState
let zap: Zap
var body: some View {
VStack(alignment: .leading) {
Text("⚡️ \(format_msats(zap.invoice.amount))", comment: "Text indicating the zap amount. i.e. number of satoshis that were tipped to a user")
.font(.headline)
.padding([.top], 2)
TextEvent(damus: damus, event: zap.request.ev, pubkey: zap.request.ev.pubkey, has_action_bar: false, booster_pubkey: nil)
.padding([.top], 1)
}
}
}
/*
struct ZapEvent_Previews: PreviewProvider {
static var previews: some View {
ZapEvent()
}
}
*/
+5 -9
View File
@@ -12,14 +12,13 @@ struct FollowButtonView: View {
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
let target: FollowTarget let target: FollowTarget
let follows_you: Bool
@State var follow_state: FollowState @State var follow_state: FollowState
var body: some View { var body: some View {
Button { Button {
follow_state = perform_follow_btn_action(follow_state, target: target) follow_state = perform_follow_btn_action(follow_state, target: target)
} label: { } label: {
Text(follow_btn_txt(follow_state, follows_you: follows_you)) Text(follow_btn_txt(follow_state))
.frame(width: 105, height: 30) .frame(width: 105, height: 30)
//.padding(.vertical, 10) //.padding(.vertical, 10)
.font(.caption.weight(.bold)) .font(.caption.weight(.bold))
@@ -71,19 +70,16 @@ struct FollowButtonPreviews: View {
var body: some View { var body: some View {
VStack { VStack {
Text("Unfollows", comment: "Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.") Text("Unfollows", comment: "Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.")
FollowButtonView(target: target, follows_you: false, follow_state: .unfollows) FollowButtonView(target: target, follow_state: .unfollows)
Text("Following", comment: "Text to indicate that the button next to it is in a state that indicates that it is in the process of following a profile.") Text("Following", comment: "Text to indicate that the button next to it is in a state that indicates that it is in the process of following a profile.")
FollowButtonView(target: target, follows_you: false, follow_state: .following) FollowButtonView(target: target, follow_state: .following)
Text("Follows", comment: "Text to indicate that button next to it is in a state that will follow a profile when tapped.")
FollowButtonView(target: target, follows_you: false, follow_state: .follows)
Text("Follows", comment: "Text to indicate that button next to it is in a state that will follow a profile when tapped.") Text("Follows", comment: "Text to indicate that button next to it is in a state that will follow a profile when tapped.")
FollowButtonView(target: target, follows_you: true, follow_state: .follows) FollowButtonView(target: target, follow_state: .follows)
Text("Unfollowing", comment: "Text to indicate that the button next to it is in a state that indicates that it is in the process of unfollowing a profile.") Text("Unfollowing", comment: "Text to indicate that the button next to it is in a state that indicates that it is in the process of unfollowing a profile.")
FollowButtonView(target: target, follows_you: false, follow_state: .unfollowing) FollowButtonView(target: target, follow_state: .unfollowing)
} }
} }
} }
+1 -1
View File
@@ -17,7 +17,7 @@ struct FollowUserView: View {
HStack { HStack {
UserView(damus_state: damus_state, pubkey: target.pubkey) UserView(damus_state: damus_state, pubkey: target.pubkey)
FollowButtonView(target: target, follows_you: false, follow_state: damus_state.contacts.follow_state(target.pubkey)) FollowButtonView(target: target, follow_state: damus_state.contacts.follow_state(target.pubkey))
} }
} }
} }
+63 -80
View File
@@ -7,18 +7,67 @@
import SwiftUI import SwiftUI
import LinkPresentation import LinkPresentation
import NaturalLanguage
struct NoteArtifacts {
let content: AttributedString
let images: [URL]
let invoices: [Invoice]
let links: [URL]
static func just_content(_ content: String) -> NoteArtifacts {
NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
}
}
func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts {
let blocks = ev.blocks(privkey)
var invoices: [Invoice] = []
var img_urls: [URL] = []
var link_urls: [URL] = []
let txt: AttributedString = blocks.reduce("") { str, block in
switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
case .text(let txt):
return str + AttributedString(stringLiteral: txt)
case .hashtag(let htag):
return str + hashtag_str(htag)
case .invoice(let invoice):
invoices.append(invoice)
return str
case .url(let url):
// Handle Image URLs
if is_image_url(url) {
// Append Image
img_urls.append(url)
return str
} else {
link_urls.append(url)
return str + url_str(url)
}
}
}
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
}
func is_image_url(_ url: URL) -> Bool {
let str = url.lastPathComponent.lowercased()
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
}
struct NoteContentView: View { struct NoteContentView: View {
let damus_state: DamusState let privkey: String?
let event: NostrEvent let event: NostrEvent
let profiles: Profiles
let previews: PreviewCache
let show_images: Bool let show_images: Bool
@State var artifacts: NoteArtifacts @State var artifacts: NoteArtifacts
let size: EventViewKind
@State var preview: LinkViewRepresentable? = nil @State var preview: LinkViewRepresentable? = nil
let size: EventViewKind
func MainContent() -> some View { func MainContent() -> some View {
return VStack(alignment: .leading) { return VStack(alignment: .leading) {
@@ -26,10 +75,6 @@ struct NoteContentView: View {
.font(eventviewsize_to_font(size)) .font(eventviewsize_to_font(size))
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
if size == .selected {
TranslateView(damus_state: damus_state, event: event, size: size)
}
if show_images && artifacts.images.count > 0 { if show_images && artifacts.images.count > 0 {
ImageCarousel(urls: artifacts.images) ImageCarousel(urls: artifacts.images)
} else if !show_images && artifacts.images.count > 0 { } else if !show_images && artifacts.images.count > 0 {
@@ -42,7 +87,7 @@ struct NoteContentView: View {
.cornerRadius(10) .cornerRadius(10)
} }
if artifacts.invoices.count > 0 { if artifacts.invoices.count > 0 {
InvoicesView(our_pubkey: damus_state.keypair.pubkey, invoices: artifacts.invoices) InvoicesView(invoices: artifacts.invoices)
} }
if let preview = self.preview, show_images { if let preview = self.preview, show_images {
@@ -61,16 +106,16 @@ struct NoteContentView: View {
var body: some View { var body: some View {
MainContent() MainContent()
.onAppear() { .onAppear() {
self.artifacts = render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey) self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
} }
.onReceive(handle_notify(.profile_updated)) { notif in .onReceive(handle_notify(.profile_updated)) { notif in
let profile = notif.object as! ProfileUpdate let profile = notif.object as! ProfileUpdate
let blocks = event.blocks(damus_state.keypair.privkey) let blocks = event.blocks(privkey)
for block in blocks { for block in blocks {
switch block { switch block {
case .mention(let m): case .mention(let m):
if m.type == .pubkey && m.ref.ref_id == profile.pubkey { if m.type == .pubkey && m.ref.ref_id == profile.pubkey {
self.artifacts = render_note_content(ev: event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey) self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
} }
case .text: return case .text: return
case .hashtag: return case .hashtag: return
@@ -80,7 +125,7 @@ struct NoteContentView: View {
} }
} }
.task { .task {
if let preview = damus_state.previews.lookup(self.event.id) { if let preview = previews.lookup(self.event.id) {
switch preview { switch preview {
case .value(let view): case .value(let view):
self.preview = view self.preview = view
@@ -94,13 +139,13 @@ struct NoteContentView: View {
let meta = await getMetaData(for: artifacts.links.first!) let meta = await getMetaData(for: artifacts.links.first!)
let view = meta.map { LinkViewRepresentable(meta: .linkmeta($0)) } let view = meta.map { LinkViewRepresentable(meta: .linkmeta($0)) }
damus_state.previews.store(evid: self.event.id, preview: view) previews.store(evid: self.event.id, preview: view)
self.preview = view self.preview = view
} }
} }
} }
func getMetaData(for url: URL) async -> LPLinkMetadata? { func getMetaData(for url: URL) async -> LPLinkMetadata? {
// iOS 15 is crashing for some reason // iOS 15 is crashing for some reason
guard #available(iOS 16, *) else { guard #available(iOS 16, *) else {
@@ -150,74 +195,12 @@ func mention_str(_ m: Mention, profiles: Profiles) -> AttributedString {
} }
} }
struct NoteContentView_Previews: PreviewProvider { struct NoteContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let state = test_damus_state() let state = test_damus_state()
let content = "hi there ¯\\_(ツ)_/¯ https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg" let content = "hi there ¯\\_(ツ)_/¯ https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
let artifacts = NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: []) let artifacts = NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
NoteContentView(damus_state: state, event: NostrEvent(content: content, pubkey: "pk"), show_images: true, artifacts: artifacts, size: .normal) NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, previews: PreviewCache(), show_images: true, artifacts: artifacts, size: .normal)
} }
} }
extension View {
func translate_button_style() -> some View {
return self
.font(.footnote)
.contentShape(Rectangle())
.padding([.top, .bottom], 10)
}
}
struct NoteArtifacts {
let content: AttributedString
let images: [URL]
let invoices: [Invoice]
let links: [URL]
static func just_content(_ content: String) -> NoteArtifacts {
NoteArtifacts(content: AttributedString(stringLiteral: content), images: [], invoices: [], links: [])
}
}
func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -> NoteArtifacts {
let blocks = ev.blocks(privkey)
return render_blocks(blocks: blocks, profiles: profiles, privkey: privkey)
}
func render_blocks(blocks: [Block], profiles: Profiles, privkey: String?) -> NoteArtifacts {
var invoices: [Invoice] = []
var img_urls: [URL] = []
var link_urls: [URL] = []
let txt: AttributedString = blocks.reduce("") { str, block in
switch block {
case .mention(let m):
return str + mention_str(m, profiles: profiles)
case .text(let txt):
return str + AttributedString(stringLiteral: txt)
case .hashtag(let htag):
return str + hashtag_str(htag)
case .invoice(let invoice):
invoices.append(invoice)
return str
case .url(let url):
// Handle Image URLs
if is_image_url(url) {
// Append Image
img_urls.append(url)
return str
} else {
link_urls.append(url)
return str + url_str(url)
}
}
}
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
}
func is_image_url(_ url: URL) -> Bool {
let str = url.lastPathComponent.lowercased()
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
}
+2 -1
View File
@@ -34,7 +34,8 @@ func PostButton(action: @escaping () -> ()) -> some View {
.keyboardShortcut("n", modifiers: [.command, .shift]) .keyboardShortcut("n", modifiers: [.command, .shift])
} }
func PostButtonContainer(is_left_handed: Bool, action: @escaping () -> Void) -> some View { func PostButtonContainer(userSettings: UserSettingsStore, action: @escaping () -> Void) -> some View {
let is_left_handed = userSettings.left_handed.self
return VStack { return VStack {
Spacer() Spacer()
+1 -14
View File
@@ -17,7 +17,6 @@ let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Tex
struct PostView: View { struct PostView: View {
@State var post: String = "" @State var post: String = ""
@FocusState var focus: Bool @FocusState var focus: Bool
@State var showPrivateKeyWarning: Bool = false
let replying_to: NostrEvent? let replying_to: NostrEvent?
let references: [ReferencedId] let references: [ReferencedId]
@@ -66,11 +65,7 @@ struct PostView: View {
if !is_post_empty { if !is_post_empty {
Button(NSLocalizedString("Post", comment: "Button to post a note.")) { Button(NSLocalizedString("Post", comment: "Button to post a note.")) {
showPrivateKeyWarning = contentContainsPrivateKey(self.post) self.send_post()
if !showPrivateKeyWarning {
self.send_post()
}
} }
} }
} }
@@ -104,14 +99,6 @@ struct PostView: View {
} }
} }
.padding() .padding()
.alert(NSLocalizedString("Note contains \"nsec1\" private key. Are you sure?", comment: "Alert user that they might be attempting to paste a private key and ask them to confirm."), isPresented: $showPrivateKeyWarning, actions: {
Button(NSLocalizedString("No", comment: "Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key."), role: .cancel) {
showPrivateKeyWarning = false
}
Button(NSLocalizedString("Yes, Post with Private Key", comment: "Button to proceed with posting a note even though it looks like they might be posting a private key."), role: .destructive) {
self.send_post()
}
})
} }
} }
+2 -4
View File
@@ -57,10 +57,8 @@ struct UserSearch_Previews: PreviewProvider {
} }
func search_users(profiles: Profiles, tags: [[String]], search _search: String) -> [SearchedUser] { func search_users(profiles: Profiles, tags: [[String]], search: String) -> [SearchedUser] {
var seen_user = Set<String>() var seen_user = Set<String>()
let search = _search.lowercased()
return tags.reduce(into: Array<SearchedUser>()) { arr, tag in return tags.reduce(into: Array<SearchedUser>()) { arr, tag in
guard tag.count >= 2 && tag[0] == "p" else { guard tag.count >= 2 && tag[0] == "p" else {
return return
@@ -79,7 +77,7 @@ func search_users(profiles: Profiles, tags: [[String]], search _search: String)
let profile = profiles.lookup(id: pubkey) let profile = profiles.lookup(id: pubkey)
guard ((petname?.lowercased().hasPrefix(search) ?? false) || (profile?.name?.lowercased().hasPrefix(search) ?? false)) else { guard ((petname?.hasPrefix(search) ?? false) || (profile?.name?.hasPrefix(search) ?? false)) else {
return return
} }
-34
View File
@@ -1,34 +0,0 @@
//
// FollowsYou.swift
// damus
//
// Created by William Casarin on 2023-02-07.
//
import SwiftUI
struct FollowsYou: View {
@Environment(\.colorScheme) var colorScheme
var fill_color: Color {
colorScheme == .light ? Color("DamusLightGrey") : Color("DamusDarkGrey")
}
var body: some View {
Text("Follows you", comment: "Text to indicate that a user is following your profile.")
.padding([.leading, .trailing], 6.0)
.padding([.top, .bottom], 2.0)
.foregroundColor(.gray)
.background {
RoundedRectangle(cornerRadius: 5.0)
.foregroundColor(fill_color)
}
.font(.footnote)
}
}
struct FollowsYou_Previews: PreviewProvider {
static var previews: some View {
FollowsYou()
}
}
-61
View File
@@ -1,61 +0,0 @@
//
// ProfileNameView.swift
// damus
//
// Created by William Casarin on 2023-02-07.
//
import SwiftUI
struct ProfileNameView: View {
let pubkey: String
let profile: Profile?
let follows_you: Bool
let damus: DamusState
var spacing: CGFloat { 10.0 }
var body: some View {
Group {
if let real_name = profile?.display_name {
VStack(alignment: .leading) {
Text(real_name)
.font(.title3.weight(.bold))
HStack(alignment: .center, spacing: spacing) {
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", damus: damus, show_friend_confirmed: true)
.font(.callout)
.foregroundColor(.gray)
if follows_you {
FollowsYou()
}
}
KeyView(pubkey: pubkey)
.pubkey_context_menu(bech32_pubkey: pubkey)
}
} else {
VStack(alignment: .leading) {
HStack(alignment: .center, spacing: spacing) {
ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true)
.font(.title3.weight(.bold))
if follows_you {
FollowsYou()
}
}
KeyView(pubkey: pubkey)
.pubkey_context_menu(bech32_pubkey: pubkey)
}
}
}
}
}
struct ProfileNameView_Previews: PreviewProvider {
static var previews: some View {
VStack {
ProfileNameView(pubkey: test_event.pubkey, profile: nil, follows_you: true, damus: test_damus_state())
ProfileNameView(pubkey: test_event.pubkey, profile: nil, follows_you: false, damus: test_damus_state())
}
}
}
+103 -102
View File
@@ -19,7 +19,7 @@ enum FollowState {
case unfollows case unfollows
} }
func follow_btn_txt(_ fs: FollowState, follows_you: Bool) -> String { func follow_btn_txt(_ fs: FollowState) -> String {
switch fs { switch fs {
case .follows: case .follows:
return NSLocalizedString("Unfollow", comment: "Button to unfollow a user.") return NSLocalizedString("Unfollow", comment: "Button to unfollow a user.")
@@ -28,11 +28,7 @@ func follow_btn_txt(_ fs: FollowState, follows_you: Bool) -> String {
case .unfollowing: case .unfollowing:
return NSLocalizedString("Unfollowing...", comment: "Label to indicate that the user is in the process of unfollowing another user.") return NSLocalizedString("Unfollowing...", comment: "Label to indicate that the user is in the process of unfollowing another user.")
case .unfollows: case .unfollows:
if follows_you { return NSLocalizedString("Follow", comment: "Button to follow a user.")
return NSLocalizedString("Follow Back", comment: "Button to follow a user back.")
} else {
return NSLocalizedString("Follow", comment: "Button to follow a user.")
}
} }
} }
@@ -49,6 +45,35 @@ func follow_btn_enabled_state(_ fs: FollowState) -> Bool {
} }
} }
struct ProfileNameView: View {
let pubkey: String
let profile: Profile?
let damus: DamusState
var body: some View {
Group {
if let real_name = profile?.display_name {
VStack(alignment: .leading) {
Text(real_name)
.font(.title3.weight(.bold))
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", damus: damus, show_friend_confirmed: true)
.font(.callout)
.foregroundColor(.gray)
KeyView(pubkey: pubkey)
.pubkey_context_menu(bech32_pubkey: pubkey)
}
} else {
VStack(alignment: .leading) {
ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true)
.font(.title3.weight(.bold))
KeyView(pubkey: pubkey)
.pubkey_context_menu(bech32_pubkey: pubkey)
}
}
}
}
}
struct EditButton: View { struct EditButton: View {
let damus_state: DamusState let damus_state: DamusState
@@ -66,8 +91,6 @@ struct EditButton: View {
RoundedRectangle(cornerRadius: 24) RoundedRectangle(cornerRadius: 24)
.stroke(borderColor(), lineWidth: 1) .stroke(borderColor(), lineWidth: 1)
} }
.minimumScaleFactor(0.5)
.lineLimit(1)
} }
} }
@@ -92,6 +115,7 @@ struct ProfileView: View {
@State var is_zoomed: Bool = false @State var is_zoomed: Bool = false
@State var show_share_sheet: Bool = false @State var show_share_sheet: Bool = false
@State var action_sheet_presented: Bool = false @State var action_sheet_presented: Bool = false
@StateObject var user_settings = UserSettingsStore()
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@@ -117,10 +141,10 @@ struct ProfileView: View {
func LNButton(lnurl: String, profile: Profile) -> some View { func LNButton(lnurl: String, profile: Profile) -> some View {
Button(action: { Button(action: {
if damus_state.settings.show_wallet_selector { if user_settings.show_wallet_selector {
showing_select_wallet = true showing_select_wallet = true
} else { } else {
open_with_wallet(wallet: damus_state.settings.default_wallet.model, invoice: lnurl) open_with_wallet(wallet: user_settings.default_wallet.model, invoice: lnurl)
} }
}) { }) {
Image(systemName: "bolt.circle") Image(systemName: "bolt.circle")
@@ -136,7 +160,8 @@ struct ProfileView: View {
} }
.cornerRadius(24) .cornerRadius(24)
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) { .sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
SelectWalletView(showingSelectWallet: $showing_select_wallet, our_pubkey: damus_state.pubkey, invoice: lnurl) SelectWalletView(showingSelectWallet: $showing_select_wallet, invoice: lnurl)
.environmentObject(user_settings)
} }
} }
@@ -149,24 +174,6 @@ struct ProfileView: View {
Image(systemName: "ellipsis.circle") Image(systemName: "ellipsis.circle")
.profile_button_style(scheme: colorScheme) .profile_button_style(scheme: colorScheme)
} }
.confirmationDialog(NSLocalizedString("Actions", comment: "Title for confirmation dialog to either share, report, or block a profile."), isPresented: $action_sheet_presented) {
Button(NSLocalizedString("Share", comment: "Button to share the link to a profile.")) {
show_share_sheet = true
}
// Only allow reporting if logged in with private key and the currently viewed profile is not the logged in profile.
if profile.pubkey != damus_state.pubkey && damus_state.is_privkey_user {
Button(NSLocalizedString("Report", comment: "Button to report a profile."), role: .destructive) {
let target: ReportTarget = .user(profile.pubkey)
notify(.report, target)
}
Button(NSLocalizedString("Block", comment: "Button to block a profile."), role: .destructive) {
notify(.block, profile.pubkey)
}
}
}
} }
var ShareButton: some View { var ShareButton: some View {
@@ -214,61 +221,6 @@ struct ProfileView: View {
return 0 return 0
} }
func ActionSection(profile_data: Profile?) -> some View {
return Group {
ActionSheetButton
if let profile = profile_data {
if let lnurl = profile.lnurl, lnurl != "" {
LNButton(lnurl: lnurl, profile: profile)
}
}
DMButton
if profile.pubkey != damus_state.pubkey {
FollowButtonView(
target: profile.get_follow_target(),
follows_you: profile.follows(pubkey: damus_state.pubkey),
follow_state: damus_state.contacts.follow_state(profile.pubkey)
)
} else if damus_state.keypair.privkey != nil {
NavigationLink(destination: EditMetadataView(damus_state: damus_state)) {
EditButton(damus_state: damus_state)
}
}
}
}
func NameSection(profile_data: Profile?) -> some View {
return Group {
HStack(alignment: .center) {
ProfilePicView(pubkey: profile.pubkey, size: pfp_size, highlight: .custom(imageBorderColor(), 4.0), profiles: damus_state.profiles)
.onTapGesture {
is_zoomed.toggle()
}
.fullScreenCover(isPresented: $is_zoomed) {
ProfileZoomView(pubkey: profile.pubkey, profiles: damus_state.profiles) }
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
Spacer()
ActionSection(profile_data: profile_data)
.offset(y: -15.0) // Increase if set a frame
}
let follows_you = profile.follows(pubkey: damus_state.pubkey)
ProfileNameView(pubkey: profile.pubkey, profile: profile_data, follows_you: follows_you, damus: damus_state)
//.padding(.bottom)
.padding(.top,-(pfp_size/2.0))
}
}
var pfp_size: CGFloat {
return 90.0
}
var TopSection: some View { var TopSection: some View {
ZStack(alignment: .top) { ZStack(alignment: .top) {
GeometryReader { geometry in GeometryReader { geometry in
@@ -281,14 +233,55 @@ struct ProfileView: View {
}.frame(height: BANNER_HEIGHT) }.frame(height: BANNER_HEIGHT)
VStack(alignment: .leading, spacing: 8.0) { VStack(alignment: .leading, spacing: 8.0) {
let profile_data = damus_state.profiles.lookup(id: profile.pubkey) let data = damus_state.profiles.lookup(id: profile.pubkey)
let pfp_size: CGFloat = 90.0
NameSection(profile_data: profile_data) HStack(alignment: .center) {
ProfilePicView(pubkey: profile.pubkey, size: pfp_size, highlight: .custom(imageBorderColor(), 4.0), profiles: damus_state.profiles)
.onTapGesture {
is_zoomed.toggle()
}
.fullScreenCover(isPresented: $is_zoomed) {
ProfileZoomView(pubkey: profile.pubkey, profiles: damus_state.profiles) }
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
Spacer()
Group {
ActionSheetButton
if let profile = data {
if let lnurl = profile.lnurl, lnurl != "" {
LNButton(lnurl: lnurl, profile: profile)
}
}
DMButton
if profile.pubkey != damus_state.pubkey {
FollowButtonView(
target: profile.get_follow_target(),
follow_state: damus_state.contacts.follow_state(profile.pubkey)
)
} else if damus_state.keypair.privkey != nil {
NavigationLink(destination: EditMetadataView(damus_state: damus_state)) {
EditButton(damus_state: damus_state)
}
}
}
.offset(y: -15.0) // Increase if set a frame
}
ProfileNameView(pubkey: profile.pubkey, profile: data, damus: damus_state)
//.padding(.bottom)
.padding(.top,-(pfp_size/2.0))
Text(ProfileView.markdown.process(profile_data?.about ?? "")) Text(ProfileView.markdown.process(data?.about ?? ""))
.font(.subheadline).textSelection(.enabled) .font(.subheadline)
if let url = profile_data?.website_url { if let url = data?.website_url {
WebsiteLink(url: url) WebsiteLink(url: url)
} }
@@ -322,19 +315,10 @@ struct ProfileView: View {
} }
if let relays = profile.relays { if let relays = profile.relays {
// Only open relay config view if the user is logged in with private key and they are looking at their own profile. NavigationLink(destination: UserRelaysView(state: damus_state, pubkey: profile.pubkey, relays: Array(relays.keys).sorted())) {
let relay_text = Text("\(Text("\(relays.keys.count)", comment: "Number of relay servers a user is connected.").font(.subheadline.weight(.medium))) \(Text(String(format: NSLocalizedString("relays_count", comment: "Part of a larger sentence to describe how many relay servers a user is connected."), relays.keys.count)).font(.subheadline).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.") Text("\(Text("\(relays.keys.count)", comment: "Number of relay servers a user is connected.").font(.subheadline.weight(.medium))) \(Text(String(format: NSLocalizedString("relays_count", comment: "Part of a larger sentence to describe how many relay servers a user is connected."), relays.keys.count)).font(.subheadline).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.")
if profile.pubkey == damus_state.pubkey && damus_state.is_privkey_user {
NavigationLink(destination: RelayConfigView(state: damus_state)) {
relay_text
}
.buttonStyle(PlainButtonStyle())
} else {
NavigationLink(destination: UserRelaysView(state: damus_state, pubkey: profile.pubkey, relays: Array(relays.keys).sorted())) {
relay_text
}
.buttonStyle(PlainButtonStyle())
} }
.buttonStyle(PlainButtonStyle())
} }
} }
} }
@@ -390,6 +374,23 @@ struct ProfileView: View {
} }
} }
} }
.confirmationDialog(NSLocalizedString("Actions", comment: "Title for confirmation dialog to either share, report, or block a profile."), isPresented: $action_sheet_presented) {
Button(NSLocalizedString("Share", comment: "Button to share the link to a profile.")) {
show_share_sheet = true
}
// Only allow reporting if logged in with private key and the currently viewed profile is not the logged in profile.
if profile.pubkey != damus_state.pubkey && damus_state.is_privkey_user {
Button(NSLocalizedString("Report", comment: "Button to report a profile."), role: .destructive) {
let target: ReportTarget = .user(profile.pubkey)
notify(.report, target)
}
Button(NSLocalizedString("Block", comment: "Button to block a profile."), role: .destructive) {
notify(.block, profile.pubkey)
}
}
}
.ignoresSafeArea() .ignoresSafeArea()
} }
} }
@@ -406,7 +407,7 @@ struct ProfileView_Previews: PreviewProvider {
func test_damus_state() -> DamusState { func test_damus_state() -> DamusState {
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681" let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
let damus = DamusState.empty let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(our_pubkey: pubkey), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: pubkey), previews: PreviewCache())
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io") let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io")
let tsprof = TimestampedProfile(profile: prof, timestamp: 0) let tsprof = TimestampedProfile(profile: prof, timestamp: 0)
+16 -50
View File
@@ -23,44 +23,19 @@ struct QRCodeView: View {
} }
var body: some View { var body: some View {
ZStack(alignment: .center) { ZStack(alignment: .topLeading) {
DamusGradient()
ZStack(alignment: .topLeading) { Button {
DamusGradient() presentationMode.wrappedValue.dismiss()
Button { } label: {
presentationMode.wrappedValue.dismiss() Image(systemName: "xmark")
} label: { .foregroundColor(.white)
Image(systemName: "xmark") .font(.subheadline)
.foregroundColor(.white) .padding(.leading, 20)
.font(.subheadline)
.padding(.leading, 20)
}
.zIndex(1)
} }
.zIndex(1)
VStack(alignment: .center) { VStack(alignment: .center) {
let profile = damus_state.profiles.lookup(id: damus_state.pubkey)
if (damus_state.profiles.lookup(id: damus_state.pubkey)?.picture) != nil {
ProfilePicView(pubkey: damus_state.pubkey, size: 90.0, highlight: .custom(Color("DamusWhite"), 4.0), profiles: damus_state.profiles)
.padding(.top, 50)
} else {
Image(systemName: "person.fill")
.font(.system(size: 60))
.foregroundColor(Color("DamusWhite"))
.padding(.top, 50)
}
if let display_name = profile?.display_name {
Text(display_name)
.foregroundColor(Color("DamusWhite"))
.font(.system(size: 24, weight: .heavy))
}
if let name = profile?.name {
Text("@" + name)
.foregroundColor(Color("DamusWhite"))
.font(.body)
}
Spacer() Spacer()
@@ -71,24 +46,15 @@ struct QRCodeView: View {
.scaledToFit() .scaledToFit()
.frame(width: 200, height: 200) .frame(width: 200, height: 200)
.padding() .padding()
.cornerRadius(10)
.overlay(RoundedRectangle(cornerRadius: 10) Text(key)
.stroke(Color("DamusWhite"), lineWidth: 1)) .font(.headline)
.shadow(radius: 10) .foregroundColor(Color(.white))
.padding()
} }
Spacer() Spacer()
Text("Follow me on nostr", comment: "Text on QR code view to prompt viewer looking at screen to follow the user.")
.foregroundColor(Color("DamusWhite"))
.font(.system(size: 24, weight: .heavy))
.padding(.top)
Text("Scan the code", comment: "Text on QR code view to prompt viewer to scan the QR code on screen with their device camera.")
.foregroundColor(Color("DamusWhite"))
.font(.system(size: 18, weight: .ultraLight))
Spacer()
} }
} }
@@ -34,11 +34,7 @@ struct RelayView: View {
Circle() Circle()
.frame(width: 8.0, height: 8.0) .frame(width: 8.0, height: 8.0)
.foregroundColor(conn_color) .foregroundColor(conn_color)
NavigationLink { Text(relay)
RelayDetailView(state: state, relay: relay, conn_color: conn_color)
} label: {
Text(relay)
}
} }
.onReceive(timer) { _ in .onReceive(timer) { _ in
update_connection_color() update_connection_color()
-109
View File
@@ -1,109 +0,0 @@
//
// RelayConfigView.swift
// damus
//
// Created by William Casarin on 2023-01-30.
//
import SwiftUI
struct RelayConfigView: View {
let state: DamusState
@State var new_relay: String = ""
@State var show_add_relay: Bool = false
@State var relays: [RelayDescriptor]
init(state: DamusState) {
self.state = state
_relays = State(initialValue: state.pool.descriptors)
}
var recommended: [RelayDescriptor] {
let rs: [RelayDescriptor] = []
return BOOTSTRAP_RELAYS.reduce(into: rs) { (xs, x) in
if let _ = state.pool.get_relay(x) {
} else {
xs.append(RelayDescriptor(url: URL(string: x)!, info: .rw))
}
}
}
var body: some View {
MainContent
.onReceive(handle_notify(.relays_changed)) { _ in
self.relays = state.pool.descriptors
}
.sheet(isPresented: $show_add_relay) {
AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in
guard var relay = m_relay else {
return
}
if relay.starts(with: "wss://") == false && relay.starts(with: "ws://") == false {
relay = "wss://" + relay
}
guard let url = URL(string: relay) else {
return
}
guard let ev = state.contacts.event else {
return
}
guard let privkey = state.keypair.privkey else {
return
}
let info = RelayInfo.rw
guard (try? state.pool.add_relay(url, info: info)) != nil else {
return
}
state.pool.connect(to: [relay])
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: info) else {
return
}
process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: ev)
state.pool.send(.event(new_ev))
}
}
}
var MainContent: some View {
Form {
Section {
List(Array(relays), id: \.url) { relay in
RelayView(state: state, relay: relay.url.absoluteString)
}
} header: {
HStack {
Text("Relays", comment: "Header text for relay server list for configuration.")
Spacer()
Button(action: { show_add_relay = true }) {
Image(systemName: "plus")
.foregroundColor(.accentColor)
}
}
}
if recommended.count > 0 {
Section(NSLocalizedString("Recommended Relays", comment: "Section title for recommend relay servers that could be added as part of configuration")) {
List(recommended, id: \.url) { r in
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
}
}
}
}
}
}
struct RelayConfigView_Previews: PreviewProvider {
static var previews: some View {
RelayConfigView(state: test_damus_state())
}
}
-129
View File
@@ -1,129 +0,0 @@
//
// RelayDetailView.swift
// damus
//
// Created by Joel Klabo on 2/1/23.
//
import SwiftUI
struct RelayDetailView: View {
let state: DamusState
let relay: String
@State private var errorString: String?
@State private var nip11: RelayMetadata?
@State var conn_color: Color
@Environment(\.dismiss) var dismiss
func FieldText(_ str: String?) -> some View {
Text(str ?? "No data available")
}
var body: some View {
Group {
if let nip11 {
Form {
if let pubkey = nip11.pubkey {
Section(NSLocalizedString("Admin", comment: "Label to display relay contact user.")) {
UserView(damus_state: state, pubkey: pubkey)
}
}
Section(NSLocalizedString("Relay", comment: "Label to display relay address.")) {
HStack {
Text(relay)
Spacer()
Circle()
.frame(width: 8.0, height: 8.0)
.foregroundColor(conn_color)
}
}
Section(NSLocalizedString("Description", comment: "Label to display relay description.")) {
FieldText(nip11.description)
}
Section(NSLocalizedString("Contact", comment: "Label to display relay contact information.")) {
FieldText(nip11.contact)
}
Section(NSLocalizedString("Software", comment: "Label to display relay software.")) {
FieldText(nip11.software)
}
Section(NSLocalizedString("Version", comment: "Label to display relay software version.")) {
FieldText(nip11.version)
}
if let nips = nip11.supported_nips, nips.count > 0 {
Section(NSLocalizedString("Supported NIPs", comment: "Label to display relay's supported NIPs.")) {
Text(nipsList(nips: nips))
}
}
}
} else if let errorString {
Text(errorString)
.foregroundColor(.red)
.font(.caption)
} else {
ProgressView()
}
}
.onReceive(handle_notify(.switched_timeline)) { notif in
dismiss()
}
.navigationTitle(nip11?.name ?? "")
.task {
var urlString = relay.replacingOccurrences(of: "wss://", with: "https://")
urlString = urlString.replacingOccurrences(of: "ws://", with: "http://")
guard let url = URL(string: urlString) else {
return
}
var request = URLRequest(url: url)
request.setValue("application/nostr+json", forHTTPHeaderField: "Accept")
var res: (Data, URLResponse)? = nil
do {
res = try await URLSession.shared.data(for: request)
} catch {
errorString = error.localizedDescription
return
}
guard let data = res?.0 else {
errorString = "Relay not responding to metadata request"
return
}
do {
let nip11 = try JSONDecoder().decode(RelayMetadata.self, from: data)
self.nip11 = nip11
} catch {
errorString = error.localizedDescription
}
}
}
private func nipsList(nips: [Int]) -> AttributedString {
var attrString = AttributedString()
let lastNipIndex = nips.count - 1
for (index, nip) in nips.enumerated() {
if let link = NIPURLBuilder.url(forNIP: nip) {
let nipString = NIPURLBuilder.formatNipNumber(nip: nip)
var nipAttrString = AttributedString(stringLiteral: nipString)
nipAttrString.link = link
attrString = attrString + nipAttrString
if index < lastNipIndex {
attrString = attrString + AttributedString(stringLiteral: ", ")
}
}
}
return attrString
}
}
struct RelayDetailView_Previews: PreviewProvider {
static var previews: some View {
RelayDetailView(state: test_damus_state(), relay: "wss://nostr.klabo.blog", conn_color: .green)
}
}
+65
View File
@@ -0,0 +1,65 @@
//
// SwiftUIView.swift
// damus
//
// Created by William Casarin on 2022-04-19.
//
import SwiftUI
struct ReplyQuoteView: View {
let privkey: String?
let quoter: NostrEvent
let event_id: String
let profiles: Profiles
let previews: PreviewCache
@EnvironmentObject var thread: ThreadModel
func MainContent(event: NostrEvent) -> some View {
HStack(alignment: .top) {
Rectangle()
.frame(width: 2)
.padding([.leading], 4)
.foregroundColor(.accentColor)
VStack(alignment: .leading) {
HStack(alignment: .top) {
ProfilePicView(pubkey: event.pubkey, size: 16, highlight: .reply, profiles: profiles)
Text(Profile.displayName(profile: profiles.lookup(id: event.pubkey), pubkey: event.pubkey))
.foregroundColor(.accentColor)
Text("\(format_relative_time(event.created_at))", comment: "Amount of time that has passed since reply quote event occurred.")
.foregroundColor(.gray)
}
NoteContentView(privkey: privkey, event: event, profiles: profiles, previews: previews, show_images: false, artifacts: .just_content(event.content), size: .normal)
.font(.callout)
.foregroundColor(.accentColor)
//Spacer()
}
//.border(Color.red)
}
//.border(Color.green)
}
var body: some View {
Group {
if let event = thread.lookup(event_id) {
MainContent(event: event)
.padding(4)
.frame(maxWidth: .infinity, alignment: .leading)
.contentShape(Rectangle())
}
}
}
}
struct ReplyQuoteView_Previews: PreviewProvider {
static var previews: some View {
let s = test_damus_state()
let quoter = NostrEvent(content: "a\nb\nc", pubkey: "pubkey")
ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", profiles: s.profiles, previews: PreviewCache())
.environmentObject(ThreadModel(event: quoter, damus_state: s))
}
}
+1 -1
View File
@@ -35,7 +35,7 @@ struct SearchResultsView: View {
} }
} }
case .hashtag(let ht): case .hashtag(let ht):
let search_model = SearchModel(contacts: damus_state.contacts, pool: damus_state.pool, search: .filter_hashtag([ht])) let search_model = SearchModel(pool: damus_state.pool, search: .filter_hashtag([ht]))
let dst = SearchView(appstate: damus_state, search: search_model) let dst = SearchView(appstate: damus_state, search: search_model)
NavigationLink(destination: dst) { NavigationLink(destination: dst) {
Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.") Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.")
+1 -4
View File
@@ -25,9 +25,6 @@ struct SearchView: View {
.onDisappear() { .onDisappear() {
search.unsubscribe() search.unsubscribe()
} }
.onReceive(handle_notify(.new_mutes)) { notif in
search.filter_muted()
}
} }
} }
@@ -46,7 +43,7 @@ struct SearchView_Previews: PreviewProvider {
let filter = NostrFilter.filter_hashtag(["bitcoin"]) let filter = NostrFilter.filter_hashtag(["bitcoin"])
let pool = test_state.pool let pool = test_state.pool
let model = SearchModel(contacts: test_state.contacts, pool: pool, search: filter) let model = SearchModel(pool: pool, search: filter)
SearchView(appstate: test_state, search: model) SearchView(appstate: test_state, search: model)
} }
+3 -3
View File
@@ -9,10 +9,10 @@ import SwiftUI
struct SelectWalletView: View { struct SelectWalletView: View {
@Binding var showingSelectWallet: Bool @Binding var showingSelectWallet: Bool
let our_pubkey: String
let invoice: String let invoice: String
@Environment(\.openURL) private var openURL @Environment(\.openURL) private var openURL
@State var invoice_copied: Bool = false @State var invoice_copied: Bool = false
@EnvironmentObject var user_settings: UserSettingsStore
@State var allWalletModels: [Wallet.Model] = Wallet.allModels @State var allWalletModels: [Wallet.Model] = Wallet.allModels
let generator = UIImpactFeedbackGenerator(style: .light) let generator = UIImpactFeedbackGenerator(style: .light)
@@ -38,7 +38,7 @@ struct SelectWalletView: View {
Section(NSLocalizedString("Select a Lightning wallet", comment: "Title of section for selecting a Lightning wallet to pay a Lightning invoice.")) { Section(NSLocalizedString("Select a Lightning wallet", comment: "Title of section for selecting a Lightning wallet to pay a Lightning invoice.")) {
List{ List{
Button() { Button() {
let wallet_model = get_default_wallet(our_pubkey).model let wallet_model = user_settings.default_wallet.model
open_with_wallet(wallet: wallet_model, invoice: invoice) open_with_wallet(wallet: wallet_model, invoice: invoice)
} label: { } label: {
HStack { HStack {
@@ -73,6 +73,6 @@ struct SelectWalletView_Previews: PreviewProvider {
@State static var invoice: String = "" @State static var invoice: String = ""
static var previews: some View { static var previews: some View {
SelectWalletView(showingSelectWallet: $show, our_pubkey: "", invoice: "") SelectWalletView(showingSelectWallet: $show, invoice: "")
} }
} }
+2 -2
View File
@@ -42,10 +42,10 @@ struct SetupView: View {
DamusGradient() DamusGradient()
VStack(alignment: .center) { VStack(alignment: .center) {
NavigationLink(destination: EULAView(state: state), tag: .create_account, selection: $state ) { NavigationLink(destination: EULAView(), tag: .create_account, selection: $state ) {
EmptyView() EmptyView()
} }
NavigationLink(destination: EULAView(state: state), tag: .login, selection: $state ) { NavigationLink(destination: LoginView(), tag: .login, selection: $state ) {
EmptyView() EmptyView()
} }
+3 -7
View File
@@ -10,7 +10,9 @@ import SwiftUI
struct SideMenuView: View { struct SideMenuView: View {
let damus_state: DamusState let damus_state: DamusState
@Binding var isSidebarVisible: Bool @Binding var isSidebarVisible: Bool
@State var confirm_logout: Bool = false @State var confirm_logout: Bool = false
@StateObject var user_settings = UserSettingsStore()
@State private var showQRCode = false @State private var showQRCode = false
@@ -113,13 +115,7 @@ struct SideMenuView: View {
.foregroundColor(textColor()) .foregroundColor(textColor())
} }
NavigationLink(destination: RelayConfigView(state: damus_state)) { NavigationLink(destination: ConfigView(state: damus_state).environmentObject(user_settings)) {
Label(NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), systemImage: "network")
.font(.title2)
.foregroundColor(textColor())
}
NavigationLink(destination: ConfigView(state: damus_state)) {
Label(NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), systemImage: "gear") Label(NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), systemImage: "gear")
.font(.title2) .font(.title2)
.foregroundColor(textColor()) .foregroundColor(textColor())
-9
View File
@@ -1,9 +0,0 @@
/* Bundle display name */
"CFBundleDisplayName" = "دامُس";
/* Bundle name */
"CFBundleName" = "دامُس";
/* Privacy - Photo Library Additions Usage Description */
"NSPhotoLibraryAddUsageDescription" = "السماح لدامُس بالوصول إلى الصور يتيح لك حفظ الصور";
-654
View File
@@ -1,654 +0,0 @@
/* Blank space to separate profile picture from profile editor form. */
" " = "61b6edf1108e6f396680a33b02486a70_tr";
/* Description of how the nip05 identifier would be used for verification. */
"'%@' at '%@' will be used for verification" = "سيتم التحقق من '%@' @ '%@'";
/* Description of why the nip05 identifier is invalid. */
"'%@' is an invalid NIP-05 identifier. It should look like an email." = "'%@' عنوان NIP-05 غير صالح. من المفترض أن يشابه صيغة الايميل مثل المثال الموضح.";
/* Navigation bar title for view that shows who is following a user. */
"(Profile.displayName(profile: profile, pubkey: whos))'s Followers" = "متابعي (Profile.displayName(profile: profile, pubkey: whos))";
/* Navigation bar title for view that shows who a user is following. */
"(who) following" = "(who) يتابع";
/* Prefix character to username. */
"@" = "@";
/* Abbreviated version of a nostr public key. */
"%@" = "%@";
/* Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.
Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'. */
"%@ %@" = "%@ %@";
/* Alert message that informs a user was blocked. */
"%@ has been blocked" = "تم حظر %@";
/* Explanation of what is done to keep personally identifiable information private. There is a heading that precedes this explanation which is a variable to this string. */
"%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." = "انشاء حسابك لايتطلب رقم جوال أو بريد الكتروني أو معلومات شخصية. احصل على حسابك الخاص في ثواني.";
/* Explanation of what is done to keep private data encrypted. There is a heading that precedes this explanation which is a variable to this string. */
"%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs" = "محادثات خاصة مشفرة كليا. ";
/* Explanation of what can be done by users to earn money. There is a heading that precedes this explanation which is a variable to this string. */
"%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet." = "%@. بسهولة مطلقة، أرسل و استقبل برقيات البتكوين ⚡️عملة الانترنت العالمية.";
/* Number of zap payments on a post.
Number of relay servers a user is connected. */
"%lld" = "%lld";
/* Fraction of how many of the user's relay servers that are operational. */
"%lld/%lld" = "%lld/%lld";
/* Placeholder for event mention. */
"< e >" = "< e >";
/* Text indicating the zap amount. i.e. number of satoshis that were tipped to a user */
"⚡️ %@" = "⚡️ %@";
/* Label to prompt for about text entry for user to describe about themself. */
"About" = "النبذة التعريفية";
/* Label for About Me section of user profile form. */
"About Me" = "النبذة التعريفية";
/* Placeholder text for About Me description. */
"Absolute Boss" = "مدير كبير";
/* Button to accept the end user license agreement before being allowed into the app. */
"Accept" = "موافق";
/* Label to indicate the public ID of the account. */
"Account ID" = "معرف الحساب";
/* Title for confirmation dialog to either share, report, or block a profile. */
"Actions" = "خيارات";
/* Button to add recommended relay server.
Button to confirm adding user inputted relay. */
"Add" = "اضافة";
/* Button label to re-add all original participants as profiles to reply to in a note */
"Add all" = "اضافة الجميع";
/* Label for section for adding a relay server. */
"Add Relay" = "اضافة موصّل";
/* Any amount of sats */
"Any" = "أي شيء";
/* Prompt for optional entry of API Key to use translation server. */
"API Key (optional)" = "مفتاح API (اختياري)";
/* Alert message to ask if user wants to repost a post. */
"Are you sure you want to repost this?" = "هل أنت متأكد من اعادة النشر؟";
/* Label for Banner Image section of user profile form. */
"Banner Image" = "صورة الخلفية";
/* Reminder to user that they should save their account information. */
"Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus." = "قبل البدء، يجب عليك حفظ معلومات حسابك لتتمكن من الوصول إليه مستقبلا في حالة حذف دامُس أو تغيير جهازك.";
/* Dropdown option label for Lightning wallet, Bitcoin Beach. */
"Bitcoin Beach" = "Bitcoin Beach";
/* Label for Bitcoin Lightning Tips section of user profile form. */
"Bitcoin Lightning Tips" = "اكراميات البتكوين";
/* Dropdown option label for Lightning wallet, Blixt Wallet */
"Blixt Wallet" = "Blixt Wallet";
/* Alert button to block a user.
Button to block a profile.
Context menu option for blocking users. */
"Block" = "حظر";
/* Alert message prompt to ask if a user should be blocked. */
"Block %@?" = "حظر %@؟";
/* Title of alert for blocking a user. */
"Block User" = "حظر المستخدم";
/* Sidebar menu label for Profile view. */
"Blocked" = "قائمة الحظر";
/* Navigation title of view to see list of blocked users. */
"Blocked Users" = "المحظورون";
/* Dropdown option label for Lightning wallet, Blue Wallet. */
"Blue Wallet" = "Blue Wallet";
/* Dropdown option label for Lightning wallet, Breez. */
"Breez" = "Breez";
/* Context menu option for broadcasting the user's note to all of the user's connected relay servers. */
"Broadcast" = "بث";
/* Alert button to cancel out of alert for blocking a user.
Button to cancel out of alert that creates a new mutelist.
Button to cancel out of posting a note.
Button to cancel out of reposting a post.
Button to cancel out of view adding user inputted relay.
Cancel deleting the user.
Cancel out of logging out the user. */
"Cancel" = "الغاء";
/* Dropdown option label for Lightning wallet, Cash App. */
"Cash App" = "Cash App";
/* Navigation bar title for Chatroom view. */
"Chat" = "المحادثة";
/* Button for clearing cached data. */
"Clear" = "مسح";
/* Section title for clearing cached data. */
"Clear Cache" = "مسح البيانات المؤقتة";
/* Label indicating that a user's key was copied. */
"Copied" = "تم النسخ";
/* Button to copy a relay server address. */
"Copy" = "نسخ";
/* Context menu option for copying the ID of the account that created the note. */
"Copy Account ID" = "نسخ عنوان الحساب";
/* Context menu option to copy an image into clipboard.
Context menu option to copy an image to clipboard. */
"Copy Image" = "نسخ الصورة";
/* Context menu option to copy the URL of an image into clipboard. */
"Copy Image URL" = "نسخ رابط الصورة";
/* Title of section for copying a Lightning invoice identifier. */
"Copy invoice" = "نسخ البرقية";
/* Context menu option for copying a user's Lightning URL. */
"Copy LNURL" = "نسخ LNURL";
/* Context menu option for copying the ID of the note. */
"Copy Note ID" = "نسخ معرف المنشور";
/* Context menu option for copying the JSON text from the note. */
"Copy Note JSON" = "نسخ المنشور بصيغة JSON";
/* Button to copy report ID. */
"Copy Report ID" = "نسخ معرف البلاغ";
/* Context menu option for copying the text from an note. */
"Copy Text" = "نسخ النص";
/* Context menu option for copying the ID of the user who created the note. */
"Copy User Pubkey" = "نسخ معرف الحساب";
/* Alert message to indicate that the blocked user could not be found. */
"Could not find user to block..." = "لم يتم العثور حساب لحظره";
/* Button to create account. */
"Create" = "انشاء";
/* Button to create an account. */
"Create Account" = "انشاء حساب";
/* Title of alert prompting the user to create a new mutelist. */
"Create new mutelist" = "أنشئ قائمة حظر جديدة";
/* Example description about Bitcoin creator(s), Satoshi Nakamoto. */
"Creator(s) of Bitcoin. Absolute legend." = "مبتكر البتكوين. اسطورة لن تتكرر.";
/* Dropdown option for selecting a custom translation server. */
"Custom" = "مخصص";
/* Name of the app, shown on the first screen when user is not logged in. */
"Damus" = "دامُس";
/* Button to pay a Lightning invoice with the user's default Lightning wallet. */
"Default Wallet" = "المحفظة الافتراضية";
/* Button for deleting the users account.
Button to delete a relay server that the user connects to.
Button to remove a user from their blocklist.
Section title for deleting the user */
"Delete" = "حذف";
/* Alert for deleting the users account.
Button to delete the user's account. */
"Delete Account" = "حذف الحساب";
/* Alert message to indicate this is a deleted account */
"Deleted Account" = "حذف الحساب";
/* Button to dismiss a text field alert. */
"Dismiss" = "اغلاق";
/* Label to prompt display name entry. */
"Display Name" = "الاسم";
/* Navigation title for DMs view, where DM is the English abbreviation for Direct Message.
Navigation title for view of DMs, where DM is an English abbreviation for Direct Message. */
"DMs" = "الرسائل الخاصة";
/* Button to dismiss wallet selection view for paying Lightning invoice. */
"Done" = "انهاء";
/* Heading indicating that this application allows users to earn money. */
"Earn Money" = "اكسب المال.";
/* Button to edit user's profile. */
"Edit" = "تحرير";
/* Text indicating that the view is used for editing which participants are replied to in a note. */
"Edit participants" = "تحرير المشاركين";
/* Heading indicating that this application keeps private messaging end-to-end encrypted. */
"Encrypted" = "مشفر";
/* Prompt for user to enter an account key to login. */
"Enter your account key to login:" = "أدخل مفتاح حسابك لتسجيل الدخول:";
/* Error message indicating why saving keys failed. */
"Error: %@" = "خطأ: %@";
/* Label indicating that the below text is the EULA, an acronym for End User License Agreement. */
"EULA" = "اتفاقية الاستخدام";
/* Button to follow a user. */
"Follow" = "متابعة";
/* Label describing followers of a user. */
"Followers" = "المتابعون";
/* Text to indicate that the button next to it is in a state that indicates that it is in the process of following a profile.
Part of a larger sentence to describe how many profiles a user is following. */
"Following" = "المتابَعين";
/* Label to indicate that the user is in the process of following another user. */
"Following..." = "يتابع...";
/* Text to indicate that button next to it is in a state that will follow a profile when tapped. */
"Follows" = "تابع";
/* Navigation bar title for Global view where posts from all connected relay servers appear. */
"Global" = "عام";
/* Navigation link to go to post referenced by hex code. */
"Goto post %@" = "عرض المنشور %@";
/* Navigation link to go to profile. */
"Goto profile %@" = "عرض الحساب %@";
/* Button to hide a post from a user who has been blocked. */
"Hide" = "اخفاء";
/* Button to hide the DeepL translation API key.
Button to hide the LibreTranslate server API key. */
"Hide API Key" = "اخفاء مفتاح API";
/* Navigation bar title for Home view where posts and replies appear from those who the user is following. */
"Home" = "الرئيسية";
/* Placeholder example text for profile picture URL. */
"https://example.com/pic.jpg" = "https://example.com/pic.jpg";
/* Placeholder example text for website URL for user profile. */
"https://jb55.com" = "https://jb55.com";
/* Button for user to report that the account or content has illegal content. */
"Illegal content" = "محتوى غير قانوني";
/* Error message indicating that an invalid account key was entered for login. */
"Invalid key" = "المفتاح غير صالح";
/* Button for user to report that the account or content has spam. */
"It's spam" = "سبام";
/* Placeholder example text for identifier used for NIP-05 verification. */
"jb55@jb55.com" = "jb55@jb55.com";
/* Moves the post button to the left side of the screen */
"Left Handed" = "تفضيل استخدام اليد اليسرى";
/* Button to complete account creation and start using the app. */
"Let's go!" = "هيا بنا!";
/* Placeholder text for entry of Lightning Address or LNURL. */
"Lightning Address or LNURL" = "عنوان البرق أو LNURL";
/* Indicates that the view is for paying a Lightning invoice. */
"Lightning Invoice" = "برقية";
/* Dropdown option label for Lightning wallet, LNLink. */
"LNLink" = "LNLink";
/* Dropdown option label for system default for Lightning wallet. */
"Local default" = "الاختيار";
/* Button to log into account.
Button to log into an account. */
"Login" = "الدخول";
/* Alert for logging out the user.
Button for logging out the user.
Button to close the alert that informs that the current account has been deleted. */
"Logout" = "الخروج";
/* Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out. */
"Make sure your nsec account key is saved before you logout or you will lose access to this account" = "تأكد من حفظ مفتاح حسابك السري قبل الخروج حتى لا تفقد امكانية الدخول الى حسابك.";
/* Dropdown option label for Lightning wallet, Muun. */
"Muun" = "Muun";
/* Label for NIP-05 Verification section of user profile form. */
"NIP-05 Verification" = "تحقق NIP-05";
/* Button to cancel out of posting a note after being alerted that it looks like they might be posting a private key. */
"No" = "لا";
/* Alert message prompt that asks if the user wants to create a new block list, overwriting previous block lists. */
"No block list found, create a new one? This will overwrite any previous block lists." = "لم نعثر على قائمة حظر. هل تريد انشاء قائمة جديدة؟ سيتم استبدال أي قوائم سابقة ان وجدت";
/* No search results. */
"none" = "لا شيء";
/* Dropdown option for selecting no translation service. */
"None" = "لا اختيار";
/* Alert user that they might be attempting to paste a private key and ask them to confirm. */
"Note contains \"nsec1\" private key. Are you sure?" = "يحتوي المنشور على مفتاح خاص \"nsec1\". هل أنت متأكد؟";
/* Indicates that there are no notes in the timeline to view. */
"Nothing to see here. Check back later!" = "لا جديد في هذه اللحظة. يرجى المعاودة لاحقا!";
/* Navigation title for notifications. */
"Notifications" = "التنبيهات";
/* String indicating that a given timestamp just occurred */
"now" = "الان";
/* Prompt for user to enter in an account key to login. This text shows the characters the key could start with if it was a private key. */
"nsec1..." = "nsec1...";
/* Button for user to report that the account or content has nudity or explicit content. */
"Nudity or explicit content" = "عري أو محتوى فاضح";
/* Label indicating that a form input is optional. */
"optional" = "غير الزامي";
/* Button to pay a Lightning invoice. */
"Pay" = "ادفع";
/* Navigation bar title for view to pay Lightning invoice. */
"Pay the Lightning invoice" = "ادفع البرقية";
/* Dropdown option label for Lightning wallet, Phoenix. */
"Phoenix" = "Phoenix";
/* Button to post a note. */
"Post" = "انشر";
/* Text to indicate that what is being shown is a post from a user who has been blocked. */
"Post from a user you've blocked" = "منشور لمستخدم محظور";
/* Label for filter for seeing only posts (instead of posts and replies). */
"Posts" = "المنشورات";
/* Label for filter for seeing posts and replies (instead of only posts). */
"Posts & Replies" = "المنشورات والردود";
/* Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading. */
"Private" = "خصوصية";
/* Title of the secure field that holds the user's private key. */
"Private Key" = "المفتاح السري";
/* Sidebar menu label for Profile view. */
"Profile" = "الملف الشخصي";
/* Label for Profile Picture section of user profile form. */
"Profile Picture" = "صورة الحساب";
/* Section title for the user's public account ID. */
"Public Account ID" = "معرف الحساب";
/* Label indicating that the text is a user's public account key. */
"Public key" = "المفتاح العام";
/* Label indicating that the text is a user's public account key. */
"Public Key" = "المفتاح العام";
/* Prompt to ask user if the key they entered is a public key. */
"Public Key?" = "مفتاح عام؟";
/* Navigation bar title for Reactions view. */
"Reactions" = "التفاعل";
/* Section title for recommend relay servers that could be added as part of configuration */
"Recommended Relays" = "موصّلات موصى بها";
/* Button to reject the end user license agreement, which disallows the user from being let into the app. */
"Reject" = "رفض";
/* Label to display relay address.
Text field for relay server. Used for testing purposes. */
"Relay" = "موصّل";
/* Sidebar menu label for Relay servers view
Sidebar menu label for Relays view. */
"Relays" = "موصّلات";
/* Description of what was done as a result of sending a report to relay servers. */
"Relays have been notified and clients will be able to use this information to filter content. Thank you!" = "تم ابلاغ الموصّلات وسيتم الاستفادة من هذا البلاغ لتصفية المحتوى. شكرا لك!";
/* Button label to remove all participants from a note reply. */
"Remove all" = "حذف المشاركين";
/* Label to indicate that the user is replying to themself. */
"Reply to self" = "رد على منشوره السابق";
/* Label to indicate that the user is replying to 2 users. */
"Replying to %@ & %@" = "رد على %1$@ & %2$@";
/* Indicating that the user is replying to the following listed people. */
"Replying to:" = "رد على:";
/* Button to report a profile.
Context menu option for reporting content. */
"Report" = "ابلاغ";
/* Label indicating that the text underneath is the identifier of the report that was sent to relay servers. */
"Report ID:" = "معرف البلاغ";
/* Message indicating that a report was successfully sent to relay servers. */
"Report sent!" = "تم الابلاغ!";
/* Button to confirm reposting a post.
Title of alert for confirming to repost a post. */
"Repost" = "إعادة نشر";
/* Text indicating that the post was reposted (i.e. re-shared). */
"Reposted" = "منشور مُعاد";
/* Navigation bar title for Reposts view. */
"Reposts" = "اعادات النشر";
/* Picker option for DM selector for seeing only message requests (DMs that someone else sent the user which has not been responded to yet). DM is the English abbreviation for Direct Message. */
"Requests" = "طلبات";
/* Button to retry completing account creation after an error occurred. */
"Retry" = "اعادة المحاولة";
/* Dropdown option label for Lightning wallet, River */
"River" = "River";
/* Example username of Bitcoin creator(s), Satoshi Nakamoto. */
"satoshi" = "ساتوشي";
/* Name of Bitcoin creator(s). */
"Satoshi Nakamoto" = "ساتوشي ناكاموتو";
/* Button for saving profile. */
"Save" = "حفظ";
/* Context menu option to save an image. */
"Save Image" = "حفظ الصورة";
/* Navigation link to search hashtag. */
"Search hashtag: #%@" = "البحث عن وسم: #%@";
/* Placeholder text to prompt entry of search query. */
"Search..." = "بحث...";
/* Section title for user's secret account login key. */
"Secret Account Login Key" = "المفتاح السري للحساب";
/* Title of section for selecting a Lightning wallet to pay a Lightning invoice. */
"Select a Lightning wallet" = "اختر محفظة البرق";
/* Prompt selection of user's default wallet */
"Select default wallet" = "المحفظة الافتراضية";
/* Text prompt for user to send a message to the other user. */
"Send a message to start the conversation..." = "أرسل رسالة لبدء المحادثة...";
/* Prompt selection of LibreTranslate server to perform machine translations on notes */
"Server" = "خادم";
/* Navigation title for Settings view.
Sidebar menu label for accessing the app settings */
"Settings" = "الاعدادات";
/* Button to share a post
Button to share an image.
Button to share the link to a profile. */
"Share" = "مشاركة";
/* Button to show a post from a user who has been blocked.
Toggle to show or hide user's secret account login key. */
"Show" = "عرض";
/* Button to show the DeepL translation API key.
Button to show the LibreTranslate server API key. */
"Show API Key" = "عرض مفتاح API";
/* Toggle to show or hide selection of wallet. */
"Show wallet selector" = "هل تريد اختيار المحفظة عند كل عملية دفع؟";
/* Sidebar menu label to sign out of the account. */
"Sign out" = "خروج";
/* Dropdown option label for Lightning wallet, Strike. */
"Strike" = "Strike";
/* Button to close out of alert that informs that the action to block a user was successful. */
"Thanks!" = "شكرا!";
/* Button for user to report that the account is impersonating someone. */
"They are impersonating someone" = "انتحال صفة شخص آخر";
/* Warning that the inputted account key is a public key and the result of what happens because of it. */
"This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." = "هذا مفتاح عام. لن تستطيع النشر أو التفاعل بهذا الحساب بأي طريقة. تستطيع فقط مشاهدة المحتوى العام من منظور صاحب الحساب.";
/* Warning that the inputted account key for login is an old-style and asking user to verify if it is a public key. */
"This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key." = "صيغة المفتاح قديمة. لا نستطيع التحديد إذا ما كان المفتاح خاصا أو عاما. الرجاء تفعيل الخانة بالأسفل إذا كان المفتاح عاما.";
/* Label to describe that a public key is the user's account ID and what they can do with it. */
"This is your account ID, you can give this to your friends so that they can follow you. Click to copy." = "هذا معرف حسابك. بإمكانك إرساله لأصدقائك حتى يتمكنوا من متابعتك. اضغط للنسخ.";
/* Label to describe that a private key is the user's secret account key and what they should do with it. */
"This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!" = "هذا مفتاح الحساب السري. تحتاجه للدخول إلى حسابك. لا تشاركه مع أي شخص! احتفظ به في مكان آمن مثل برنامج إدارة كلمات المرور السرية. ";
/* Navigation bar title for note thread.
Navigation bar title for threaded event detail view. */
"Thread" = "منشور";
/* Button to translate note from different language. */
"Translate Note" = "ترجم المنشور";
/* Button to indicate that the note has been translated from a different language. */
"Translated from (lang)" = "مُترجَم من (lang)";
/* Button to indicate that the note is in the process of being translated from a different language. */
"Translating from (lang)..." = "الترجمة من (lang)...";
/* Text field prompt asking user to type the word DELETE to confirm that they want to proceed with deleting their account. The all caps lock DELETE word should not be translated. Everything else should. */
"Type DELETE to delete" = "اكتب DELETE لتأكيد الحذف";
/* Text box prompt to ask user to type their post. */
"Type your post here..." = "اكتب المنشور هنا...";
/* Non-breaking space character to fill in blank space next to event action button icons. */
"u{00A0}" = "u{00A0}";
/* Button to unfollow a user. */
"Unfollow" = "الغاء المتابعة";
/* Text to indicate that the button next to it is in a state that indicates that it is in the process of unfollowing a profile. */
"Unfollowing" = "يلغي المتابعة";
/* Label to indicate that the user is in the process of unfollowing another user. */
"Unfollowing..." = "يلغي المتابعة...";
/* Text to indicate that the button next to it is in a state that will unfollow a profile when tapped. */
"Unfollows" = "ألغى متابعة";
/* Example URL to LibreTranslate server */
"URL" = "رابط";
/* Alert message to indicate the user has been blocked */
"User blocked" = "الحساب محظور";
/* Alert message that informs a user was blocked. */
"User has been blocked" = "تم الحظر";
/* Label for Username section of user profile form.
Label to prompt username entry. */
"Username" = "اسم المستخدم";
/* Sidebar menu label for Wallet view. */
"Wallet" = "المحفظة";
/* Dropdown option label for Lightning wallet, Wallet of Satoshi. */
"Wallet of Satoshi" = "Wallet of Satoshi";
/* Section title for selection of wallet. */
"Wallet Selector" = "تفضيلات المحفظة";
/* Label for Website section of user profile form. */
"Website" = "موقع الكتروني";
/* Welcoming message to the reader. The variable is 'you', the reader. */
"Welcome to the social network %@ control." = "مرحبا بك في شبكتك الاجتماعية!";
/* Text to welcome user. */
"Welcome, %@!" = "مرحبا، %@!";
/* Header text to prompt user what issue they want to report. */
"What do you want to report?" = "عن ماذا تريد الابلاغ";
/* Placeholder example for relay server address. */
"wss://some.relay.com" = "wss://some.relay.com";
/* Text of button that confirms to overwrite the existing mutelist. */
"Yes, Overwrite" = "نعم، استبدل";
/* Button to proceed with posting a note even though it looks like they might be posting a private key. */
"Yes, Post with Private Key" = "نعم، انشر المفتاح الخاص";
/* You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself. */
"you" = "أنت";
/* Label for Your Name section of user profile form. */
"Your Name" = "الاسم";
/* Footer text to inform user what will happen when the report is submitted. */
"Your report will be sent to the relays you are connected to" = "سيتم ارسال بلاغك للموصّلات المتصلة بحسابك";
/* Dropdown option label for Lightning wallet, Zebedee. */
"Zebedee" = "Zebedee";
/* Dropdown option label for Lightning wallet, Zeus LN. */
"Zeus LN" = "Zeus LN";
-222
View File
@@ -1,222 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>collapsed_event_view_other_notes</key>
<dict>
<key>NOTES</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string>%d منشورات اضافية</string>
<key>many</key>
<string>%d منشورات اضافية</string>
<key>one</key>
<string>%d منشور اضافي</string>
<key>other</key>
<string>%d منشورات اضافية</string>
<key>two</key>
<string>%d منشوران</string>
<key>zero</key>
<string>%d منشورات أخرى</string>
</dict>
<key>NSStringLocalizedFormatKey</key>
<string>··· %#@NOTES@ ···</string>
</dict>
<key>followers_count</key>
<dict>
<key>FOLLOWERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string>المتابعون</string>
<key>many</key>
<string>المتابعون</string>
<key>one</key>
<string>متابع</string>
<key>other</key>
<string>المتابعون</string>
<key>two</key>
<string>متابعان</string>
<key>zero</key>
<string>متابع</string>
</dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@FOLLOWERS@</string>
</dict>
<key>reactions_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@REACTIONS@</string>
<key>REACTIONS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string>تفاعلات</string>
<key>many</key>
<string>تفاعل</string>
<key>one</key>
<string>تفاعل</string>
<key>other</key>
<string>تفاعل</string>
<key>two</key>
<string>تفاعل</string>
<key>zero</key>
<string>تفاعل</string>
</dict>
</dict>
<key>relays_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@RELAYS@</string>
<key>RELAYS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string>موصّلات</string>
<key>many</key>
<string>موصّلات</string>
<key>one</key>
<string> موصّل</string>
<key>other</key>
<string>موصّلات</string>
<key>two</key>
<string>موصّلان</string>
<key>zero</key>
<string>موصّل</string>
</dict>
</dict>
<key>replying_to_one_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>رد على %@%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string> &amp; %d آخرون</string>
<key>many</key>
<string> &amp; %d آخرون</string>
<key>one</key>
<string>&amp; %d آخر</string>
<key>other</key>
<string>&amp; %d آخرون</string>
<key>two</key>
<string> &amp; %d آخران</string>
<key>zero</key>
<string></string>
</dict>
</dict>
<key>replying_to_two_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>رد على%@, %@%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string> &amp; %d آخرون</string>
<key>many</key>
<string> &amp; %d آخرون</string>
<key>one</key>
<string>&amp; %d آخر</string>
<key>other</key>
<string>&amp; %d آخرون</string>
<key>two</key>
<string> &amp; %d آخران</string>
<key>zero</key>
<string></string>
</dict>
</dict>
<key>reposts_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@REPOSTS@</string>
<key>REPOSTS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string>اعادات نشر</string>
<key>many</key>
<string>اعادات نشر</string>
<key>one</key>
<string>اعادة نشر</string>
<key>other</key>
<string>اعادات نشر</string>
<key>two</key>
<string>اعادات نشر</string>
<key>zero</key>
<string>اعادات نشر</string>
</dict>
</dict>
<key>sats_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%1$#@SATS@</string>
<key>SATS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>@</string>
<key>few</key>
<string>%2$@ ساتوشي</string>
<key>many</key>
<string>%2$@ ساتوشي</string>
<key>one</key>
<string>%2$@ ساتوشي</string>
<key>other</key>
<string>%2$@ ساتوشي</string>
<key>two</key>
<string>%2$@ ساتوشي</string>
<key>zero</key>
<string>%2$@ ساتوشي</string>
</dict>
</dict>
<key>zaps_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@ZAPS@</string>
<key>ZAPS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>few</key>
<string>Zaps</string>
<key>many</key>
<string>Zaps</string>
<key>one</key>
<string>Zap</string>
<key>other</key>
<string>Zaps</string>
<key>two</key>
<string>Zaps</string>
<key>zero</key>
<string>Zaps</string>
</dict>
</dict>
</dict>
</plist>

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