Compare commits
74 Commits
tx_transla
...
tyiu/more-
| Author | SHA1 | Date | |
|---|---|---|---|
|
b367382aae
|
|||
|
bf1866056c
|
|||
|
016dfa54f3
|
|||
|
887eb902bf
|
|||
|
dcf328e7ac
|
|||
|
|
c10fcc52e3 | ||
|
|
08dd2d5414 | ||
|
|
421245a2f8 | ||
|
|
ec4790a8fe | ||
|
|
5f22a7691f | ||
|
|
d878ff6fdb | ||
|
|
7081c4ac69 | ||
|
|
b03bef2f8b | ||
|
|
a153515366 | ||
|
|
d5857325b1 | ||
|
|
04e4bc7985 | ||
|
|
e7d32d9ea7 | ||
|
|
442a50f9ae | ||
|
|
9f8379cb1e | ||
|
|
d0eb632ce6 | ||
|
|
cd62418dda | ||
|
|
ba32d15a49 | ||
|
|
b688fa98a5 | ||
|
|
61a451184b | ||
|
|
96741af97b | ||
|
|
553dcb785c | ||
|
|
22008aeabc | ||
|
|
64bb28e017 | ||
|
|
c58c349053 | ||
|
|
4d358415bd | ||
|
|
5ba5a68628 | ||
|
|
89c857d3e9 | ||
|
|
4d3a3184b4 | ||
|
|
71b1a9d14f | ||
|
|
8785f31834 | ||
|
|
104205394f | ||
|
|
6593c9456d | ||
|
|
186954195d | ||
|
|
dba31a9d33 | ||
|
|
a69fb5306c | ||
|
|
fb1bcdd31f | ||
|
|
2ccc7e9a30 | ||
|
|
e9380c3821 | ||
|
|
39fa973a80 | ||
|
|
b70ce53b88 | ||
|
|
16e3c4e1cf | ||
|
|
b3b8a708f3 | ||
|
|
d89e3d32f8 | ||
|
|
b99e9b9acc | ||
|
|
61974ca696 | ||
|
|
42f484bc64 | ||
|
|
43c6084620 | ||
|
|
501412e75c | ||
|
|
5789cc0097 | ||
|
|
d4995aa4bf | ||
|
|
57dbb6a487 | ||
|
|
7f71ddce1d | ||
|
|
abf736ec2a | ||
|
|
2cfcc82b2d | ||
|
|
aaa21bf1bf | ||
|
|
ba03be5b91 | ||
|
|
3f3b78f9bc | ||
|
|
2348f64dff | ||
| 8428f0af43 | |||
|
|
8c91ce3e10 | ||
|
|
5fd5593595 | ||
|
|
2e99e5acaa | ||
|
|
0b7a600c67 | ||
|
|
bd5390fbc0 | ||
|
|
068099c5a7 | ||
|
|
7372c4847d | ||
|
|
b42f0ec5eb | ||
|
|
0d2ab6aff3 | ||
|
|
e3732b3adc |
87
CHANGELOG.md
@@ -1,3 +1,88 @@
|
||||
## [1.0.0-5] - 2023-01-06
|
||||
|
||||
### Added
|
||||
|
||||
- Added share button to profile (William Casarin)
|
||||
- Added universal link sharing of notes (William Casarin)
|
||||
- Added clear cache button to wipe pfp/image cache (OlegAba)
|
||||
- Allow Adding Relay Without wss:// Prefix (Joel Klabo)
|
||||
- Allow Saving Images to Library (Joel Klabo)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Added damus gradient to post button (Ben Weeks)
|
||||
- Center the Post Button (Thomas)
|
||||
- Switch yellow nip05 check to gray (William Casarin)
|
||||
- Switch from bluecheck to purplecheck (William Casarin)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add system background color to profile pics (OlegAba)
|
||||
- High res color pubkey on profile page (William Casarin)
|
||||
- Don't spin forever if we're temporarily disconnected (William Casarin)
|
||||
- Fixed a few issues with avatars not animating (OlegAba)
|
||||
- Scroll to bottom when new DM received (Aidan O'Loan)
|
||||
- Make reply view scrollable (Joel Klabo)
|
||||
- Hide profile edit button when logged in with pubkey (Swift)
|
||||
|
||||
|
||||
[1.0.0-5]: https://github.com/damus-io/damus/releases/tag/v1.0.0-5
|
||||
|
||||
## [1.0.0-4] - 2023-01-04
|
||||
|
||||
### Added
|
||||
|
||||
- Added NIP05 Verification (William Casarin)
|
||||
- Downscale images if they are unreasonably large (OlegAba)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Revert to old style ln/dm buttons (William Casarin)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix ascii shrug guy (Lionello Lunesu)
|
||||
- Fix navigation popping in threads (William Casarin)
|
||||
|
||||
|
||||
[1.0.0-4]: https://github.com/damus-io/damus/releases/tag/v1.0.0-4
|
||||
|
||||
## [1.0.0-2] - 2023-01-03
|
||||
|
||||
### Added
|
||||
|
||||
- Cache link previews (William Casarin)
|
||||
- Added brb.io to recommended relay list (William Casarin)
|
||||
- Add Blixt Wallet to Wallet Selector (Benjamin Hakes)
|
||||
- Add River Wallet to Wallet Selector (Benjamin Hakes)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Added muted shaka images instead of thumbs up (CutClout)
|
||||
- Updated profile page look and feel (Ben Weeks)
|
||||
- Filter replies from global feed (Nitesh Balusu)
|
||||
- Show non-image links inline (William Casarin)
|
||||
- Add swipe gesture to switch between tabs (Thomas Rademaker)
|
||||
- Parse links in profiles (Lionello Lunesu) (Lio李歐)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix detection of email addresses in profiles (Lionello Lunesu)
|
||||
- Fix padding on search results view (OlegAba)
|
||||
- Fix home view moving after selecting from search result (OlegAba)
|
||||
- Fix bug where boost event is loaded in the thread instead of the boosted event (William Casarin)
|
||||
- Hide edit button on profile page when no private key (Swift)
|
||||
- Fixed follows and relays getting out of sync on profile pages (William Casarin)
|
||||
|
||||
|
||||
|
||||
[1.0.0-2]: https://github.com/damus-io/damus/releases/tag/v1.0.0-2
|
||||
## [1.0.0] - 2023-01-01
|
||||
|
||||
### Added
|
||||
@@ -266,3 +351,5 @@
|
||||
|
||||
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
||||
- Formatting Notes (may not format as intended in other web clients)
|
||||
- Italics: 1 asterisk `*italic*`
|
||||
- Bold: 2 asterisk `**bold**`
|
||||
- Strikethrough: 2 tildes `~~strikethrough~~`
|
||||
- Strikethrough: 1 tildes `~strikethrough~`
|
||||
- Code: 1 back-tick ``code``
|
||||
|
||||
#### 💬 Encrypted DMs (chat app, bottom navigation)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||
<file original="damus/en.lproj/InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||
<file original="damus/en-US.lproj/InfoPlist.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
@@ -15,9 +15,14 @@
|
||||
<target>damus</target>
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
|
||||
<source>"Granting Damus access to your photo library allows you to save photos.</source>
|
||||
<target>"Granting Damus access to your photo library allows you to save photos.</target>
|
||||
<note>Privacy - Photo Library Additions Usage Description</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
@@ -25,67 +30,87 @@
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<source> </source>
|
||||
<target> </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Blank space to separate profile picture from profile editor form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@" xml:space="preserve">
|
||||
<source>%@</source>
|
||||
<target>%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Number of people following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ following" xml:space="preserve">
|
||||
<source>%@ following</source>
|
||||
<target>%@ following</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@'s Followers" xml:space="preserve">
|
||||
<source>%@'s Followers</source>
|
||||
<target>%@'s Followers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<trans-unit id="%@ %@" xml:space="preserve">
|
||||
<source>%@ %@</source>
|
||||
<target>%@ %@</target>
|
||||
<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="%@. 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>
|
||||
<target>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs" xml:space="preserve">
|
||||
<source>%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs</source>
|
||||
<target>%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet." xml:space="preserve">
|
||||
<source>%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet.</source>
|
||||
<target>%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld" xml:space="preserve">
|
||||
<source>%lld</source>
|
||||
<target>%lld</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Number of profiles a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld/%lld" xml:space="preserve">
|
||||
<source>%lld/%lld</source>
|
||||
<target>%lld/%lld</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Fraction of how many of the user's relay servers that are operational.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="&nbsp;" xml:space="preserve">
|
||||
<source>&nbsp;</source>
|
||||
<target>&nbsp;</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="+" xml:space="preserve">
|
||||
<source>+</source>
|
||||
<target>+</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<trans-unit id="'%@' at '%@' will be used for verification" xml:space="preserve">
|
||||
<source>'%@' at '%@' will be used for verification</source>
|
||||
<target>'%@' at '%@' will be used for verification</target>
|
||||
<note>Description of how the nip05 identifier would be used for verification.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
|
||||
<source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
|
||||
<target>'%@' is an invalid nip05 identifier. It should look like an email.</target>
|
||||
<note>Description of why the nip05 identifier is invalid.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(Profile.displayName(profile: profile, pubkey: whos))'s Followers" xml:space="preserve">
|
||||
<source>(Profile.displayName(profile: profile, pubkey: whos))'s Followers</source>
|
||||
<target>(Profile.displayName(profile: profile, pubkey: whos))'s Followers</target>
|
||||
<note>Navigation bar title for view that shows who is following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(formattedSats) sat" xml:space="preserve">
|
||||
<source>(formattedSats) sat</source>
|
||||
<target>(formattedSats) sat</target>
|
||||
<note>Amount of 1 sat.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(formattedSats) sats" xml:space="preserve">
|
||||
<source>(formattedSats) sats</source>
|
||||
<target>(formattedSats) sats</target>
|
||||
<note>Amount of sats.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(who) following" xml:space="preserve">
|
||||
<source>(who) following</source>
|
||||
<target>(who) following</target>
|
||||
<note>Navigation bar title for view that shows who a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="< e >" xml:space="preserve">
|
||||
<source>< e ></source>
|
||||
<target>< e ></target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder for event mention.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="@" xml:space="preserve">
|
||||
<source>@</source>
|
||||
<target>@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Prefix character to username.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About" xml:space="preserve">
|
||||
<source>About</source>
|
||||
@@ -95,7 +120,7 @@
|
||||
<trans-unit id="About Me" xml:space="preserve">
|
||||
<source>About Me</source>
|
||||
<target>About Me</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for About Me section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Absolute Boss" xml:space="preserve">
|
||||
<source>Absolute Boss</source>
|
||||
@@ -110,22 +135,28 @@
|
||||
<trans-unit id="Add" xml:space="preserve">
|
||||
<source>Add</source>
|
||||
<target>Add</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to add recommended relay server.
|
||||
Button to confirm adding user inputted relay.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add Relay" xml:space="preserve">
|
||||
<source>Add Relay</source>
|
||||
<target>Add Relay</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for section for adding a relay server.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Any" xml:space="preserve">
|
||||
<source>Any</source>
|
||||
<target>Any</target>
|
||||
<note>Any amount of sats</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Are you sure you want to boost this post?" xml:space="preserve">
|
||||
<source>Are you sure you want to boost this post?</source>
|
||||
<target>Are you sure you want to boost this post?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Alert message to ask if user wants to boost a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="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." xml:space="preserve">
|
||||
<source>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.</source>
|
||||
<target>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.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Reminder to user that they should save their account information.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bitcoin Beach" xml:space="preserve">
|
||||
<source>Bitcoin Beach</source>
|
||||
@@ -135,7 +166,7 @@
|
||||
<trans-unit id="Bitcoin Lightning Tips" xml:space="preserve">
|
||||
<source>Bitcoin Lightning Tips</source>
|
||||
<target>Bitcoin Lightning Tips</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for Bitcoin Lightning Tips section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Blixt Wallet" xml:space="preserve">
|
||||
<source>Blixt Wallet</source>
|
||||
@@ -150,12 +181,13 @@
|
||||
<trans-unit id="Boost" xml:space="preserve">
|
||||
<source>Boost</source>
|
||||
<target>Boost</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to confirm boosting a post.
|
||||
Title of alert for confirming to boost a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Boosted" xml:space="preserve">
|
||||
<source>Boosted</source>
|
||||
<target>Boosted</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Text indicating that the post was boosted (i.e. re-shared).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Breez" xml:space="preserve">
|
||||
<source>Breez</source>
|
||||
@@ -165,72 +197,90 @@
|
||||
<trans-unit id="Broadcast" xml:space="preserve">
|
||||
<source>Broadcast</source>
|
||||
<target>Broadcast</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for broadcasting the user's note to all of the user's connected relay servers.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<target>Cancel</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to cancel out of posting a note.
|
||||
Button to cancel out of view adding user inputted relay.
|
||||
Cancel out of logging out the user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cash App" xml:space="preserve">
|
||||
<source>Cash App</source>
|
||||
<target>Cash App</target>
|
||||
<note>Dropdown option label for Lightning wallet, Cash App.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat" xml:space="preserve">
|
||||
<source>Chat</source>
|
||||
<target>Chat</target>
|
||||
<note>Navigation bar title for Chatroom view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Clear" xml:space="preserve">
|
||||
<source>Clear</source>
|
||||
<target>Clear</target>
|
||||
<note>Button for clearing cached data.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Clear Cache" xml:space="preserve">
|
||||
<source>Clear Cache</source>
|
||||
<target>Clear Cache</target>
|
||||
<note>Section title for clearing cached data.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copied" xml:space="preserve">
|
||||
<source>Copied</source>
|
||||
<target>Copied</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label indicating that a user's key was copied.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Copy</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to copy a relay server address.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Account ID" xml:space="preserve">
|
||||
<source>Copy Account ID</source>
|
||||
<target>Copy Account ID</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for copying the ID of the account that created the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Image" xml:space="preserve">
|
||||
<source>Copy Image</source>
|
||||
<target>Copy Image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option to copy an image into clipboard.
|
||||
Context menu option to copy an image to clipboard.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Image URL" xml:space="preserve">
|
||||
<source>Copy Image URL</source>
|
||||
<target>Copy Image URL</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option to copy the URL of an image into clipboard.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy LNURL" xml:space="preserve">
|
||||
<source>Copy LNURL</source>
|
||||
<target>Copy LNURL</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for copying a user's Lightning URL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Note ID" xml:space="preserve">
|
||||
<source>Copy Note ID</source>
|
||||
<target>Copy Note ID</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for copying the ID of the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Note JSON" xml:space="preserve">
|
||||
<source>Copy Note JSON</source>
|
||||
<target>Copy Note JSON</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for copying the JSON text from the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Text" xml:space="preserve">
|
||||
<source>Copy Text</source>
|
||||
<target>Copy Text</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for copying the text from an note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy User ID" xml:space="preserve">
|
||||
<source>Copy User ID</source>
|
||||
<target>Copy User ID</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Context menu option for copying the ID of the user who created the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy invoice" xml:space="preserve">
|
||||
<source>Copy invoice</source>
|
||||
<target>Copy invoice</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Title of section for copying a Lightning invoice identifier.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create" xml:space="preserve">
|
||||
<source>Create</source>
|
||||
@@ -250,27 +300,27 @@
|
||||
<trans-unit id="DM" xml:space="preserve">
|
||||
<source>DM</source>
|
||||
<target>DM</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation title for DM view, which is the English abbreviation for Direct Message.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Damus" xml:space="preserve">
|
||||
<source>Damus</source>
|
||||
<target>Damus</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Name of the app, shown on the first screen when user is not logged in.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Default Wallet" xml:space="preserve">
|
||||
<source>Default Wallet</source>
|
||||
<target>Default Wallet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to pay a Lightning invoice with the user's default Lightning wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete" xml:space="preserve">
|
||||
<source>Delete</source>
|
||||
<target>Delete</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to delete a relay server that the user connects to.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Dismiss" xml:space="preserve">
|
||||
<source>Dismiss</source>
|
||||
<target>Dismiss</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to dismiss a text field alert.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Display Name" xml:space="preserve">
|
||||
<source>Display Name</source>
|
||||
@@ -280,47 +330,47 @@
|
||||
<trans-unit id="Done" xml:space="preserve">
|
||||
<source>Done</source>
|
||||
<target>Done</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to dismiss wallet selection view for paying Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Earn Money" xml:space="preserve">
|
||||
<source>Earn Money</source>
|
||||
<target>Earn Money</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Heading indicating that this application allows users to earn money.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit" xml:space="preserve">
|
||||
<source>Edit</source>
|
||||
<target>Edit</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to edit user's profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit Profile" xml:space="preserve">
|
||||
<source>Edit Profile</source>
|
||||
<target>Edit Profile</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Title of navigation view for Edit Profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted" xml:space="preserve">
|
||||
<source>Encrypted</source>
|
||||
<target>Encrypted</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted DMs" xml:space="preserve">
|
||||
<source>Encrypted DMs</source>
|
||||
<target>Encrypted DMs</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<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">
|
||||
<source>Enter your account key to login:</source>
|
||||
<target>Enter your account key to login:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Prompt for user to enter an account key to login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: %@" xml:space="preserve">
|
||||
<source>Error: %@</source>
|
||||
<target>Error: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Error message indicating why saving keys failed.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Filter State" xml:space="preserve">
|
||||
<source>Filter State</source>
|
||||
<target>Filter State</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Filter state for seeing either only posts, or posts & replies.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follow" xml:space="preserve">
|
||||
<source>Follow</source>
|
||||
@@ -330,12 +380,13 @@
|
||||
<trans-unit id="Followers" xml:space="preserve">
|
||||
<source>Followers</source>
|
||||
<target>Followers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label describing followers of a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Following" xml:space="preserve">
|
||||
<source>Following</source>
|
||||
<target>Following</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Following..." xml:space="preserve">
|
||||
<source>Following...</source>
|
||||
@@ -345,27 +396,32 @@
|
||||
<trans-unit id="Follows" xml:space="preserve">
|
||||
<source>Follows</source>
|
||||
<target>Follows</target>
|
||||
<note>No comment provided by engineer.</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 id="Global" xml:space="preserve">
|
||||
<source>Global</source>
|
||||
<target>Global</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation bar title for Global view where posts from all connected relay servers appear.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Goto post %@" xml:space="preserve">
|
||||
<source>Goto post %@</source>
|
||||
<target>Goto post %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation link to go to post referenced by hex code.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Goto profile %@" xml:space="preserve">
|
||||
<source>Goto profile %@</source>
|
||||
<target>Goto profile %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation link to go to profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Home" xml:space="preserve">
|
||||
<source>Home</source>
|
||||
<target>Home</target>
|
||||
<note>No comment provided by engineer.</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 id="Invalid key" xml:space="preserve">
|
||||
<source>Invalid key</source>
|
||||
<target>Invalid key</target>
|
||||
<note>Error message indicating that an invalid account key was entered for login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="LNLink" xml:space="preserve">
|
||||
<source>LNLink</source>
|
||||
@@ -380,12 +436,12 @@
|
||||
<trans-unit id="Lightning Address or LNURL" xml:space="preserve">
|
||||
<source>Lightning Address or LNURL</source>
|
||||
<target>Lightning Address or LNURL</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder text for entry of Lightning Address or LNURL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lightning Invoice" xml:space="preserve">
|
||||
<source>Lightning Invoice</source>
|
||||
<target>Lightning Invoice</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Indicates that the view is for paying a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local default" xml:space="preserve">
|
||||
<source>Local default</source>
|
||||
@@ -395,17 +451,19 @@
|
||||
<trans-unit id="Login" xml:space="preserve">
|
||||
<source>Login</source>
|
||||
<target>Login</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to log into account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Logout" xml:space="preserve">
|
||||
<source>Logout</source>
|
||||
<target>Logout</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Alert for logging out the user.
|
||||
Button for logging out the user.
|
||||
Button to logout the user.</note>
|
||||
</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">
|
||||
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
|
||||
<target>Make sure your nsec account key is saved before you logout or you will lose access to this account</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Muun" xml:space="preserve">
|
||||
<source>Muun</source>
|
||||
@@ -415,27 +473,27 @@
|
||||
<trans-unit id="NIP-05 Verification" xml:space="preserve">
|
||||
<source>NIP-05 Verification</source>
|
||||
<target>NIP-05 Verification</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for NIP-05 Verification section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
|
||||
<source>Nothing to see here. Check back later!</source>
|
||||
<target>Nothing to see here. Check back later!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Indicates that there are no notes in the timeline to view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications" xml:space="preserve">
|
||||
<source>Notifications</source>
|
||||
<target>Notifications</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation title for notifications.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Pay" xml:space="preserve">
|
||||
<source>Pay</source>
|
||||
<target>Pay</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Pay the lightning invoice" xml:space="preserve">
|
||||
<source>Pay the lightning invoice</source>
|
||||
<target>Pay the lightning invoice</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<trans-unit id="Pay the Lightning invoice" xml:space="preserve">
|
||||
<source>Pay the Lightning invoice</source>
|
||||
<target>Pay the Lightning invoice</target>
|
||||
<note>Navigation bar title for view to pay Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Phoenix" xml:space="preserve">
|
||||
<source>Phoenix</source>
|
||||
@@ -445,82 +503,93 @@
|
||||
<trans-unit id="Post" xml:space="preserve">
|
||||
<source>Post</source>
|
||||
<target>Post</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to post a note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Posts" xml:space="preserve">
|
||||
<source>Posts</source>
|
||||
<target>Posts</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for filter for seeing only posts (instead of posts and replies).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Posts & Replies" xml:space="preserve">
|
||||
<source>Posts & Replies</source>
|
||||
<target>Posts & Replies</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for filter for seeing posts and replies (instead of only posts).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private" xml:space="preserve">
|
||||
<source>Private</source>
|
||||
<target>Private</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private Key" xml:space="preserve">
|
||||
<source>Private Key</source>
|
||||
<target>Private Key</target>
|
||||
<note>No comment provided by engineer.</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 id="PrivateKey" xml:space="preserve">
|
||||
<source>PrivateKey</source>
|
||||
<target>PrivateKey</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Title of the secure field that holds the user's private key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile Picture" xml:space="preserve">
|
||||
<source>Profile Picture</source>
|
||||
<target>Profile Picture</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for Profile Picture section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Account ID" xml:space="preserve">
|
||||
<source>Public Account ID</source>
|
||||
<target>Public Account ID</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Section title for the user's public account ID.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Key" xml:space="preserve">
|
||||
<source>Public Key</source>
|
||||
<target>Public Key</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label indicating that the text is a user's public account key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Key?" xml:space="preserve">
|
||||
<source>Public Key?</source>
|
||||
<target>Public Key?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Prompt to ask user if the key they entered is a public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public key" xml:space="preserve">
|
||||
<source>Public key</source>
|
||||
<target>Public key</target>
|
||||
<note>Label indicating that the text is a user's public account key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recommended Relays" xml:space="preserve">
|
||||
<source>Recommended Relays</source>
|
||||
<target>Recommended Relays</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Section title for recommend relay servers that could be added as part of configuration</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Relay" xml:space="preserve">
|
||||
<source>Relay</source>
|
||||
<target>Relay</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Text field for relay server. Used for testing purposes.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Relays" xml:space="preserve">
|
||||
<source>Relays</source>
|
||||
<target>Relays</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Header text for relay server list for configuration.
|
||||
Part of a larger sentence to describe how many relay servers a user is connected.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply to self" xml:space="preserve">
|
||||
<source>Reply to self</source>
|
||||
<target>Reply to self</target>
|
||||
<note>Label to indicate that the user is replying to themself.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Replying to %@ & %@" xml:space="preserve">
|
||||
<source>Replying to %1$@ & %2$@</source>
|
||||
<target>Replying to %1$@ & %2$@</target>
|
||||
<note>Label to indicate that the user is replying to 2 users.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Replying to:" xml:space="preserve">
|
||||
<source>Replying to:</source>
|
||||
<target>Replying to:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Indicating that the user is replying to the following listed people.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reset" xml:space="preserve">
|
||||
<source>Reset</source>
|
||||
<target>Reset</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Section title for resetting the user</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="River" xml:space="preserve">
|
||||
<source>River</source>
|
||||
@@ -535,57 +604,62 @@
|
||||
<trans-unit id="Save" xml:space="preserve">
|
||||
<source>Save</source>
|
||||
<target>Save</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button for saving profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save Image" xml:space="preserve">
|
||||
<source>Save Image</source>
|
||||
<target>Save Image</target>
|
||||
<note>Context menu option to save an image.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search hashtag: #%@" xml:space="preserve">
|
||||
<source>Search hashtag: #%@</source>
|
||||
<target>Search hashtag: #%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation link to search hashtag.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search..." xml:space="preserve">
|
||||
<source>Search...</source>
|
||||
<target>Search...</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder text to prompt entry of search query.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secret Account Login Key" xml:space="preserve">
|
||||
<source>Secret Account Login Key</source>
|
||||
<target>Secret Account Login Key</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Section title for user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select a lightning wallet" xml:space="preserve">
|
||||
<source>Select a lightning wallet</source>
|
||||
<target>Select a lightning wallet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<trans-unit id="Select a Lightning wallet" xml:space="preserve">
|
||||
<source>Select a Lightning wallet</source>
|
||||
<target>Select a Lightning wallet</target>
|
||||
<note>Title of section for selecting a Lightning wallet to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select default wallet" xml:space="preserve">
|
||||
<source>Select default wallet</source>
|
||||
<target>Select default wallet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Prompt selection of user's default wallet</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send a message to start the conversation..." xml:space="preserve">
|
||||
<source>Send a message to start the conversation...</source>
|
||||
<target>Send a message to start the conversation...</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Text prompt for user to send a message to the other user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Settings" xml:space="preserve">
|
||||
<source>Settings</source>
|
||||
<target>Settings</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation title for Settings view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share" xml:space="preserve">
|
||||
<source>Share</source>
|
||||
<target>Share</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button to share an image.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show" xml:space="preserve">
|
||||
<source>Show</source>
|
||||
<target>Show</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Toggle to show or hide user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show wallet selector" xml:space="preserve">
|
||||
<source>Show wallet selector</source>
|
||||
<target>Show wallet selector</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Toggle to show or hide selection of wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Strike" xml:space="preserve">
|
||||
<source>Strike</source>
|
||||
@@ -595,27 +669,28 @@
|
||||
<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>
|
||||
<target>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.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Warning that the inputted account key is a public key and the result of what happens because of it.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="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." xml:space="preserve">
|
||||
<source>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.</source>
|
||||
<target>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.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Warning that the inputted account key for login is an old-style and asking user to verify if it is a public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is your account ID, you can give this to your friends so that they can follow you. Click to copy." xml:space="preserve">
|
||||
<source>This is your account ID, you can give this to your friends so that they can follow you. Click to copy.</source>
|
||||
<target>This is your account ID, you can give this to your friends so that they can follow you. Click to copy.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label to describe that a public key is the user's account ID and what they can do with it.</note>
|
||||
</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">
|
||||
<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>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!</target>
|
||||
<note>No comment provided by engineer.</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 id="Thread" xml:space="preserve">
|
||||
<source>Thread</source>
|
||||
<target>Thread</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Navigation bar title for note thread.
|
||||
Navigation bar title for threaded event detail view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Type your post here..." xml:space="preserve">
|
||||
<source>Type your post here...</source>
|
||||
@@ -630,7 +705,7 @@
|
||||
<trans-unit id="Unfollowing" xml:space="preserve">
|
||||
<source>Unfollowing</source>
|
||||
<target>Unfollowing</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollowing..." xml:space="preserve">
|
||||
<source>Unfollowing...</source>
|
||||
@@ -640,12 +715,13 @@
|
||||
<trans-unit id="Unfollows" xml:space="preserve">
|
||||
<source>Unfollows</source>
|
||||
<target>Unfollows</target>
|
||||
<note>No comment provided by engineer.</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 id="Username" xml:space="preserve">
|
||||
<source>Username</source>
|
||||
<target>Username</target>
|
||||
<note>Label to prompt username entry.</note>
|
||||
<note>Label for Username section of user profile form.
|
||||
Label to prompt username entry.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
|
||||
<source>Wallet Of Satoshi</source>
|
||||
@@ -655,27 +731,27 @@
|
||||
<trans-unit id="Wallet Selector" xml:space="preserve">
|
||||
<source>Wallet Selector</source>
|
||||
<target>Wallet Selector</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Section title for selection of wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Website" xml:space="preserve">
|
||||
<source>Website</source>
|
||||
<target>Website</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for Website section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome to the social network %@ control." xml:space="preserve">
|
||||
<source>Welcome to the social network %@ control.</source>
|
||||
<target>Welcome to the social network %@ control.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Welcoming message to the reader. The variable is 'you', the reader.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome, %@!" xml:space="preserve">
|
||||
<source>Welcome, %@!</source>
|
||||
<target>Welcome, %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Text to welcome user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your Name" xml:space="preserve">
|
||||
<source>Your Name</source>
|
||||
<target>Your Name</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label for Your Name section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Zebedee" xml:space="preserve">
|
||||
<source>Zebedee</source>
|
||||
@@ -687,35 +763,45 @@
|
||||
<target>Zeus LN</target>
|
||||
<note>Dropdown option label for Lightning wallet, Zeus LN.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve">
|
||||
<source>collapsed_event_view_other_notes</source>
|
||||
<target>collapsed_event_view_other_notes</target>
|
||||
<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 id="https://example.com/pic.jpg" xml:space="preserve">
|
||||
<source>https://example.com/pic.jpg</source>
|
||||
<target>https://example.com/pic.jpg</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder example text for profile picture URL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="https://jb55.com" xml:space="preserve">
|
||||
<source>https://jb55.com</source>
|
||||
<target>https://jb55.com</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder example text for website URL for user profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="jb55@jb55.com" xml:space="preserve">
|
||||
<source>jb55@jb55.com</source>
|
||||
<target>jb55@jb55.com</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder example text for identifier used for NIP-05 verification.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="none" xml:space="preserve">
|
||||
<source>none</source>
|
||||
<target>none</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>No search results.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="now" xml:space="preserve">
|
||||
<source>now</source>
|
||||
<target>now</target>
|
||||
<note>String indicating that a given timestamp just occurred</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="nsec1..." xml:space="preserve">
|
||||
<source>nsec1...</source>
|
||||
<target>nsec1...</target>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="optional" xml:space="preserve">
|
||||
<source>optional</source>
|
||||
<target>optional</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Label indicating that a form input is optional.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="replying_to_one_and_others" translate="no" xml:space="preserve">
|
||||
<source>replying_to_one_and_others</source>
|
||||
@@ -735,66 +821,81 @@
|
||||
<trans-unit id="wss://some.relay.com" xml:space="preserve">
|
||||
<source>wss://some.relay.com</source>
|
||||
<target>wss://some.relay.com</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Placeholder example for relay server address.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<target>you</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="··· %lld other notes ···" xml:space="preserve">
|
||||
<source>··· %lld other notes ···</source>
|
||||
<target>··· %lld other notes ···</target>
|
||||
<note>No comment provided by engineer.</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 id="🤙" xml:space="preserve">
|
||||
<source>🤙</source>
|
||||
<target>🤙</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>Button with emoji to like an event.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en.lproj/Localizable.stringsdict" source-language="en" target-language="en" datatype="plaintext">
|
||||
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>1 other note</source>
|
||||
<target>1 other note</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>%d other notes</source>
|
||||
<target>%d other notes</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source>0 other notes</source>
|
||||
<target>0 other notes</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>··· %#@NOTES@ ···</source>
|
||||
<target>··· %#@NOTES@ ···</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>Replying to %@%#@others@</source>
|
||||
<target>Replying to %@%#@others@</target>
|
||||
<source>Replying to %@%#@OTHERS@</source>
|
||||
<target>Replying to %@%#@OTHERS@</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</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">
|
||||
<source> & 1 other</source>
|
||||
<target> & 1 other</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/others:dict/other:dict/:string" xml:space="preserve">
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source> & %d others</source>
|
||||
<target> & %d others</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/others:dict/zero:dict/:string" xml:space="preserve">
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source/>
|
||||
<target/>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>Replying to %@, %@%#@others@</source>
|
||||
<target>Replying to %@, %@%#@others@</target>
|
||||
<source>Replying to %@, %@%#@OTHERS@</source>
|
||||
<target>Replying to %@, %@%#@OTHERS@</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</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">
|
||||
<source> & 1 other</source>
|
||||
<target> & 1 other</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/others:dict/other:dict/:string" xml:space="preserve">
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source> & %d others</source>
|
||||
<target> & %d others</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/others:dict/zero:dict/:string" xml:space="preserve">
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source/>
|
||||
<target/>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
@@ -5,8 +5,8 @@
|
||||
<key>replying_to_one_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Replying to %@%#@others@</string>
|
||||
<key>others</key>
|
||||
<string>Replying to %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
@@ -23,8 +23,8 @@
|
||||
<key>replying_to_two_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Replying to %@, %@%#@others@</string>
|
||||
<key>others</key>
|
||||
<string>Replying to %@, %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
@@ -38,5 +38,23 @@
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>collapsed_event_view_other_notes</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>··· %#@NOTES@ ···</string>
|
||||
<key>NOTES</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>0 other notes</string>
|
||||
<key>one</key>
|
||||
<string>1 other note</string>
|
||||
<key>other</key>
|
||||
<string>%d other notes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"developmentRegion" : "en",
|
||||
"developmentRegion" : "en-US",
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "en",
|
||||
"targetLocale" : "en-US",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "14C18",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
@@ -1,4 +0,0 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
743
damus Localizations/es-419.xcloc/Localized Contents/es-419.xliff
Normal file
@@ -0,0 +1,743 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||
<file original="damus/en-US.lproj/InfoPlist.strings" datatype="plaintext" source-language="en-US" target-language="es-419">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||
<source>Damus</source>
|
||||
<note>Bundle display name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||
<source>damus</source>
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
|
||||
<source>"Granting Damus access to your photo library allows you to save photos.</source>
|
||||
<note>Privacy - Photo Library Additions Usage Description</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="es-419" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<source> </source>
|
||||
<note>Blank space to separate profile picture from profile editor form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@" xml:space="preserve">
|
||||
<source>%@</source>
|
||||
<note>Number of people following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ %@" xml:space="preserve">
|
||||
<source>%@ %@</source>
|
||||
<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="%@. 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>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs" xml:space="preserve">
|
||||
<source>%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs</source>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet." xml:space="preserve">
|
||||
<source>%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet.</source>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld" xml:space="preserve">
|
||||
<source>%lld</source>
|
||||
<note>Number of profiles a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld/%lld" xml:space="preserve">
|
||||
<source>%lld/%lld</source>
|
||||
<note>Fraction of how many of the user's relay servers that are operational.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="&nbsp;" xml:space="preserve">
|
||||
<source>&nbsp;</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="'%@' at '%@' will be used for verification" xml:space="preserve">
|
||||
<source>'%@' at '%@' will be used for verification</source>
|
||||
<note>Description of how the nip05 identifier would be used for verification.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
|
||||
<source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
|
||||
<note>Description of why the nip05 identifier is invalid.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(Profile.displayName(profile: profile, pubkey: whos))'s Followers" xml:space="preserve">
|
||||
<source>(Profile.displayName(profile: profile, pubkey: whos))'s Followers</source>
|
||||
<note>Navigation bar title for view that shows who is following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(formattedSats) sat" xml:space="preserve">
|
||||
<source>(formattedSats) sat</source>
|
||||
<note>Amount of 1 sat.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(formattedSats) sats" xml:space="preserve">
|
||||
<source>(formattedSats) sats</source>
|
||||
<note>Amount of sats.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(who) following" xml:space="preserve">
|
||||
<source>(who) following</source>
|
||||
<note>Navigation bar title for view that shows who a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="< e >" xml:space="preserve">
|
||||
<source>< e ></source>
|
||||
<note>Placeholder for event mention.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="@" xml:space="preserve">
|
||||
<source>@</source>
|
||||
<note>Prefix character to username.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About" xml:space="preserve">
|
||||
<source>About</source>
|
||||
<note>Label to prompt for about text entry for user to describe about themself.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About Me" xml:space="preserve">
|
||||
<source>About Me</source>
|
||||
<note>Label for About Me section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Absolute Boss" xml:space="preserve">
|
||||
<source>Absolute Boss</source>
|
||||
<note>Placeholder text for About Me description.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Account ID" xml:space="preserve">
|
||||
<source>Account ID</source>
|
||||
<note>Label to indicate the public ID of the account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add" xml:space="preserve">
|
||||
<source>Add</source>
|
||||
<note>Button to add recommended relay server.
|
||||
Button to confirm adding user inputted relay.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add Relay" xml:space="preserve">
|
||||
<source>Add Relay</source>
|
||||
<note>Label for section for adding a relay server.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Any" xml:space="preserve">
|
||||
<source>Any</source>
|
||||
<note>Any amount of sats</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Are you sure you want to boost this post?" xml:space="preserve">
|
||||
<source>Are you sure you want to boost this post?</source>
|
||||
<note>Alert message to ask if user wants to boost a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="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." xml:space="preserve">
|
||||
<source>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.</source>
|
||||
<note>Reminder to user that they should save their account information.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bitcoin Beach" xml:space="preserve">
|
||||
<source>Bitcoin Beach</source>
|
||||
<note>Dropdown option label for Lightning wallet, Bitcoin Beach.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bitcoin Lightning Tips" xml:space="preserve">
|
||||
<source>Bitcoin Lightning Tips</source>
|
||||
<note>Label for Bitcoin Lightning Tips section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Blixt Wallet" xml:space="preserve">
|
||||
<source>Blixt Wallet</source>
|
||||
<note>Dropdown option label for Lightning wallet, Blixt Wallet</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Blue Wallet" xml:space="preserve">
|
||||
<source>Blue Wallet</source>
|
||||
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Boost" xml:space="preserve">
|
||||
<source>Boost</source>
|
||||
<note>Button to confirm boosting a post.
|
||||
Title of alert for confirming to boost a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Boosted" xml:space="preserve">
|
||||
<source>Boosted</source>
|
||||
<note>Text indicating that the post was boosted (i.e. re-shared).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Breez" xml:space="preserve">
|
||||
<source>Breez</source>
|
||||
<note>Dropdown option label for Lightning wallet, Breez.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Broadcast" xml:space="preserve">
|
||||
<source>Broadcast</source>
|
||||
<note>Context menu option for broadcasting the user's note to all of the user's connected relay servers.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<note>Button to cancel out of posting a note.
|
||||
Button to cancel out of view adding user inputted relay.
|
||||
Cancel out of logging out the user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cash App" xml:space="preserve">
|
||||
<source>Cash App</source>
|
||||
<note>Dropdown option label for Lightning wallet, Cash App.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat" xml:space="preserve">
|
||||
<source>Chat</source>
|
||||
<note>Navigation bar title for Chatroom view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Clear" xml:space="preserve">
|
||||
<source>Clear</source>
|
||||
<note>Button for clearing cached data.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Clear Cache" xml:space="preserve">
|
||||
<source>Clear Cache</source>
|
||||
<note>Section title for clearing cached data.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copied" xml:space="preserve">
|
||||
<source>Copied</source>
|
||||
<note>Label indicating that a user's key was copied.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<note>Button to copy a relay server address.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Account ID" xml:space="preserve">
|
||||
<source>Copy Account ID</source>
|
||||
<note>Context menu option for copying the ID of the account that created the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Image" xml:space="preserve">
|
||||
<source>Copy Image</source>
|
||||
<note>Context menu option to copy an image into clipboard.
|
||||
Context menu option to copy an image to clipboard.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Image URL" xml:space="preserve">
|
||||
<source>Copy Image URL</source>
|
||||
<note>Context menu option to copy the URL of an image into clipboard.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy LNURL" xml:space="preserve">
|
||||
<source>Copy LNURL</source>
|
||||
<note>Context menu option for copying a user's Lightning URL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Note ID" xml:space="preserve">
|
||||
<source>Copy Note ID</source>
|
||||
<note>Context menu option for copying the ID of the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Note JSON" xml:space="preserve">
|
||||
<source>Copy Note JSON</source>
|
||||
<note>Context menu option for copying the JSON text from the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Text" xml:space="preserve">
|
||||
<source>Copy Text</source>
|
||||
<note>Context menu option for copying the text from an note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy User ID" xml:space="preserve">
|
||||
<source>Copy User ID</source>
|
||||
<note>Context menu option for copying the ID of the user who created the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy invoice" xml:space="preserve">
|
||||
<source>Copy invoice</source>
|
||||
<note>Title of section for copying a Lightning invoice identifier.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create" xml:space="preserve">
|
||||
<source>Create</source>
|
||||
<note>Button to create account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create Account" xml:space="preserve">
|
||||
<source>Create Account</source>
|
||||
<note>Button to create an account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creator(s) of Bitcoin. Absolute legend." xml:space="preserve">
|
||||
<source>Creator(s) of Bitcoin. Absolute legend.</source>
|
||||
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="DM" xml:space="preserve">
|
||||
<source>DM</source>
|
||||
<note>Navigation title for DM view, which is the English abbreviation for Direct Message.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Damus" xml:space="preserve">
|
||||
<source>Damus</source>
|
||||
<note>Name of the app, shown on the first screen when user is not logged in.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Default Wallet" xml:space="preserve">
|
||||
<source>Default Wallet</source>
|
||||
<note>Button to pay a Lightning invoice with the user's default Lightning wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete" xml:space="preserve">
|
||||
<source>Delete</source>
|
||||
<note>Button to delete a relay server that the user connects to.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Dismiss" xml:space="preserve">
|
||||
<source>Dismiss</source>
|
||||
<note>Button to dismiss a text field alert.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Display Name" xml:space="preserve">
|
||||
<source>Display Name</source>
|
||||
<note>Label to prompt display name entry.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Done" xml:space="preserve">
|
||||
<source>Done</source>
|
||||
<note>Button to dismiss wallet selection view for paying Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Earn Money" xml:space="preserve">
|
||||
<source>Earn Money</source>
|
||||
<note>Heading indicating that this application allows users to earn money.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit" xml:space="preserve">
|
||||
<source>Edit</source>
|
||||
<note>Button to edit user's profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit Profile" xml:space="preserve">
|
||||
<source>Edit Profile</source>
|
||||
<note>Title of navigation view for Edit Profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted" xml:space="preserve">
|
||||
<source>Encrypted</source>
|
||||
<note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted DMs" xml:space="preserve">
|
||||
<source>Encrypted DMs</source>
|
||||
<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">
|
||||
<source>Enter your account key to login:</source>
|
||||
<note>Prompt for user to enter an account key to login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: %@" xml:space="preserve">
|
||||
<source>Error: %@</source>
|
||||
<note>Error message indicating why saving keys failed.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Filter State" xml:space="preserve">
|
||||
<source>Filter State</source>
|
||||
<note>Filter state for seeing either only posts, or posts & replies.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follow" xml:space="preserve">
|
||||
<source>Follow</source>
|
||||
<note>Button to follow a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Followers" xml:space="preserve">
|
||||
<source>Followers</source>
|
||||
<note>Label describing followers of a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Following" xml:space="preserve">
|
||||
<source>Following</source>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Following..." xml:space="preserve">
|
||||
<source>Following...</source>
|
||||
<note>Label to indicate that the user is in the process of following another user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follows" xml:space="preserve">
|
||||
<source>Follows</source>
|
||||
<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 id="Global" xml:space="preserve">
|
||||
<source>Global</source>
|
||||
<note>Navigation bar title for Global view where posts from all connected relay servers appear.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Goto post %@" xml:space="preserve">
|
||||
<source>Goto post %@</source>
|
||||
<note>Navigation link to go to post referenced by hex code.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Goto profile %@" xml:space="preserve">
|
||||
<source>Goto profile %@</source>
|
||||
<note>Navigation link to go to profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Home" xml:space="preserve">
|
||||
<source>Home</source>
|
||||
<note>Navigation bar title for Home view where posts and replies appear from those who the user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid key" xml:space="preserve">
|
||||
<source>Invalid key</source>
|
||||
<note>Error message indicating that an invalid account key was entered for login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="LNLink" xml:space="preserve">
|
||||
<source>LNLink</source>
|
||||
<note>Dropdown option label for Lightning wallet, LNLink.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Let's go!" xml:space="preserve">
|
||||
<source>Let's go!</source>
|
||||
<note>Button to complete account creation and start using the app.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lightning Address or LNURL" xml:space="preserve">
|
||||
<source>Lightning Address or LNURL</source>
|
||||
<note>Placeholder text for entry of Lightning Address or LNURL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lightning Invoice" xml:space="preserve">
|
||||
<source>Lightning Invoice</source>
|
||||
<note>Indicates that the view is for paying a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local default" xml:space="preserve">
|
||||
<source>Local default</source>
|
||||
<note>Dropdown option label for system default for Lightning wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Login" xml:space="preserve">
|
||||
<source>Login</source>
|
||||
<note>Button to log into account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Logout" xml:space="preserve">
|
||||
<source>Logout</source>
|
||||
<note>Alert for logging out the user.
|
||||
Button for logging out the user.
|
||||
Button to logout the user.</note>
|
||||
</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">
|
||||
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
|
||||
<note>Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Muun" xml:space="preserve">
|
||||
<source>Muun</source>
|
||||
<note>Dropdown option label for Lightning wallet, Muun.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NIP-05 Verification" xml:space="preserve">
|
||||
<source>NIP-05 Verification</source>
|
||||
<note>Label for NIP-05 Verification section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
|
||||
<source>Nothing to see here. Check back later!</source>
|
||||
<note>Indicates that there are no notes in the timeline to view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications" xml:space="preserve">
|
||||
<source>Notifications</source>
|
||||
<note>Navigation title for notifications.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Pay" xml:space="preserve">
|
||||
<source>Pay</source>
|
||||
<note>Button to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Pay the Lightning invoice" xml:space="preserve">
|
||||
<source>Pay the Lightning invoice</source>
|
||||
<note>Navigation bar title for view to pay Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Phoenix" xml:space="preserve">
|
||||
<source>Phoenix</source>
|
||||
<note>Dropdown option label for Lightning wallet, Phoenix.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Post" xml:space="preserve">
|
||||
<source>Post</source>
|
||||
<note>Button to post a note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Posts" xml:space="preserve">
|
||||
<source>Posts</source>
|
||||
<note>Label for filter for seeing only posts (instead of posts and replies).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Posts & Replies" xml:space="preserve">
|
||||
<source>Posts & Replies</source>
|
||||
<note>Label for filter for seeing posts and replies (instead of only posts).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private" xml:space="preserve">
|
||||
<source>Private</source>
|
||||
<note>Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private Key" xml:space="preserve">
|
||||
<source>Private Key</source>
|
||||
<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 id="PrivateKey" xml:space="preserve">
|
||||
<source>PrivateKey</source>
|
||||
<note>Title of the secure field that holds the user's private key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile Picture" xml:space="preserve">
|
||||
<source>Profile Picture</source>
|
||||
<note>Label for Profile Picture section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Account ID" xml:space="preserve">
|
||||
<source>Public Account ID</source>
|
||||
<note>Section title for the user's public account ID.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Key" xml:space="preserve">
|
||||
<source>Public Key</source>
|
||||
<note>Label indicating that the text is a user's public account key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Key?" xml:space="preserve">
|
||||
<source>Public Key?</source>
|
||||
<note>Prompt to ask user if the key they entered is a public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public key" xml:space="preserve">
|
||||
<source>Public key</source>
|
||||
<note>Label indicating that the text is a user's public account key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recommended Relays" xml:space="preserve">
|
||||
<source>Recommended Relays</source>
|
||||
<note>Section title for recommend relay servers that could be added as part of configuration</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Relay" xml:space="preserve">
|
||||
<source>Relay</source>
|
||||
<note>Text field for relay server. Used for testing purposes.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Relays" xml:space="preserve">
|
||||
<source>Relays</source>
|
||||
<note>Header text for relay server list for configuration.
|
||||
Part of a larger sentence to describe how many relay servers a user is connected.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply to self" xml:space="preserve">
|
||||
<source>Reply to self</source>
|
||||
<note>Label to indicate that the user is replying to themself.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Replying to %@ & %@" xml:space="preserve">
|
||||
<source>Replying to %1$@ & %2$@</source>
|
||||
<note>Label to indicate that the user is replying to 2 users.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Replying to:" xml:space="preserve">
|
||||
<source>Replying to:</source>
|
||||
<note>Indicating that the user is replying to the following listed people.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reset" xml:space="preserve">
|
||||
<source>Reset</source>
|
||||
<note>Section title for resetting the user</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="River" xml:space="preserve">
|
||||
<source>River</source>
|
||||
<note>Dropdown option label for Lightning wallet, River</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Satoshi Nakamoto" xml:space="preserve">
|
||||
<source>Satoshi Nakamoto</source>
|
||||
<note>Name of Bitcoin creator(s).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save" xml:space="preserve">
|
||||
<source>Save</source>
|
||||
<note>Button for saving profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save Image" xml:space="preserve">
|
||||
<source>Save Image</source>
|
||||
<note>Context menu option to save an image.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search hashtag: #%@" xml:space="preserve">
|
||||
<source>Search hashtag: #%@</source>
|
||||
<note>Navigation link to search hashtag.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search..." xml:space="preserve">
|
||||
<source>Search...</source>
|
||||
<note>Placeholder text to prompt entry of search query.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secret Account Login Key" xml:space="preserve">
|
||||
<source>Secret Account Login Key</source>
|
||||
<note>Section title for user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select a Lightning wallet" xml:space="preserve">
|
||||
<source>Select a Lightning wallet</source>
|
||||
<note>Title of section for selecting a Lightning wallet to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select default wallet" xml:space="preserve">
|
||||
<source>Select default wallet</source>
|
||||
<note>Prompt selection of user's default wallet</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send a message to start the conversation..." xml:space="preserve">
|
||||
<source>Send a message to start the conversation...</source>
|
||||
<note>Text prompt for user to send a message to the other user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Settings" xml:space="preserve">
|
||||
<source>Settings</source>
|
||||
<note>Navigation title for Settings view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share" xml:space="preserve">
|
||||
<source>Share</source>
|
||||
<note>Button to share an image.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show" xml:space="preserve">
|
||||
<source>Show</source>
|
||||
<note>Toggle to show or hide user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show wallet selector" xml:space="preserve">
|
||||
<source>Show wallet selector</source>
|
||||
<note>Toggle to show or hide selection of wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Strike" xml:space="preserve">
|
||||
<source>Strike</source>
|
||||
<note>Dropdown option label for Lightning wallet, Strike.</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">
|
||||
<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>
|
||||
<note>Warning that the inputted account key is a public key and the result of what happens because of it.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="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." xml:space="preserve">
|
||||
<source>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.</source>
|
||||
<note>Warning that the inputted account key for login is an old-style and asking user to verify if it is a public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is your account ID, you can give this to your friends so that they can follow you. Click to copy." xml:space="preserve">
|
||||
<source>This is your account ID, you can give this to your friends so that they can follow you. Click to copy.</source>
|
||||
<note>Label to describe that a public key is the user's account ID and what they can do with it.</note>
|
||||
</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">
|
||||
<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>
|
||||
<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 id="Thread" xml:space="preserve">
|
||||
<source>Thread</source>
|
||||
<note>Navigation bar title for note thread.
|
||||
Navigation bar title for threaded event detail view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Type your post here..." xml:space="preserve">
|
||||
<source>Type your post here...</source>
|
||||
<note>Text box prompt to ask user to type their post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollow" xml:space="preserve">
|
||||
<source>Unfollow</source>
|
||||
<note>Button to unfollow a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollowing" xml:space="preserve">
|
||||
<source>Unfollowing</source>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollowing..." xml:space="preserve">
|
||||
<source>Unfollowing...</source>
|
||||
<note>Label to indicate that the user is in the process of unfollowing another user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollows" xml:space="preserve">
|
||||
<source>Unfollows</source>
|
||||
<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 id="Username" xml:space="preserve">
|
||||
<source>Username</source>
|
||||
<note>Label for Username section of user profile form.
|
||||
Label to prompt username entry.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
|
||||
<source>Wallet Of Satoshi</source>
|
||||
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wallet Selector" xml:space="preserve">
|
||||
<source>Wallet Selector</source>
|
||||
<note>Section title for selection of wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Website" xml:space="preserve">
|
||||
<source>Website</source>
|
||||
<note>Label for Website section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome to the social network %@ control." xml:space="preserve">
|
||||
<source>Welcome to the social network %@ control.</source>
|
||||
<note>Welcoming message to the reader. The variable is 'you', the reader.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome, %@!" xml:space="preserve">
|
||||
<source>Welcome, %@!</source>
|
||||
<note>Text to welcome user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your Name" xml:space="preserve">
|
||||
<source>Your Name</source>
|
||||
<note>Label for Your Name section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Zebedee" xml:space="preserve">
|
||||
<source>Zebedee</source>
|
||||
<note>Dropdown option label for Lightning wallet, Zebedee.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Zeus LN" xml:space="preserve">
|
||||
<source>Zeus LN</source>
|
||||
<note>Dropdown option label for Lightning wallet, Zeus LN.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve">
|
||||
<source>collapsed_event_view_other_notes</source>
|
||||
<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 id="https://example.com/pic.jpg" xml:space="preserve">
|
||||
<source>https://example.com/pic.jpg</source>
|
||||
<note>Placeholder example text for profile picture URL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="https://jb55.com" xml:space="preserve">
|
||||
<source>https://jb55.com</source>
|
||||
<note>Placeholder example text for website URL for user profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="jb55@jb55.com" xml:space="preserve">
|
||||
<source>jb55@jb55.com</source>
|
||||
<note>Placeholder example text for identifier used for NIP-05 verification.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="none" xml:space="preserve">
|
||||
<source>none</source>
|
||||
<note>No search results.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="now" xml:space="preserve">
|
||||
<source>now</source>
|
||||
<note>String indicating that a given timestamp just occurred</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="nsec1..." xml:space="preserve">
|
||||
<source>nsec1...</source>
|
||||
<note>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.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="optional" xml:space="preserve">
|
||||
<source>optional</source>
|
||||
<note>Label indicating that a form input is optional.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="replying_to_one_and_others" translate="no" xml:space="preserve">
|
||||
<source>replying_to_one_and_others</source>
|
||||
<note>Label to indicate that the user is replying to 1 user and others. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="replying_to_two_and_others" translate="no" xml:space="preserve">
|
||||
<source>replying_to_two_and_others</source>
|
||||
<note>Label to indicate that the user is replying to 2 users and others. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="satoshi" xml:space="preserve">
|
||||
<source>satoshi</source>
|
||||
<note>Example username of Bitcoin creator(s), Satoshi Nakamoto.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="wss://some.relay.com" xml:space="preserve">
|
||||
<source>wss://some.relay.com</source>
|
||||
<note>Placeholder example for relay server address.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<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 id="🤙" xml:space="preserve">
|
||||
<source>🤙</source>
|
||||
<note>Button with emoji to like an event.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="es-419" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>1 other note</source>
|
||||
<target>1 other note</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>%d other notes</source>
|
||||
<target>%d other notes</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source>0 other notes</source>
|
||||
<target>0 other notes</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>··· %#@NOTES@ ···</source>
|
||||
<target>··· %#@NOTES@ ···</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>Replying to %@%#@OTHERS@</source>
|
||||
<target>Replying to %@%#@OTHERS@</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source> & 1 other</source>
|
||||
<target> & 1 other</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source> & %d others</source>
|
||||
<target> & %d others</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source/>
|
||||
<target/>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>Replying to %@, %@%#@OTHERS@</source>
|
||||
<target>Replying to %@, %@%#@OTHERS@</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source> & 1 other</source>
|
||||
<target> & 1 other</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source> & %d others</source>
|
||||
<target> & %d others</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source/>
|
||||
<target/>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
@@ -5,8 +5,8 @@
|
||||
<key>replying_to_one_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Replying to %@%#@others@</string>
|
||||
<key>others</key>
|
||||
<string>Replying to %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
@@ -23,8 +23,8 @@
|
||||
<key>replying_to_two_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Replying to %@, %@%#@others@</string>
|
||||
<key>others</key>
|
||||
<string>Replying to %@, %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
@@ -38,5 +38,23 @@
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>collapsed_event_view_other_notes</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>··· %#@NOTES@ ···</string>
|
||||
<key>NOTES</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>zero</key>
|
||||
<string>0 other notes</string>
|
||||
<key>one</key>
|
||||
<string>1 other note</string>
|
||||
<key>other</key>
|
||||
<string>%d other notes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
12
damus Localizations/es-419.xcloc/contents.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"developmentRegion" : "en-US",
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "es-419",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "14C18",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "14.2"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -12,8 +12,6 @@
|
||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
|
||||
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
||||
3AB18056296375CA00FD1BD8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AB18052296375CA00FD1BD8 /* InfoPlist.strings */; };
|
||||
3AB18057296375CA00FD1BD8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AB18054296375CA00FD1BD8 /* Localizable.strings */; };
|
||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
||||
@@ -53,6 +51,7 @@
|
||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA328296DEE006E126D /* SearchModel.swift */; };
|
||||
4C363AA828297703006E126D /* InsertSort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA728297703006E126D /* InsertSort.swift */; };
|
||||
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D322960DB0500558C0F /* Markdown.swift */; };
|
||||
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D3629637E0500558C0F /* PreviewCache.swift */; };
|
||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79A28306D7B00E1F516 /* Contacts.swift */; };
|
||||
4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79C2833036D00E1F516 /* FollowingView.swift */; };
|
||||
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79E2833115300E1F516 /* FollowButtonView.swift */; };
|
||||
@@ -116,6 +115,7 @@
|
||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
|
||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; };
|
||||
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */; };
|
||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838529656C8B00DC99E7 /* NIP05.swift */; };
|
||||
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
||||
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
|
||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
|
||||
@@ -138,9 +138,11 @@
|
||||
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; };
|
||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
|
||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
@@ -167,13 +169,8 @@
|
||||
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
|
||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
|
||||
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
|
||||
3A4325A92961E11400BFCD9D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
3AB1803D29636FB100FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
3AB18058296377E500FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3AB18059296377E700FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3AB1805A2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3AB1805B2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3AB1805C2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.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>"; };
|
||||
3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.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>"; };
|
||||
@@ -215,6 +212,7 @@
|
||||
4C363AA328296DEE006E126D /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; };
|
||||
4C363AA728297703006E126D /* InsertSort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertSort.swift; sourceTree = "<group>"; };
|
||||
4C3A1D322960DB0500558C0F /* Markdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Markdown.swift; sourceTree = "<group>"; };
|
||||
4C3A1D3629637E0500558C0F /* PreviewCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCache.swift; sourceTree = "<group>"; };
|
||||
4C3AC79A28306D7B00E1F516 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = "<group>"; };
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowButtonView.swift; sourceTree = "<group>"; };
|
||||
@@ -308,6 +306,7 @@
|
||||
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>"; };
|
||||
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRelaysView.swift; sourceTree = "<group>"; };
|
||||
4CB8838529656C8B00DC99E7 /* NIP05.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP05.swift; sourceTree = "<group>"; };
|
||||
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
|
||||
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
|
||||
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
|
||||
@@ -333,8 +332,10 @@
|
||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = "<group>"; };
|
||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; };
|
||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
||||
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -469,48 +470,48 @@
|
||||
4C75EFA227FA576C0006080F /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3169CAE4294E699400EE4006 /* Empty Views */,
|
||||
4C75EFA327FA577B0006080F /* PostView.swift */,
|
||||
4C75EFAC28049CFB0006080F /* PostButton.swift */,
|
||||
4C75EFB82804A2740006080F /* EventView.swift */,
|
||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
|
||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
|
||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
|
||||
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
|
||||
4C363A8728236948006E126D /* BlocksView.swift */,
|
||||
4C285C8128385570008A31F1 /* CarouselView.swift */,
|
||||
4C0A3F8B280F5FCA000448DE /* ChatroomView.swift */,
|
||||
4C0A3F90280F6528000448DE /* ChatView.swift */,
|
||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||
4C363A8728236948006E126D /* BlocksView.swift */,
|
||||
4C363A8928236B57006E126D /* MentionView.swift */,
|
||||
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
||||
4C363A8D28236FE4006E126D /* NoteContentView.swift */,
|
||||
4C363AA128296A7E006E126D /* SearchView.swift */,
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
||||
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
|
||||
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
||||
4C3AC7A628369BA200E1F516 /* SearchHomeView.swift */,
|
||||
4C285C8128385570008A31F1 /* CarouselView.swift */,
|
||||
4C285C8328385690008A31F1 /* CreateAccountView.swift */,
|
||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
|
||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
||||
4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */,
|
||||
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */,
|
||||
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
|
||||
4C285C8328385690008A31F1 /* CreateAccountView.swift */,
|
||||
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */,
|
||||
4C216F31286E388800040376 /* DMChatView.swift */,
|
||||
4C216F33286F5ACD00040376 /* DMView.swift */,
|
||||
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
||||
3169CAE4294E699400EE4006 /* Empty Views */,
|
||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
|
||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||
4C75EFB82804A2740006080F /* EventView.swift */,
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
||||
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
||||
4C363A8928236B57006E126D /* MentionView.swift */,
|
||||
4C363A8D28236FE4006E126D /* NoteContentView.swift */,
|
||||
4C75EFAC28049CFB0006080F /* PostButton.swift */,
|
||||
4C75EFA327FA577B0006080F /* PostView.swift */,
|
||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
|
||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
||||
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
||||
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
|
||||
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
|
||||
4C3AC7A628369BA200E1F516 /* SearchHomeView.swift */,
|
||||
4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */,
|
||||
4C363AA128296A7E006E126D /* SearchView.swift */,
|
||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
||||
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
|
||||
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
@@ -550,6 +551,9 @@
|
||||
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
|
||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
|
||||
3165648A295B70D500C64604 /* LinkView.swift */,
|
||||
4C3A1D3629637E0500558C0F /* PreviewCache.swift */,
|
||||
64FBD06E296255C400D9D3B2 /* Theme.swift */,
|
||||
4CB8838529656C8B00DC99E7 /* NIP05.swift */,
|
||||
);
|
||||
path = Util;
|
||||
sourceTree = "<group>";
|
||||
@@ -603,8 +607,6 @@
|
||||
4CE6DEE827F7A08100C66700 /* ContentView.swift */,
|
||||
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
|
||||
4CE6DEEC27F7A08200C66700 /* Preview Content */,
|
||||
3AB18052296375CA00FD1BD8 /* InfoPlist.strings */,
|
||||
3AB18054296375CA00FD1BD8 /* Localizable.strings */,
|
||||
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
|
||||
);
|
||||
path = damus;
|
||||
@@ -621,6 +623,7 @@
|
||||
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */,
|
||||
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */,
|
||||
4C363A9F2828A8DD006E126D /* LikeTests.swift */,
|
||||
4C363A9D2828A822006E126D /* ReplyTests.swift */,
|
||||
@@ -736,13 +739,12 @@
|
||||
};
|
||||
buildConfigurationList = 4CE6DEDE27F7A08100C66700 /* Build configuration list for PBXProject "damus" */;
|
||||
compatibilityVersion = "Xcode 13.0";
|
||||
developmentRegion = en;
|
||||
developmentRegion = "en-US";
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
es,
|
||||
fr,
|
||||
"es-419",
|
||||
"en-US",
|
||||
);
|
||||
mainGroup = 4CE6DEDA27F7A08100C66700;
|
||||
packageReferences = (
|
||||
@@ -768,9 +770,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3AB18057296375CA00FD1BD8 /* Localizable.strings in Resources */,
|
||||
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */,
|
||||
3AB18056296375CA00FD1BD8 /* InfoPlist.strings in Resources */,
|
||||
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */,
|
||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */,
|
||||
);
|
||||
@@ -854,8 +854,10 @@
|
||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
||||
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
||||
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
|
||||
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */,
|
||||
4C3EA67528FF7A5A00C48A62 /* take.c in Sources */,
|
||||
4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */,
|
||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */,
|
||||
@@ -877,6 +879,7 @@
|
||||
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
||||
4C8682872814DE470026224F /* ProfileView.swift in Sources */,
|
||||
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
|
||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
||||
@@ -924,6 +927,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
||||
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
|
||||
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
|
||||
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
||||
@@ -960,31 +964,12 @@
|
||||
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3A4325A92961E11400BFCD9D /* en */,
|
||||
3AB1803D29636FB100FD1BD8 /* es */,
|
||||
3AB1805C2963EF7E00FD1BD8 /* fr */,
|
||||
3A5C4575296A879E0032D398 /* es-419 */,
|
||||
3A2B8B0A296A8982009CC16D /* en-US */,
|
||||
);
|
||||
name = Localizable.stringsdict;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3AB18052296375CA00FD1BD8 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3AB18058296377E500FD1BD8 /* es */,
|
||||
3AB1805A2963EF7E00FD1BD8 /* fr */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3AB18054296375CA00FD1BD8 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3AB18059296377E700FD1BD8 /* es */,
|
||||
3AB1805B2963EF7E00FD1BD8 /* fr */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -1046,6 +1031,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1101,6 +1087,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -1114,7 +1101,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1154,7 +1141,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
||||
6
damus/Assets.xcassets/Colors/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x1C",
|
||||
"red" : "0x1C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x1C",
|
||||
"red" : "0x1C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4F",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x66"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4F",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x66"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5F",
|
||||
"green" : "0x5F",
|
||||
"red" : "0x5F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5F",
|
||||
"green" : "0x5F",
|
||||
"red" : "0x5F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
damus/Assets.xcassets/Profile/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
21
damus/Assets.xcassets/Profile/ic-copy.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-copy.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-copy.imageset/ic-copy.png
vendored
Normal file
|
After Width: | Height: | Size: 354 B |
21
damus/Assets.xcassets/Profile/ic-key.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-key.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-key.imageset/ic-key.png
vendored
Normal file
|
After Width: | Height: | Size: 400 B |
52
damus/Assets.xcassets/Profile/ic-message.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-message-black.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "ic-message-white 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-message.imageset/ic-message-black.png
vendored
Normal file
|
After Width: | Height: | Size: 321 B |
BIN
damus/Assets.xcassets/Profile/ic-message.imageset/ic-message-white 1.png
vendored
Normal file
|
After Width: | Height: | Size: 341 B |
21
damus/Assets.xcassets/Profile/ic-nipverified.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-nipverified.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-nipverified.imageset/ic-nipverified.png
vendored
Normal file
|
After Width: | Height: | Size: 950 B |
21
damus/Assets.xcassets/Profile/ic-qr.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-qr.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-qr.imageset/ic-qr.png
vendored
Normal file
|
After Width: | Height: | Size: 252 B |
21
damus/Assets.xcassets/Profile/profile-banner.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "profile-banner.jpeg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/profile-banner.imageset/profile-banner.jpeg
vendored
Normal file
|
After Width: | Height: | Size: 290 KiB |
21
damus/Assets.xcassets/ic-lightning.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-lightning.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/ic-lightning.imageset/ic-lightning.png
vendored
Normal file
|
After Width: | Height: | Size: 458 B |
21
damus/Assets.xcassets/ic-tick.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-tick.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/ic-tick.imageset/ic-tick.png
vendored
Normal file
|
After Width: | Height: | Size: 671 B |
23
damus/Assets.xcassets/shaka.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "nostr-hello-outline-black.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "nostr-hello-outline-black@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "nostr-hello-outline-black@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/shaka.imageset/nostr-hello-outline-black.png
vendored
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
damus/Assets.xcassets/shaka.imageset/nostr-hello-outline-black@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 723 B |
BIN
damus/Assets.xcassets/shaka.imageset/nostr-hello-outline-black@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@@ -41,19 +41,24 @@ struct ImageContextMenuModifier: ViewModifier {
|
||||
Button {
|
||||
UIPasteboard.general.url = url
|
||||
} label: {
|
||||
Label("Copy Image URL", systemImage: "doc.on.doc")
|
||||
Label(NSLocalizedString("Copy Image URL", comment: "Context menu option to copy the URL of an image into clipboard."), systemImage: "doc.on.doc")
|
||||
}
|
||||
if let someImage = image {
|
||||
Button {
|
||||
UIPasteboard.general.image = someImage
|
||||
} label: {
|
||||
Label("Copy Image", systemImage: "photo.on.rectangle")
|
||||
Label(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image into clipboard."), systemImage: "photo.on.rectangle")
|
||||
}
|
||||
Button {
|
||||
UIImageWriteToSavedPhotosAlbum(someImage, nil, nil, nil)
|
||||
} label: {
|
||||
Label(NSLocalizedString("Save Image", comment: "Context menu option to save an image."), systemImage: "square.and.arrow.down")
|
||||
}
|
||||
}
|
||||
Button {
|
||||
showShareSheet = true
|
||||
} label: {
|
||||
Label("Share", systemImage: "square.and.arrow.up")
|
||||
Label(NSLocalizedString("Share", comment: "Button to share an image."), systemImage: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +143,7 @@ struct ImageCarousel: View {
|
||||
}
|
||||
.id(url.absoluteString)
|
||||
.contextMenu {
|
||||
Button("Copy Image") {
|
||||
Button(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image to clipboard.")) {
|
||||
UIPasteboard.general.string = url.absoluteString
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ struct InvoiceView: View {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.foregroundColor(colorScheme == .light ? .black : .white)
|
||||
.overlay {
|
||||
Text("Pay")
|
||||
Text("Pay", comment: "Button to pay a Lightning invoice.")
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(colorScheme == .light ? .white : .black)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ struct InvoiceView: View {
|
||||
HStack {
|
||||
Label("", systemImage: "bolt.fill")
|
||||
.foregroundColor(.orange)
|
||||
Text("Lightning Invoice")
|
||||
Text("Lightning Invoice", comment: "Indicates that the view is for paying a Lightning invoice.")
|
||||
}
|
||||
Divider()
|
||||
Text(invoice.description)
|
||||
|
||||
@@ -20,7 +20,7 @@ struct TextFieldAlert<Presenting>: View where Presenting: View {
|
||||
.disabled(isShowing)
|
||||
VStack {
|
||||
Text(self.title)
|
||||
TextField("Relay", text: self.$text)
|
||||
TextField(NSLocalizedString("Relay", comment: "Text field for relay server. Used for testing purposes."), text: self.$text)
|
||||
Divider()
|
||||
HStack {
|
||||
Button(action: {
|
||||
@@ -28,7 +28,7 @@ struct TextFieldAlert<Presenting>: View where Presenting: View {
|
||||
self.isShowing.toggle()
|
||||
}
|
||||
}) {
|
||||
Text("Dismiss")
|
||||
Text("Dismiss", comment: "Button to dismiss a text field alert.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,15 @@ enum ThreadState {
|
||||
enum FilterState : Int {
|
||||
case posts_and_replies = 1
|
||||
case posts = 0
|
||||
|
||||
func filter(ev: NostrEvent) -> Bool {
|
||||
switch self {
|
||||
case .posts:
|
||||
return !ev.is_reply(nil)
|
||||
case .posts_and_replies:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@@ -84,10 +93,12 @@ struct ContentView: View {
|
||||
var PostingTimelineView: some View {
|
||||
VStack {
|
||||
TabView(selection: $filter_state) {
|
||||
ContentTimelineView
|
||||
contentTimelineView(filter: FilterState.posts.filter)
|
||||
.tag(FilterState.posts)
|
||||
ContentTimelineView
|
||||
.id(FilterState.posts)
|
||||
contentTimelineView(filter: FilterState.posts_and_replies.filter)
|
||||
.tag(FilterState.posts_and_replies)
|
||||
.id(FilterState.posts_and_replies)
|
||||
}
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
}
|
||||
@@ -101,12 +112,13 @@ struct ContentView: View {
|
||||
}
|
||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||
}
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
|
||||
var ContentTimelineView: some View {
|
||||
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
||||
ZStack {
|
||||
if let damus = self.damus_state {
|
||||
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
|
||||
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter)
|
||||
}
|
||||
if privkey != nil {
|
||||
PostButtonContainer {
|
||||
@@ -118,22 +130,14 @@ struct ContentView: View {
|
||||
|
||||
var FiltersView: some View {
|
||||
VStack{
|
||||
Picker("Filter State", selection: $filter_state) {
|
||||
Text("Posts").tag(FilterState.posts)
|
||||
Text("Posts & Replies").tag(FilterState.posts_and_replies)
|
||||
Picker(NSLocalizedString("Filter State", comment: "Filter state for seeing either only posts, or posts & replies."), selection: $filter_state) {
|
||||
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 filter_event(_ ev: NostrEvent) -> Bool {
|
||||
if self.filter_state == .posts {
|
||||
return !ev.is_reply(nil)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func MainContent(damus: DamusState) -> some View {
|
||||
VStack {
|
||||
NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
|
||||
@@ -154,7 +158,7 @@ struct ContentView: View {
|
||||
|
||||
case .notifications:
|
||||
TimelineView(events: $home.notifications, loading: $home.loading, damus: damus, show_friend_icon: true, filter: { _ in true })
|
||||
.navigationTitle("Notifications")
|
||||
.navigationTitle(NSLocalizedString("Notifications", comment: "Navigation title for notifications."))
|
||||
|
||||
case .dms:
|
||||
DirectMessagesView(damus_state: damus_state!)
|
||||
@@ -164,7 +168,7 @@ struct ContentView: View {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.navigationBarTitle(selected_timeline == .home ? "Home" : "Global", displayMode: .inline)
|
||||
.navigationBarTitle(selected_timeline == .home ? NSLocalizedString("Home", comment: "Navigation bar title for Home view where posts and replies appear from those who the user is following.") : NSLocalizedString("Global", comment: "Navigation bar title for Global view where posts from all connected relay servers appear."), displayMode: .inline)
|
||||
}
|
||||
|
||||
var MaybeSearchView: some View {
|
||||
@@ -225,7 +229,7 @@ struct ContentView: View {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack(alignment: .center) {
|
||||
if home.signal.signal != home.signal.max_signal {
|
||||
Text("\(home.signal.signal)/\(home.signal.max_signal)")
|
||||
Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
@@ -411,10 +415,11 @@ struct ContentView: View {
|
||||
self.damus_state = DamusState(pool: pool, keypair: keypair,
|
||||
likes: EventCounter(our_pubkey: pubkey),
|
||||
boosts: EventCounter(our_pubkey: pubkey),
|
||||
contacts: Contacts(),
|
||||
contacts: Contacts(our_pubkey: pubkey),
|
||||
tips: TipCounter(our_pubkey: pubkey),
|
||||
profiles: Profiles(),
|
||||
dms: home.dms
|
||||
dms: home.dms,
|
||||
previews: PreviewCache()
|
||||
)
|
||||
home.damus_state = self.damus_state!
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>"Granting Damus access to your photo library allows you to save photos.</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>river</string>
|
||||
|
||||
@@ -11,8 +11,13 @@ import Foundation
|
||||
class Contacts {
|
||||
private var friends: Set<String> = Set()
|
||||
private var friend_of_friends: Set<String> = Set()
|
||||
let our_pubkey: String
|
||||
var event: NostrEvent?
|
||||
|
||||
init(our_pubkey: String) {
|
||||
self.our_pubkey = our_pubkey
|
||||
}
|
||||
|
||||
func get_friendosphere() -> [String] {
|
||||
var fs = get_friend_list()
|
||||
fs.append(contentsOf: get_friend_of_friend_list())
|
||||
@@ -56,6 +61,10 @@ class Contacts {
|
||||
return friends.contains(pubkey)
|
||||
}
|
||||
|
||||
func is_friend_or_self(_ pubkey: String) -> Bool {
|
||||
return pubkey == our_pubkey || is_friend(pubkey)
|
||||
}
|
||||
|
||||
func follow_state(_ pubkey: String) -> FollowState {
|
||||
return is_friend(pubkey) ? .follows : .unfollows
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LinkPresentation
|
||||
|
||||
struct DamusState {
|
||||
let pool: RelayPool
|
||||
@@ -16,12 +17,13 @@ struct DamusState {
|
||||
let tips: TipCounter
|
||||
let profiles: Profiles
|
||||
let dms: DirectMessagesModel
|
||||
let previews: PreviewCache
|
||||
|
||||
var pubkey: String {
|
||||
return keypair.pubkey
|
||||
}
|
||||
|
||||
static var empty: DamusState {
|
||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel())
|
||||
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(), previews: PreviewCache())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,7 +544,9 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
var old_nip05: String? = nil
|
||||
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||
old_nip05 = mprof.profile.nip05
|
||||
if mprof.timestamp > ev.created_at {
|
||||
// skip if we already have an newer profile
|
||||
return
|
||||
@@ -554,6 +556,20 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
let tprof = TimestampedProfile(profile: profile, timestamp: ev.created_at)
|
||||
profiles.add(id: ev.pubkey, profile: tprof)
|
||||
|
||||
if let nip05 = profile.nip05, old_nip05 != profile.nip05 {
|
||||
Task.detached(priority: .background) {
|
||||
let validated = await validate_nip05(pubkey: ev.pubkey, nip05_str: nip05)
|
||||
if validated != nil {
|
||||
print("validated nip05 for '\(nip05)'")
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
profiles.validated[ev.pubkey] = validated
|
||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load pfps asap
|
||||
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
||||
if let _ = URL(string: picture) {
|
||||
|
||||
@@ -187,12 +187,22 @@ enum Amount: Equatable {
|
||||
func amount_sats_str() -> String {
|
||||
switch self {
|
||||
case .any:
|
||||
return "Any"
|
||||
return NSLocalizedString("Any", comment: "Any amount of sats")
|
||||
case .specific(let amt):
|
||||
if amt < 1000 {
|
||||
return "\(Double(amt) / 1000.0) sats"
|
||||
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
|
||||
|
||||
if formattedSats == numberFormatter.string(from: 1) {
|
||||
return NSLocalizedString("\(formattedSats) sat", comment: "Amount of 1 sat.")
|
||||
}
|
||||
return "\(amt / 1000) sats"
|
||||
|
||||
return NSLocalizedString("\(formattedSats) sats", comment: "Amount of sats.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,40 +41,40 @@ class SearchHomeModel: ObservableObject {
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) {
|
||||
switch conn_ev {
|
||||
case .ws_event:
|
||||
break
|
||||
case .nostr_event(let event):
|
||||
switch event {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.base_subid || sub_id == self.profiles_subid else {
|
||||
guard case .nostr_event(let event) = conn_ev else {
|
||||
return
|
||||
}
|
||||
|
||||
switch event {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.base_subid || sub_id == self.profiles_subid else {
|
||||
return
|
||||
}
|
||||
if ev.is_textlike && ev.should_show_event && !ev.is_reply(nil) {
|
||||
if seen_pubkey.contains(ev.pubkey) {
|
||||
return
|
||||
}
|
||||
if ev.is_textlike && ev.should_show_event {
|
||||
if seen_pubkey.contains(ev.pubkey) {
|
||||
return
|
||||
}
|
||||
seen_pubkey.insert(ev.pubkey)
|
||||
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
|
||||
$0.created_at > $1.created_at
|
||||
}
|
||||
seen_pubkey.insert(ev.pubkey)
|
||||
|
||||
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
|
||||
$0.created_at > $1.created_at
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("search home notice: \(msg)")
|
||||
case .eose(let sub_id):
|
||||
loading = false
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
// Make sure we unsubscribe after we've fetched the global events
|
||||
// global events are not realtime
|
||||
unsubscribe(to: relay_id)
|
||||
|
||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
||||
}
|
||||
|
||||
|
||||
break
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("search home notice: \(msg)")
|
||||
case .eose(let sub_id):
|
||||
loading = false
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
// Make sure we unsubscribe after we've fetched the global events
|
||||
// global events are not realtime
|
||||
unsubscribe(to: relay_id)
|
||||
|
||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
||||
}
|
||||
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,27 +112,30 @@ func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent
|
||||
let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events)
|
||||
filter.authors = authors
|
||||
|
||||
if !authors.isEmpty {
|
||||
print("loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in
|
||||
let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in
|
||||
guard sub_id == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guard done && sid == profiles_subid else {
|
||||
guard !authors.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
print("loading \(authors.count) profiles from \(relay_id)")
|
||||
|
||||
damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in
|
||||
let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in
|
||||
guard sub_id == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
print("done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guard done && sid == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
print("done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class ThreadModel: ObservableObject {
|
||||
ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id }
|
||||
ref_events.referenced_ids?.append(ev.id)
|
||||
ref_events.limit = 50
|
||||
events_filter.ids = ref_events.referenced_ids!
|
||||
events_filter.ids = ref_events.referenced_ids ?? []
|
||||
events_filter.limit = 100
|
||||
events_filter.ids?.append(ev.id)
|
||||
case .event_id(let evid):
|
||||
|
||||
@@ -446,11 +446,13 @@ func hex_encode(_ data: Data) -> String {
|
||||
|
||||
|
||||
func random_bytes(count: Int) -> Data {
|
||||
var data = Data(count: count)
|
||||
_ = data.withUnsafeMutableBytes { mutableBytes in
|
||||
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes.baseAddress!)
|
||||
var bytes = [Int8](repeating: 0, count: count)
|
||||
guard
|
||||
SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) == errSecSuccess
|
||||
else {
|
||||
fatalError("can't copy secure random data")
|
||||
}
|
||||
return data
|
||||
return Data(bytes: bytes, count: count)
|
||||
}
|
||||
|
||||
func refid_to_tag(_ ref: ReferencedId) -> [String] {
|
||||
|
||||
@@ -80,7 +80,32 @@ func parse_nostr_ref_uri(_ p: Parser) -> ReferencedId? {
|
||||
return ReferencedId(ref_id: pk, relay_id: nil, key: typ)
|
||||
}
|
||||
|
||||
func decode_universal_link(_ s: String) -> NostrLink? {
|
||||
var uri = s.replacingOccurrences(of: "https://damus.io/r/", with: "")
|
||||
uri = uri.replacingOccurrences(of: "https://damus.io/", with: "")
|
||||
uri = uri.replacingOccurrences(of: "/", with: "")
|
||||
|
||||
guard let decoded = try? bech32_decode(uri) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let h = hex_encode(decoded.data)
|
||||
|
||||
if decoded.hrp == "note" {
|
||||
return .ref(ReferencedId(ref_id: h, relay_id: nil, key: "e"))
|
||||
} else if decoded.hrp == "npub" {
|
||||
return .ref(ReferencedId(ref_id: h, relay_id: nil, key: "p"))
|
||||
}
|
||||
// TODO: handle nprofile, etc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decode_nostr_uri(_ s: String) -> NostrLink? {
|
||||
if s.starts(with: "https://damus.io/") {
|
||||
return decode_universal_link(s)
|
||||
}
|
||||
|
||||
var uri = s.replacingOccurrences(of: "nostr://", with: "")
|
||||
uri = uri.replacingOccurrences(of: "nostr:", with: "")
|
||||
|
||||
|
||||
@@ -11,6 +11,17 @@ enum NostrResponse: Decodable {
|
||||
case event(String, NostrEvent)
|
||||
case notice(String)
|
||||
case eose(String)
|
||||
|
||||
var subid: String? {
|
||||
switch self {
|
||||
case .event(let sub_id, _):
|
||||
return sub_id
|
||||
case .eose(let sub_id):
|
||||
return sub_id
|
||||
case .notice:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
var container = try decoder.unkeyedContainer()
|
||||
|
||||
@@ -11,6 +11,11 @@ import UIKit
|
||||
|
||||
class Profiles {
|
||||
var profiles: [String: TimestampedProfile] = [:]
|
||||
var validated: [String: NIP05] = [:]
|
||||
|
||||
func is_validated(_ pk: String) -> NIP05? {
|
||||
return validated[pk]
|
||||
}
|
||||
|
||||
func add(id: String, profile: TimestampedProfile) {
|
||||
profiles[id] = profile
|
||||
|
||||
@@ -38,7 +38,7 @@ class RelayConnection: WebSocketDelegate {
|
||||
self.connect(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func connect(force: Bool = false){
|
||||
if !force && (self.isConnected || self.isConnecting) {
|
||||
return
|
||||
|
||||
@@ -28,9 +28,20 @@ struct RelayHandler {
|
||||
let callback: (String, NostrConnectionEvent) -> ()
|
||||
}
|
||||
|
||||
struct QueuedRequest {
|
||||
let req: NostrRequest
|
||||
let relay: String
|
||||
}
|
||||
|
||||
struct NostrRequestId: Equatable, Hashable {
|
||||
let relay: String?
|
||||
let sub_id: String
|
||||
}
|
||||
|
||||
class RelayPool {
|
||||
var relays: [Relay] = []
|
||||
var handlers: [RelayHandler] = []
|
||||
var request_queue: [QueuedRequest] = []
|
||||
|
||||
var descriptors: [RelayDescriptor] {
|
||||
relays.map { $0.descriptor }
|
||||
@@ -148,13 +159,38 @@ class RelayPool {
|
||||
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
|
||||
}
|
||||
|
||||
func count_queued(relay: String) -> Int {
|
||||
var c = 0
|
||||
for request in request_queue {
|
||||
if request.relay == relay {
|
||||
c += 1
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func queue_req(r: NostrRequest, relay: String) {
|
||||
let count = count_queued(relay: relay)
|
||||
guard count <= 10 else {
|
||||
print("can't queue, too many queued events for \(relay)")
|
||||
return
|
||||
}
|
||||
|
||||
print("queueing request: \(r) for \(relay)")
|
||||
request_queue.append(QueuedRequest(req: r, relay: relay))
|
||||
}
|
||||
|
||||
func send(_ req: NostrRequest, to: [String]? = nil) {
|
||||
let relays = to.map{ get_relays($0) } ?? self.relays
|
||||
|
||||
for relay in relays {
|
||||
if relay.connection.isConnected {
|
||||
relay.connection.send(req)
|
||||
guard relay.connection.isConnected else {
|
||||
queue_req(r: req, relay: relay.id)
|
||||
continue
|
||||
}
|
||||
|
||||
relay.connection.send(req)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +229,28 @@ class RelayPool {
|
||||
}
|
||||
}
|
||||
|
||||
func run_queue(_ relay_id: String) {
|
||||
self.request_queue = request_queue.reduce(into: Array<QueuedRequest>()) { (q, req) in
|
||||
guard req.relay == relay_id else {
|
||||
q.append(req)
|
||||
return
|
||||
}
|
||||
|
||||
print("running queueing request: \(req.req) for \(relay_id)")
|
||||
self.send(req.req, to: [relay_id])
|
||||
}
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
||||
record_last_pong(relay_id: relay_id, event: event)
|
||||
|
||||
// run req queue when we reconnect
|
||||
if case .ws_event(let ws) = event {
|
||||
if case .connected = ws {
|
||||
run_queue(relay_id)
|
||||
}
|
||||
}
|
||||
|
||||
// handle reconnect logic, etc?
|
||||
for handler in handlers {
|
||||
handler.callback(relay_id, event)
|
||||
|
||||
@@ -11,7 +11,7 @@ public class Constants {
|
||||
|
||||
static let PUB_KEY = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||
|
||||
static let EXAMPLE_DEMOS = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: PUB_KEY, privkey: "privkey"), likes: EventCounter(our_pubkey: PUB_KEY), boosts: EventCounter(our_pubkey: PUB_KEY), contacts: Contacts(), tips: TipCounter(our_pubkey: PUB_KEY), profiles: Profiles(), dms: DirectMessagesModel())
|
||||
static let EXAMPLE_DEMOS: DamusState = .empty
|
||||
|
||||
static let EXAMPLE_EVENTS = [
|
||||
NostrEvent(id: UUID().description, content: "Nostr - Damus... Haha get it? Bonjour Le Monde mon Ami! C'est la tres importante", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
|
||||
|
||||
@@ -12,26 +12,24 @@ class CustomLinkView: LPLinkView {
|
||||
override var intrinsicContentSize: CGSize { CGSize(width: 0, height: super.intrinsicContentSize.height) }
|
||||
}
|
||||
|
||||
enum Metadata {
|
||||
case linkmeta(LPLinkMetadata)
|
||||
case url(URL)
|
||||
}
|
||||
|
||||
struct LinkViewRepresentable: UIViewRepresentable {
|
||||
|
||||
typealias UIViewType = CustomLinkView
|
||||
|
||||
var metadata: LPLinkMetadata?
|
||||
var url: URL?
|
||||
let meta: Metadata
|
||||
|
||||
func makeUIView(context: Context) -> CustomLinkView {
|
||||
|
||||
if let metadata {
|
||||
let linkView = CustomLinkView(metadata: metadata)
|
||||
return linkView
|
||||
switch meta {
|
||||
case .linkmeta(let linkmeta):
|
||||
return CustomLinkView(metadata: linkmeta)
|
||||
case .url(let url):
|
||||
return CustomLinkView(url: url)
|
||||
}
|
||||
|
||||
if let url {
|
||||
let linkView = CustomLinkView(url: url)
|
||||
return linkView
|
||||
}
|
||||
|
||||
return CustomLinkView()
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: CustomLinkView, context: Context) {
|
||||
|
||||
@@ -15,12 +15,14 @@ public struct Markdown {
|
||||
return url.contains("://") ? url : "https://" + url
|
||||
}
|
||||
|
||||
/// Parse a string with markdown into an `AttributedString`, if possible, or else return it as regular text.
|
||||
public static func parse(content: String) -> AttributedString {
|
||||
// Similar to the parsing in NoteContentView
|
||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
||||
if let txt = try? AttributedString(markdown: content, options: md_opts) {
|
||||
// TODO: escape unintentional markdown
|
||||
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
||||
if let txt = try? AttributedString(markdown: escaped, options: md_opts) {
|
||||
return txt
|
||||
} else {
|
||||
return AttributedString(stringLiteral: content)
|
||||
@@ -33,11 +35,13 @@ public struct Markdown {
|
||||
var output = input
|
||||
// Start with the last match, because replacing the first would invalidate all subsequent indices
|
||||
for match in matches.reversed() {
|
||||
guard let range = Range(match.range, in: input) else { continue }
|
||||
let url = input[range]
|
||||
output.replaceSubrange(range, with: "[\(url)](\(Markdown.withScheme(url)))")
|
||||
guard let range = Range(match.range, in: input)
|
||||
, let url = match.url else { continue }
|
||||
let text = input[range]
|
||||
// Use the absoluteString from the matched URL, except when it defaults to http (since we default to https)
|
||||
let uri = url.scheme == "http" ? Markdown.withScheme(text) : url.absoluteString
|
||||
output.replaceSubrange(range, with: "[\(text)](\(uri))")
|
||||
}
|
||||
// TODO: escape unintentional markdown
|
||||
return Markdown.parse(content: output)
|
||||
}
|
||||
}
|
||||
|
||||
65
damus/Util/NIP05.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// NIP05.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-04.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NIP05 {
|
||||
let username: String
|
||||
let host: String
|
||||
|
||||
var url: URL? {
|
||||
URL(string: "https://\(host)/.well-known/nostr.json?name=\(username)")
|
||||
}
|
||||
|
||||
static func parse(_ nip05: String) -> NIP05? {
|
||||
let parts = nip05.split(separator: "@")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
return NIP05(username: String(parts[0]), host: String(parts[1]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct NIP05Response: Decodable {
|
||||
let names: [String: String]
|
||||
let relays: [String: [String]]?
|
||||
}
|
||||
|
||||
enum NIP05Validation {
|
||||
case invalid
|
||||
case valid
|
||||
}
|
||||
|
||||
func validate_nip05(pubkey: String, nip05_str: String) async -> NIP05? {
|
||||
guard let nip05 = NIP05.parse(nip05_str) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let url = nip05.url else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let ret = try? await URLSession.shared.data(from: url) else {
|
||||
return nil
|
||||
}
|
||||
let dat = ret.0
|
||||
|
||||
guard let decoded = try? JSONDecoder().decode(NIP05Response.self, from: dat) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let stored_pk = decoded.names[nip05.username] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard stored_pk == pubkey else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nip05
|
||||
}
|
||||
35
damus/Util/PreviewCache.swift
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// PreviewCache.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-02.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LinkPresentation
|
||||
|
||||
enum Preview {
|
||||
case value(LinkViewRepresentable)
|
||||
case failed
|
||||
}
|
||||
|
||||
class PreviewCache {
|
||||
var previews: [String: Preview]
|
||||
|
||||
func lookup(_ evid: String) -> Preview? {
|
||||
return previews[evid]
|
||||
}
|
||||
|
||||
func store(evid: String, preview: LinkViewRepresentable?) {
|
||||
switch preview {
|
||||
case .none:
|
||||
previews[evid] = .failed
|
||||
case .some(let meta):
|
||||
previews[evid] = .value(meta)
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.previews = [:]
|
||||
}
|
||||
}
|
||||
28
damus/Util/Theme.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Theme.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Ben Weeks on 1/1/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class Theme {
|
||||
static func navigationBarColors(background : UIColor?,
|
||||
titleColor : UIColor? = nil, tintColor : UIColor? = nil ){
|
||||
|
||||
let navigationAppearance = UINavigationBarAppearance()
|
||||
navigationAppearance.configureWithOpaqueBackground()
|
||||
navigationAppearance.backgroundColor = background ?? .clear
|
||||
|
||||
navigationAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .black]
|
||||
navigationAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .black]
|
||||
|
||||
UINavigationBar.appearance().standardAppearance = navigationAppearance
|
||||
UINavigationBar.appearance().compactAppearance = navigationAppearance
|
||||
UINavigationBar.appearance().scrollEdgeAppearance = navigationAppearance
|
||||
|
||||
UINavigationBar.appearance().tintColor = tintColor ?? titleColor ?? .black
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,10 @@ struct AddRelayView: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Form {
|
||||
Section("Add Relay") {
|
||||
Section(NSLocalizedString("Add Relay", comment: "Label for section for adding a relay server.")) {
|
||||
ZStack(alignment: .leading) {
|
||||
HStack{
|
||||
TextField("wss://some.relay.com", text: $relay)
|
||||
TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $relay)
|
||||
.padding(2)
|
||||
.padding(.leading, 25)
|
||||
.autocorrectionDisabled(true)
|
||||
@@ -47,7 +47,7 @@ struct AddRelayView: View {
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Button("Cancel") {
|
||||
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted relay.")) {
|
||||
show_add_relay = false
|
||||
action(nil)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ struct AddRelayView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
Button("Add") {
|
||||
Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted relay.")) {
|
||||
show_add_relay = false
|
||||
action(relay)
|
||||
relay = ""
|
||||
|
||||
@@ -15,13 +15,13 @@ struct CarouselItem: Identifiable {
|
||||
}
|
||||
|
||||
let carousel_items = [
|
||||
CarouselItem(image: Image("digital-nomad"), text: Text("Welcome to the social network \(Text("you").italic()) control.")),
|
||||
CarouselItem(image: Image("digital-nomad"), text: Text("Welcome to the social network \(Text("you", comment: "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.").italic()) control.", comment: "Welcoming message to the reader. The variable is 'you', the reader.")),
|
||||
CarouselItem(image: Image("encrypted-message"),
|
||||
text: Text("\(Text("Encrypted").bold()). End-to-End encrypted private messaging. Keep Big Tech out of your DMs")),
|
||||
text: Text("\(Text("Encrypted", comment: "Heading indicating that this application keeps private messaging end-to-end encrypted.").bold()). End-to-End encrypted private messaging. Keep Big Tech out of your DMs", comment: "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.")),
|
||||
CarouselItem(image: Image("undercover"),
|
||||
text: Text("\(Text("Private").bold()). Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.")),
|
||||
text: Text("\(Text("Private", comment: "Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading.").bold()). Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.", comment: "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.")),
|
||||
CarouselItem(image: Image("bitcoin-p2p"),
|
||||
text: Text("\(Text("Earn Money").bold()). Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet."))
|
||||
text: Text("\(Text("Earn Money", comment: "Heading indicating that this application allows users to earn money.").bold()). Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet.", comment: "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."))
|
||||
]
|
||||
|
||||
struct CarouselView: View {
|
||||
|
||||
@@ -86,7 +86,7 @@ struct ChatView: View {
|
||||
VStack(alignment: .leading) {
|
||||
if just_started {
|
||||
HStack {
|
||||
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), damus: damus_state, show_friend_confirmed: true)
|
||||
.foregroundColor(colorScheme == .dark ? id_to_color(event.pubkey) : Color.black)
|
||||
//.shadow(color: Color.black, radius: 2)
|
||||
Text("\(format_relative_time(event.created_at))")
|
||||
@@ -96,7 +96,7 @@ struct ChatView: View {
|
||||
|
||||
if let ref_id = thread.replies.lookup(event.id) {
|
||||
if !is_reply_to_prev() {
|
||||
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles)
|
||||
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews)
|
||||
.frame(maxHeight: expand_reply ? nil : 100)
|
||||
.environmentObject(thread)
|
||||
.onTapGesture {
|
||||
@@ -106,7 +106,7 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
|
||||
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)
|
||||
|
||||
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||
let bar = make_actionbar_model(ev: event, damus: damus_state)
|
||||
|
||||
@@ -24,7 +24,7 @@ struct ChatroomView: View {
|
||||
next_ev: ind == count-1 ? nil : thread.events[ind+1],
|
||||
damus_state: damus
|
||||
)
|
||||
.event_context_menu(ev, privkey: damus.keypair.privkey)
|
||||
.event_context_menu(ev, pubkey: ev.pubkey, privkey: damus.keypair.privkey)
|
||||
.onTapGesture {
|
||||
if thread.initial_event.id == ev.id {
|
||||
//dismiss()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
import AVFoundation
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
struct ConfigView: View {
|
||||
let state: DamusState
|
||||
@@ -54,19 +55,28 @@ struct ConfigView: View {
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
Form {
|
||||
Section("Relays") {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section("Recommended Relays") {
|
||||
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("Public Account ID") {
|
||||
Section(NSLocalizedString("Public Account ID", comment: "Section title for the user's public account ID.")) {
|
||||
HStack {
|
||||
Text(state.keypair.pubkey_bech32)
|
||||
|
||||
@@ -76,10 +86,10 @@ struct ConfigView: View {
|
||||
}
|
||||
|
||||
if let sec = state.keypair.privkey_bech32 {
|
||||
Section("Secret Account Login Key") {
|
||||
Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) {
|
||||
HStack {
|
||||
if show_privkey == false {
|
||||
SecureField("PrivateKey", text: $privkey)
|
||||
SecureField(NSLocalizedString("PrivateKey", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
|
||||
.disabled(true)
|
||||
} else {
|
||||
Text(sec)
|
||||
@@ -89,13 +99,13 @@ struct ConfigView: View {
|
||||
CopyButton(is_pk: false)
|
||||
}
|
||||
|
||||
Toggle("Show", isOn: $show_privkey)
|
||||
Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey)
|
||||
}
|
||||
}
|
||||
|
||||
Section("Wallet Selector") {
|
||||
Toggle("Show wallet selector", isOn: $user_settings.show_wallet_selector).toggleStyle(.switch)
|
||||
Picker("Select default 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: $user_settings.show_wallet_selector).toggleStyle(.switch)
|
||||
Picker(NSLocalizedString("Select default wallet", comment: "Prompt selection of user's default wallet"),
|
||||
selection: $user_settings.default_wallet) {
|
||||
ForEach(Wallet.allCases, id: \.self) { wallet in
|
||||
Text(wallet.model.displayName)
|
||||
@@ -104,49 +114,47 @@ struct ConfigView: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section("Reset") {
|
||||
Button("Logout") {
|
||||
Section(NSLocalizedString("Clear Cache", comment: "Section title for clearing cached data.")) {
|
||||
Button(NSLocalizedString("Clear", comment: "Button for clearing cached data.")) {
|
||||
KingfisherManager.shared.cache.clearMemoryCache()
|
||||
KingfisherManager.shared.cache.clearDiskCache()
|
||||
KingfisherManager.shared.cache.cleanExpiredDiskCache()
|
||||
}
|
||||
}
|
||||
|
||||
Section(NSLocalizedString("Reset", comment: "Section title for resetting the user")) {
|
||||
Button(NSLocalizedString("Logout", comment: "Button to logout the user.")) {
|
||||
confirm_logout = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button(action: { show_add_relay = true }) {
|
||||
Label("", systemImage: "plus")
|
||||
.foregroundColor(.accentColor)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
.navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view."))
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
.alert("Logout", isPresented: $confirm_logout) {
|
||||
Button("Cancel") {
|
||||
.alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) {
|
||||
Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user.")) {
|
||||
confirm_logout = false
|
||||
}
|
||||
Button("Logout") {
|
||||
Button(NSLocalizedString("Logout", comment: "Button for logging out the user.")) {
|
||||
notify(.logout, ())
|
||||
}
|
||||
} message: {
|
||||
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account")
|
||||
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 let relay = m_relay else {
|
||||
guard var relay = m_relay else {
|
||||
return
|
||||
}
|
||||
|
||||
if relay.starts(with: "wss://") == false {
|
||||
relay = "wss://" + relay
|
||||
}
|
||||
|
||||
guard let url = URL(string: relay) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let ev = state.contacts.event else {
|
||||
return
|
||||
}
|
||||
@@ -161,9 +169,9 @@ struct ConfigView: View {
|
||||
return
|
||||
}
|
||||
|
||||
state.pool.connect(to: [new_relay])
|
||||
state.pool.connect(to: [relay])
|
||||
|
||||
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: new_relay, info: info) else {
|
||||
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: info) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,14 @@ struct CreateAccountView: View {
|
||||
|
||||
HStack(alignment: .top) {
|
||||
VStack {
|
||||
Text(" ")
|
||||
Text(" ", comment: "Blank space to separate profile picture from profile editor form.")
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
VStack {
|
||||
SignupForm {
|
||||
FormLabel(NSLocalizedString("Username", comment: "Label to prompt username entry."))
|
||||
HStack(spacing: 0.0) {
|
||||
Text("@")
|
||||
Text("@", comment: "Prefix character to username.")
|
||||
.foregroundColor(.white)
|
||||
.padding(.leading, -25.0)
|
||||
|
||||
@@ -151,7 +151,7 @@ func FormLabel(_ title: String, optional: Bool = false) -> some View {
|
||||
.bold()
|
||||
.foregroundColor(.white)
|
||||
if optional {
|
||||
Text("optional")
|
||||
Text("optional", comment: "Label indicating that a form input is optional.")
|
||||
.font(.callout)
|
||||
.foregroundColor(.white.opacity(0.5))
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ struct DMChatView: View {
|
||||
VStack(alignment: .leading) {
|
||||
ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in
|
||||
DMView(event: dms.events[ind], damus_state: damus_state)
|
||||
.event_context_menu(ev, privkey: damus_state.keypair.privkey)
|
||||
.event_context_menu(ev, pubkey: ev.pubkey, privkey: damus_state.keypair.privkey)
|
||||
}
|
||||
EndBlock(height: 80)
|
||||
}
|
||||
@@ -27,6 +27,10 @@ struct DMChatView: View {
|
||||
}
|
||||
.onAppear {
|
||||
scroller.scrollTo("endblock")
|
||||
}.onChange(of: dms.events.count) { _ in
|
||||
withAnimation {
|
||||
scroller.scrollTo("endblock")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +44,7 @@ struct DMChatView: View {
|
||||
HStack {
|
||||
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles)
|
||||
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: pubkey, profile: profile, damus: damus_state, show_friend_confirmed: true)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
@@ -138,14 +142,14 @@ struct DMChatView: View {
|
||||
|
||||
Footer
|
||||
}
|
||||
Text("Send a message to start the conversation...")
|
||||
Text("Send a message to start the conversation...", comment: "Text prompt for user to send a message to the other user.")
|
||||
.lineLimit(nil)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
.opacity(((dms.events.count == 0) ? 1.0 : 0.0))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.navigationTitle("DM")
|
||||
.navigationTitle(NSLocalizedString("DM", comment: "Navigation title for DM view, which is the English abbreviation for Direct Message."))
|
||||
.toolbar { Header }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, 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)
|
||||
.padding(10)
|
||||
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))
|
||||
|
||||
@@ -53,7 +53,7 @@ struct DirectMessagesView: View {
|
||||
|
||||
var body: some View {
|
||||
MainContent
|
||||
.navigationTitle("Encrypted DMs")
|
||||
.navigationTitle(NSLocalizedString("Encrypted DMs", comment: "Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message."))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,6 @@ func isHttpsUrl(_ string: String) -> Bool {
|
||||
return urlTest.evaluate(with: string)
|
||||
}
|
||||
|
||||
struct NIP05 {
|
||||
let username: String
|
||||
let host: String
|
||||
}
|
||||
|
||||
func isImage(_ urlString: String) -> Bool {
|
||||
let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp", "image/webp"]
|
||||
|
||||
@@ -101,11 +96,7 @@ struct EditMetadataView: View {
|
||||
}
|
||||
|
||||
var nip05_parts: NIP05? {
|
||||
let parts = nip05.split(separator: "@")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
return NIP05(username: String(parts[0]), host: String(parts[1]))
|
||||
return NIP05.parse(nip05)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -116,32 +107,32 @@ struct EditMetadataView: View {
|
||||
Spacer()
|
||||
}
|
||||
Form {
|
||||
Section("Your Name") {
|
||||
Section(NSLocalizedString("Your Name", comment: "Label for Your Name section of user profile form.")) {
|
||||
TextField("Satoshi Nakamoto", text: $display_name)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section("Username") {
|
||||
Section(NSLocalizedString("Username", comment: "Label for Username section of user profile form.")) {
|
||||
TextField("satoshi", text: $name)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
|
||||
}
|
||||
|
||||
Section ("Profile Picture") {
|
||||
TextField("https://example.com/pic.jpg", text: $picture)
|
||||
Section (NSLocalizedString("Profile Picture", comment: "Label for Profile Picture section of user profile form.")) {
|
||||
TextField(NSLocalizedString("https://example.com/pic.jpg", comment: "Placeholder example text for profile picture URL."), text: $picture)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section("Website") {
|
||||
TextField("https://jb55.com", text: $website)
|
||||
Section(NSLocalizedString("Website", comment: "Label for Website section of user profile form.")) {
|
||||
TextField(NSLocalizedString("https://jb55.com", comment: "Placeholder example text for website URL for user profile."), text: $website)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section("About Me") {
|
||||
Section(NSLocalizedString("About Me", comment: "Label for About Me section of user profile form.")) {
|
||||
let placeholder = NSLocalizedString("Absolute Boss", comment: "Placeholder text for About Me description.")
|
||||
ZStack(alignment: .topLeading) {
|
||||
TextEditor(text: $about)
|
||||
@@ -155,33 +146,33 @@ struct EditMetadataView: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section("Bitcoin Lightning Tips") {
|
||||
TextField("Lightning Address or LNURL", text: $ln)
|
||||
Section(NSLocalizedString("Bitcoin Lightning Tips", comment: "Label for Bitcoin Lightning Tips section of user profile form.")) {
|
||||
TextField(NSLocalizedString("Lightning Address or LNURL", comment: "Placeholder text for entry of Lightning Address or LNURL."), text: $ln)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section(content: {
|
||||
TextField("jb55@jb55.com", text: $nip05)
|
||||
TextField(NSLocalizedString("jb55@jb55.com", comment: "Placeholder example text for identifier used for NIP-05 verification."), text: $nip05)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}, header: {
|
||||
Text("NIP-05 Verification")
|
||||
Text("NIP-05 Verification", comment: "Label for NIP-05 Verification section of user profile form.")
|
||||
}, footer: {
|
||||
if let parts = nip05_parts {
|
||||
Text(String.localizedStringWithFormat("'%@' at '%@' will be used for verification", parts.username, parts.host))
|
||||
Text("'\(parts.username)' at '\(parts.host)' will be used for verification", comment: "Description of how the nip05 identifier would be used for verification.")
|
||||
} else {
|
||||
Text(String.localizedStringWithFormat("'%@' is an invalid nip05 identifier. It should look like an email.", nip05))
|
||||
Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.", comment: "Description of why the nip05 identifier is invalid.")
|
||||
}
|
||||
})
|
||||
|
||||
Button("Save") {
|
||||
|
||||
Button(NSLocalizedString("Save", comment: "Button for saving profile.")) {
|
||||
save()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Edit Profile")
|
||||
.navigationTitle(NSLocalizedString("Edit Profile", comment: "Title of navigation view for Edit Profile."))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ struct EmptyTimelineView: View {
|
||||
Image(systemName: "tray.fill")
|
||||
.font(.system(size: 35))
|
||||
.padding()
|
||||
Text("Nothing to see here. Check back later!")
|
||||
Text("Nothing to see here. Check back later!", comment: "Indicates that there are no notes in the timeline to view.")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.callout.weight(.medium))
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ struct EventActionBar: View {
|
||||
let generator = UIImpactFeedbackGenerator(style: .medium)
|
||||
@State var sheet: ActionBarSheet? = nil
|
||||
@State var confirm_boost: Bool = false
|
||||
@State var show_share_sheet: Bool = false
|
||||
@StateObject var bar: ActionBarModel
|
||||
|
||||
var body: some View {
|
||||
@@ -40,6 +41,7 @@ struct EventActionBar: View {
|
||||
EventActionButton(img: "bubble.left", col: nil) {
|
||||
notify(.reply, event)
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
HStack(alignment: .bottom) {
|
||||
@@ -55,6 +57,7 @@ struct EventActionBar: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
HStack(alignment: .bottom) {
|
||||
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
|
||||
@@ -69,6 +72,12 @@ struct EventActionBar: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
EventActionButton(img: "square.and.arrow.up", col: Color.gray) {
|
||||
show_share_sheet = true
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
/*
|
||||
HStack(alignment: .bottom) {
|
||||
@@ -86,15 +95,22 @@ struct EventActionBar: View {
|
||||
}
|
||||
*/
|
||||
}
|
||||
.alert("Boost", isPresented: $confirm_boost) {
|
||||
.sheet(isPresented: $show_share_sheet) {
|
||||
if let note_id = bech32_note_id(event.id) {
|
||||
if let url = URL(string: "https://damus.io/" + note_id) {
|
||||
ShareSheet(activityItems: [url])
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(NSLocalizedString("Boost", comment: "Title of alert for confirming to boost a post."), isPresented: $confirm_boost) {
|
||||
Button("Cancel") {
|
||||
confirm_boost = false
|
||||
}
|
||||
Button("Boost") {
|
||||
Button(NSLocalizedString("Boost", comment: "Button to confirm boosting a post.")) {
|
||||
send_boost()
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to boost this post?")
|
||||
Text("Are you sure you want to boost this post?", comment: "Alert message to ask if user wants to boost a post.")
|
||||
}
|
||||
.onReceive(handle_notify(.liked)) { n in
|
||||
let liked = n.object as! Counted
|
||||
@@ -142,7 +158,6 @@ func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) ->
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(col == nil ? Color.gray : col!)
|
||||
}
|
||||
.padding(.trailing, 40)
|
||||
}
|
||||
|
||||
struct LikeButton: View {
|
||||
@@ -154,11 +169,11 @@ struct LikeButton: View {
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
if liked {
|
||||
Text("🤙")
|
||||
Text("🤙", comment: "Button with emoji to like an event.")
|
||||
} else {
|
||||
Label(" ", systemImage: "hand.thumbsup")
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(Color.gray)
|
||||
Image("shaka")
|
||||
.renderingMode(.template)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ struct EventDetailView: View {
|
||||
Group {
|
||||
switch cev {
|
||||
case .collapsed(let c):
|
||||
Text("··· \(c.count) other notes ···")
|
||||
Text(String(format: NSLocalizedString("collapsed_event_view_other_notes", comment: "Text to indicate that the thread was collapsed and that there are other notes to view if tapped."), c.count))
|
||||
.padding([.top,.bottom], 8)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
@@ -114,7 +114,7 @@ struct EventDetailView: View {
|
||||
scroll_after_load(thread: thread, proxy: proxy)
|
||||
}
|
||||
}
|
||||
.navigationBarTitle("Thread")
|
||||
.navigationBarTitle(NSLocalizedString("Thread", comment: "Navigation bar title for note thread."))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -103,12 +103,13 @@ struct BuilderEventView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if event == nil {
|
||||
ProgressView().padding()
|
||||
} else {
|
||||
NavigationLink(destination: BuildThreadV2View(damus: damus, event_id: event!.id)) {
|
||||
EventView(damus: damus, event: event!, show_friend_icon: true, size: .small, embedded: true)
|
||||
if let event = event {
|
||||
let ev = event.inner_event ?? event
|
||||
NavigationLink(destination: BuildThreadV2View(damus: damus, event_id: ev.id)) {
|
||||
EventView(damus: damus, event: event, show_friend_icon: true, size: .small)
|
||||
}.buttonStyle(.plain)
|
||||
} else {
|
||||
ProgressView().padding()
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
@@ -128,11 +129,10 @@ struct EventView: View {
|
||||
let pubkey: String
|
||||
let show_friend_icon: Bool
|
||||
let size: EventViewKind
|
||||
let embedded: Bool
|
||||
|
||||
@EnvironmentObject var action_bar: ActionBarModel
|
||||
|
||||
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
|
||||
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool, size: EventViewKind = .normal) {
|
||||
self.event = event
|
||||
self.highlight = highlight
|
||||
self.has_action_bar = has_action_bar
|
||||
@@ -140,10 +140,9 @@ struct EventView: View {
|
||||
self.pubkey = event.pubkey
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.size = size
|
||||
self.embedded = embedded
|
||||
}
|
||||
|
||||
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
|
||||
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool, size: EventViewKind = .normal) {
|
||||
self.event = event
|
||||
self.highlight = .none
|
||||
self.has_action_bar = false
|
||||
@@ -151,7 +150,6 @@ struct EventView: View {
|
||||
self.pubkey = event.pubkey
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.size = size
|
||||
self.embedded = embedded
|
||||
}
|
||||
|
||||
init(damus: DamusState, event: NostrEvent, pubkey: String, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
|
||||
@@ -162,7 +160,6 @@ struct EventView: View {
|
||||
self.pubkey = pubkey
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.size = size
|
||||
self.embedded = embedded
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -179,10 +176,10 @@ struct EventView: View {
|
||||
Image(systemName: "arrow.2.squarepath")
|
||||
.font(.footnote.weight(.bold))
|
||||
.foregroundColor(Color.gray)
|
||||
ProfileName(pubkey: event.pubkey, profile: prof, contacts: damus.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: event.pubkey, profile: prof, damus: damus, show_friend_confirmed: true)
|
||||
.font(.footnote.weight(.bold))
|
||||
.foregroundColor(Color.gray)
|
||||
Text("Boosted")
|
||||
Text("Boosted", comment: "Text indicating that the post was boosted (i.e. re-shared).")
|
||||
.font(.footnote.weight(.bold))
|
||||
.foregroundColor(Color.gray)
|
||||
}
|
||||
@@ -209,10 +206,8 @@ struct EventView: View {
|
||||
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
|
||||
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
|
||||
|
||||
if !embedded {
|
||||
NavigationLink(destination: pv) {
|
||||
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
|
||||
}
|
||||
NavigationLink(destination: pv) {
|
||||
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -232,7 +227,7 @@ struct EventView: View {
|
||||
}
|
||||
}
|
||||
|
||||
EventProfileName(pubkey: pubkey, profile: profile, contacts: damus.contacts, show_friend_confirmed: show_friend_icon, size: size)
|
||||
EventProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: show_friend_icon, size: size)
|
||||
if size != .selected {
|
||||
Text("\(format_relative_time(event.created_at))")
|
||||
.font(eventviewsize_to_font(size))
|
||||
@@ -249,55 +244,28 @@ struct EventView: View {
|
||||
|
||||
let should_show_img = should_show_images(contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey)
|
||||
|
||||
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
|
||||
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, previews: damus.previews, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.allowsHitTesting(!embedded)
|
||||
|
||||
if !embedded {
|
||||
let blocks = event.blocks(damus.keypair.privkey).filter { block in
|
||||
guard case .mention(let mention) = block else {
|
||||
return false
|
||||
}
|
||||
if has_action_bar {
|
||||
if size == .selected {
|
||||
Text("\(format_date(event.created_at))")
|
||||
.padding(.top, 10)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
guard case .event = mention.type else {
|
||||
return false
|
||||
}
|
||||
|
||||
if mention.ref.key != "e" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
Divider()
|
||||
.padding([.bottom], 4)
|
||||
} else {
|
||||
Rectangle().frame(height: 2).opacity(0)
|
||||
}
|
||||
|
||||
/// MARK: - Preview
|
||||
if let firstBlock = blocks.first, case .mention(let mention) = firstBlock, mention.ref.key == "e" {
|
||||
BuilderEventView(damus: damus, event_id: mention.ref.id)
|
||||
}
|
||||
let bar = make_actionbar_model(ev: event, damus: damus)
|
||||
EventActionBar(damus_state: damus, event: event, bar: bar)
|
||||
}
|
||||
|
||||
if !embedded {
|
||||
if has_action_bar {
|
||||
if size == .selected {
|
||||
Text("\(format_date(event.created_at))")
|
||||
.padding(.top, 10)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
Divider()
|
||||
.padding([.bottom], 4)
|
||||
} else {
|
||||
Rectangle().frame(height: 2).opacity(0)
|
||||
}
|
||||
|
||||
let bar = make_actionbar_model(ev: event, damus: damus)
|
||||
EventActionBar(damus_state: damus, event: event, bar: bar)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding([.top], 4)
|
||||
}
|
||||
Divider()
|
||||
.padding([.top], 4)
|
||||
}
|
||||
.padding([.leading], 2)
|
||||
}
|
||||
@@ -306,7 +274,7 @@ struct EventView: View {
|
||||
.id(event.id)
|
||||
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
|
||||
.padding([.bottom], 2)
|
||||
.event_context_menu(event, privkey: damus.keypair.privkey)
|
||||
.event_context_menu(event, pubkey: pubkey, privkey: damus.keypair.privkey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,41 +308,41 @@ extension View {
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32_pubkey
|
||||
} label: {
|
||||
Label("Copy Account ID", systemImage: "doc.on.doc")
|
||||
Label(NSLocalizedString("Copy Account ID", comment: "Context menu option for copying the ID of the account that created the note."), systemImage: "doc.on.doc")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func event_context_menu(_ event: NostrEvent, privkey: String?) -> some View {
|
||||
func event_context_menu(_ event: NostrEvent, pubkey: String, privkey: String?) -> some View {
|
||||
return self.contextMenu {
|
||||
Button {
|
||||
UIPasteboard.general.string = event.get_content(privkey)
|
||||
} label: {
|
||||
Label("Copy Text", systemImage: "doc.on.doc")
|
||||
Label(NSLocalizedString("Copy Text", comment: "Context menu option for copying the text from an note."), systemImage: "doc.on.doc")
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32_pubkey(event.pubkey) ?? event.pubkey
|
||||
UIPasteboard.general.string = bech32_pubkey(pubkey) ?? pubkey
|
||||
} label: {
|
||||
Label("Copy User ID", systemImage: "tag")
|
||||
Label(NSLocalizedString("Copy User ID", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "tag")
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32_note_id(event.id) ?? event.id
|
||||
} label: {
|
||||
Label("Copy Note ID", systemImage: "tag")
|
||||
Label(NSLocalizedString("Copy Note ID", comment: "Context menu option for copying the ID of the note."), systemImage: "tag")
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = event_to_json(ev: event)
|
||||
} label: {
|
||||
Label("Copy Note JSON", systemImage: "note")
|
||||
Label(NSLocalizedString("Copy Note JSON", comment: "Context menu option for copying the JSON text from the note."), systemImage: "note")
|
||||
}
|
||||
|
||||
Button {
|
||||
NotificationCenter.default.post(name: .broadcast_event, object: event)
|
||||
} label: {
|
||||
Label("Broadcast", systemImage: "globe")
|
||||
Label(NSLocalizedString("Broadcast", comment: "Context menu option for broadcasting the user's note to all of the user's connected relay servers."), systemImage: "globe")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +382,7 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String {
|
||||
let othersCount = n - pubkeys.count
|
||||
return String(format: NSLocalizedString("replying_to_two_and_others", comment: "Label to indicate that the user is replying to 2 users and others."), names[0], names[1], othersCount)
|
||||
}
|
||||
return String.localizedStringWithFormat("Replying to %@ & %@", names[0], names[1])
|
||||
return String(format: NSLocalizedString("Replying to %@ & %@", comment: "Label to indicate that the user is replying to 2 users."), names[0], names[1])
|
||||
}
|
||||
|
||||
let othersCount = n - pubkeys.count
|
||||
|
||||
@@ -19,10 +19,11 @@ struct FollowButtonView: View {
|
||||
follow_state = perform_follow_btn_action(follow_state, target: target)
|
||||
} label: {
|
||||
Text(follow_btn_txt(follow_state))
|
||||
.frame(height: 30)
|
||||
.padding(.horizontal, 25)
|
||||
.padding(.vertical, 10)
|
||||
//.padding(.vertical, 10)
|
||||
.font(.caption.weight(.bold))
|
||||
.foregroundColor(follow_state == .unfollows ? emptyColor() : fillColor())
|
||||
.foregroundColor(follow_state == .unfollows ? filledTextColor() : borderColor())
|
||||
.background(follow_state == .unfollows ? fillColor() : emptyColor())
|
||||
.cornerRadius(20)
|
||||
.overlay {
|
||||
@@ -48,16 +49,20 @@ struct FollowButtonView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func filledTextColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusWhite") : Color("DamusBlack")
|
||||
}
|
||||
|
||||
func fillColor() -> Color {
|
||||
colorScheme == .light ? .black : .white
|
||||
colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite")
|
||||
}
|
||||
|
||||
func emptyColor() -> Color {
|
||||
colorScheme == .light ? .white : .black
|
||||
Color.black.opacity(0)
|
||||
}
|
||||
|
||||
func borderColor() -> Color {
|
||||
colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.2)
|
||||
colorScheme == .light ? Color("DamusDarkGrey") : Color("DamusLightGrey")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,16 +70,16 @@ struct FollowButtonPreviews: View {
|
||||
let target: FollowTarget = .pubkey("")
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Unfollows")
|
||||
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, follow_state: .unfollows)
|
||||
|
||||
Text("Following")
|
||||
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, follow_state: .following)
|
||||
|
||||
Text("Follows")
|
||||
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, follow_state: .follows)
|
||||
|
||||
Text("Unfollowing")
|
||||
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, follow_state: .unfollowing)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct FollowUserView: View {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
let profile = damus_state.profiles.lookup(id: target.pubkey)
|
||||
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
|
||||
ProfileName(pubkey: target.pubkey, profile: profile, damus: damus_state, show_friend_confirmed: false)
|
||||
if let about = profile?.about {
|
||||
Text(FollowUserView.markdown.process(about))
|
||||
.lineLimit(3)
|
||||
@@ -57,7 +57,7 @@ struct FollowersView: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers")
|
||||
.navigationBarTitle(NSLocalizedString("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers", comment: "Navigation bar title for view that shows who is following a user."))
|
||||
.onAppear {
|
||||
followers.subscribe()
|
||||
}
|
||||
@@ -91,7 +91,7 @@ struct FollowingView: View {
|
||||
.onDisappear {
|
||||
following.unsubscribe()
|
||||
}
|
||||
.navigationBarTitle("\(who) following")
|
||||
.navigationBarTitle(NSLocalizedString("\(who) following", comment: "Navigation bar title for view that shows who a user is following."))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,21 +122,21 @@ struct LoginView: View {
|
||||
ZStack(alignment: .top) {
|
||||
DamusGradient()
|
||||
VStack {
|
||||
Text("Login")
|
||||
Text("Login", comment: "Title of view to log into an account.")
|
||||
.foregroundColor(.white)
|
||||
.font(.title)
|
||||
.padding()
|
||||
|
||||
Text("Enter your account key to login:")
|
||||
Text("Enter your account key to login:", comment: "Prompt for user to enter an account key to login.")
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
|
||||
KeyInput("nsec1...", key: $key)
|
||||
KeyInput(NSLocalizedString("nsec1...", comment: "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."), key: $key)
|
||||
|
||||
let parsed = parse_key(key)
|
||||
|
||||
if parsed?.is_hex ?? false {
|
||||
Text("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.")
|
||||
Text("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.", comment: "Warning that the inputted account key for login is an old-style and asking user to verify if it is a public key.")
|
||||
.font(.subheadline.bold())
|
||||
.foregroundColor(.white)
|
||||
PubkeySwitch(isOn: $is_pubkey)
|
||||
@@ -150,15 +150,15 @@ struct LoginView: View {
|
||||
}
|
||||
|
||||
if parsed?.is_pub ?? false {
|
||||
Text("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.")
|
||||
Text("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.", comment: "Warning that the inputted account key is a public key and the result of what happens because of it.")
|
||||
.foregroundColor(.white)
|
||||
.padding()
|
||||
}
|
||||
|
||||
if let p = parsed {
|
||||
DamusWhiteButton("Login") {
|
||||
DamusWhiteButton(NSLocalizedString("Login", comment: "Button to log into account.")) {
|
||||
if !process_login(p, is_pubkey: self.is_pubkey) {
|
||||
self.error = "Invalid key"
|
||||
self.error = NSLocalizedString("Invalid key", comment: "Error message indicating that an invalid account key was entered for login.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,7 @@ struct PubkeySwitch: View {
|
||||
var body: some View {
|
||||
HStack {
|
||||
Toggle(isOn: $isOn) {
|
||||
Text("Public Key?")
|
||||
Text("Public Key?", comment: "Prompt to ask user if the key they entered is a public key.")
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ struct MentionView: View {
|
||||
let pk = bech32_pubkey(mention.ref.ref_id) ?? mention.ref.ref_id
|
||||
PubkeyView(pubkey: pk, relay: mention.ref.relay_id)
|
||||
case .event:
|
||||
Text("< e >")
|
||||
Text("< e >", comment: "Placeholder for event mention.")
|
||||
//EventBlockView(pubkey: mention.ref.ref_id, relay: mention.ref.relay_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
||||
if is_image_url(url) {
|
||||
// Append Image
|
||||
img_urls.append(url)
|
||||
return str
|
||||
} else {
|
||||
link_urls.append(url)
|
||||
return str + url.absoluteString
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,12 +61,13 @@ struct NoteContentView: View {
|
||||
let privkey: String?
|
||||
let event: NostrEvent
|
||||
let profiles: Profiles
|
||||
let previews: PreviewCache
|
||||
|
||||
let show_images: Bool
|
||||
|
||||
@State var artifacts: NoteArtifacts
|
||||
|
||||
@State var metaData: LPLinkMetadata? = nil
|
||||
@State var preview: LinkViewRepresentable? = nil
|
||||
let size: EventViewKind
|
||||
|
||||
func MainContent() -> some View {
|
||||
@@ -89,12 +90,14 @@ struct NoteContentView: View {
|
||||
InvoicesView(invoices: artifacts.invoices)
|
||||
}
|
||||
|
||||
if show_images, self.metaData != nil {
|
||||
LinkViewRepresentable(metadata: self.metaData)
|
||||
if show_images, self.preview != nil {
|
||||
self.preview
|
||||
} else {
|
||||
ForEach(artifacts.links, id:\.self) { link in
|
||||
LinkViewRepresentable(url: link)
|
||||
.frame(height: 50)
|
||||
if let url = link {
|
||||
LinkViewRepresentable(meta: .url(url))
|
||||
.frame(height: 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,7 +105,6 @@ struct NoteContentView: View {
|
||||
|
||||
var body: some View {
|
||||
MainContent()
|
||||
.animation(.easeInOut, value: metaData)
|
||||
.onAppear() {
|
||||
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
|
||||
}
|
||||
@@ -123,9 +125,22 @@ struct NoteContentView: View {
|
||||
}
|
||||
}
|
||||
.task {
|
||||
if let preview = previews.lookup(self.event.id) {
|
||||
switch preview {
|
||||
case .value(let view):
|
||||
self.preview = view
|
||||
case .failed:
|
||||
// don't try to refetch meta if we've failed
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if show_images, artifacts.links.count == 1 {
|
||||
let meta = await getMetaData(for: artifacts.links.first!)
|
||||
|
||||
self.metaData = await getMetaData(for: artifacts.links.first!)
|
||||
let view = meta.map { LinkViewRepresentable(meta: .linkmeta($0)) }
|
||||
previews.store(evid: self.event.id, preview: view)
|
||||
self.preview = view
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,8 +183,8 @@ func mention_str(_ m: Mention, profiles: Profiles) -> String {
|
||||
struct NoteContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
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: content, images: [], invoices: [], links: [])
|
||||
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,29 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
let BUTTON_SIZE = 57.0
|
||||
let LINEAR_GRADIENT = LinearGradient(gradient: Gradient(colors: [
|
||||
Color("DamusPurple"),
|
||||
Color("DamusBlue")
|
||||
]), startPoint: .topTrailing, endPoint: .bottomTrailing)
|
||||
|
||||
func PostButton(action: @escaping () -> ()) -> some View {
|
||||
return Button(action: action, label: {
|
||||
Text("+")
|
||||
.font(.system(.largeTitle))
|
||||
.frame(width: 57, height: 50)
|
||||
.foregroundColor(Color.white)
|
||||
.padding(.bottom, 7)
|
||||
ZStack(alignment: .center) {
|
||||
Circle()
|
||||
.fill(LINEAR_GRADIENT)
|
||||
.frame(width: BUTTON_SIZE, height: BUTTON_SIZE, alignment: .center)
|
||||
.rotationEffect(.degrees(20))
|
||||
.padding()
|
||||
.shadow(color: Color.black.opacity(0.3),
|
||||
radius: 3,
|
||||
x: 3,
|
||||
y: 3)
|
||||
Image(systemName: "plus")
|
||||
.font(.system(.title2))
|
||||
.foregroundColor(Color.white)
|
||||
}
|
||||
})
|
||||
.background(Color.blue)
|
||||
.cornerRadius(38.5)
|
||||
.padding()
|
||||
.shadow(color: Color.black.opacity(0.3),
|
||||
radius: 3,
|
||||
x: 3,
|
||||
y: 3)
|
||||
.keyboardShortcut("n", modifiers: [.command, .shift])
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ struct PostView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Button("Cancel") {
|
||||
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of posting a note.")) {
|
||||
self.cancel()
|
||||
}
|
||||
.foregroundColor(.primary)
|
||||
@@ -63,7 +63,7 @@ struct PostView: View {
|
||||
Spacer()
|
||||
|
||||
if !is_post_empty {
|
||||
Button("Post") {
|
||||
Button(NSLocalizedString("Post", comment: "Button to post a note.")) {
|
||||
self.send_post()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,76 +7,73 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ProfileFullName: View {
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
let contacts: Contacts
|
||||
|
||||
@State var display_name: String?
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if let real_name = profile?.display_name {
|
||||
Text(real_name)
|
||||
.bold()
|
||||
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", contacts: contacts, show_friend_confirmed: true)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
// ProfileName(pubkey: pubkey, profile: profile, contacts: contacts, show_friend_confirmed: true)
|
||||
}
|
||||
}
|
||||
func get_friend_icon(contacts: Contacts, pubkey: String, show_confirmed: Bool) -> String? {
|
||||
if !show_confirmed {
|
||||
return nil
|
||||
}
|
||||
|
||||
if contacts.is_friend_or_self(pubkey) {
|
||||
return "person.fill.checkmark"
|
||||
}
|
||||
|
||||
if contacts.is_friend_of_friend(pubkey) {
|
||||
return "person.fill.and.arrow.left.and.arrow.right"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
struct ProfileName: View {
|
||||
let damus_state: DamusState
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
let contacts: Contacts
|
||||
let prefix: String
|
||||
|
||||
let show_friend_confirmed: Bool
|
||||
|
||||
@State var display_name: String?
|
||||
@State var nip05: NIP05?
|
||||
|
||||
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool) {
|
||||
init(pubkey: String, profile: Profile?, damus: DamusState, show_friend_confirmed: Bool) {
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = ""
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.damus_state = damus
|
||||
}
|
||||
|
||||
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool) {
|
||||
init(pubkey: String, profile: Profile?, prefix: String, damus: DamusState, show_friend_confirmed: Bool) {
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = prefix
|
||||
self.contacts = contacts
|
||||
self.damus_state = damus
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
}
|
||||
|
||||
var friend_icon: String? {
|
||||
if !show_friend_confirmed {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.contacts.is_friend(self.pubkey) {
|
||||
return "person.fill.checkmark"
|
||||
}
|
||||
|
||||
if self.contacts.is_friend_of_friend(self.pubkey) {
|
||||
return "person.fill.and.arrow.left.and.arrow.right"
|
||||
}
|
||||
|
||||
return nil
|
||||
return get_friend_icon(contacts: damus_state.contacts, pubkey: pubkey, show_confirmed: show_friend_confirmed)
|
||||
}
|
||||
|
||||
var current_nip05: NIP05? {
|
||||
nip05 ?? damus_state.profiles.is_validated(pubkey)
|
||||
}
|
||||
|
||||
var nip05_color: Color {
|
||||
return get_nip05_color(pubkey: pubkey, contacts: damus_state.contacts)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
HStack(spacing: 2) {
|
||||
Text(prefix + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
|
||||
.font(.body)
|
||||
.fontWeight(prefix == "@" ? .none : .bold)
|
||||
if let friend = friend_icon {
|
||||
if let nip05 = current_nip05 {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.foregroundColor(nip05_color)
|
||||
Text(nip05.host)
|
||||
.foregroundColor(nip05_color)
|
||||
}
|
||||
if let friend = friend_icon, current_nip05 == nil {
|
||||
Image(systemName: friend)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
@@ -87,62 +84,57 @@ struct ProfileName: View {
|
||||
return
|
||||
}
|
||||
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
||||
nip05 = damus_state.profiles.is_validated(pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Profile Name used when displaying an event in the timeline
|
||||
struct EventProfileName: View {
|
||||
let damus_state: DamusState
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
let contacts: Contacts
|
||||
let prefix: String
|
||||
|
||||
let show_friend_confirmed: Bool
|
||||
|
||||
@State var display_name: String?
|
||||
@State var nip05: NIP05?
|
||||
|
||||
let size: EventViewKind
|
||||
|
||||
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
|
||||
init(pubkey: String, profile: Profile?, damus: DamusState, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
|
||||
self.damus_state = damus
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = ""
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.size = size
|
||||
}
|
||||
|
||||
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
|
||||
init(pubkey: String, profile: Profile?, prefix: String, damus: DamusState, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
|
||||
self.damus_state = damus
|
||||
self.pubkey = pubkey
|
||||
self.profile = profile
|
||||
self.prefix = prefix
|
||||
self.contacts = contacts
|
||||
self.show_friend_confirmed = show_friend_confirmed
|
||||
self.size = size
|
||||
}
|
||||
|
||||
var friend_icon: String? {
|
||||
if !show_friend_confirmed {
|
||||
return nil
|
||||
}
|
||||
|
||||
if self.contacts.is_friend(self.pubkey) {
|
||||
return "person.fill.checkmark"
|
||||
}
|
||||
|
||||
if self.contacts.is_friend_of_friend(self.pubkey) {
|
||||
return "person.fill.and.arrow.left.and.arrow.right"
|
||||
}
|
||||
|
||||
return nil
|
||||
return get_friend_icon(contacts: damus_state.contacts, pubkey: pubkey, show_confirmed: show_friend_confirmed)
|
||||
}
|
||||
|
||||
var current_nip05: NIP05? {
|
||||
nip05 ?? damus_state.profiles.is_validated(pubkey)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
HStack(spacing: 2) {
|
||||
if let real_name = profile?.display_name {
|
||||
Text(real_name)
|
||||
.font(.body.weight(.bold))
|
||||
.padding([.trailing], 2)
|
||||
|
||||
Text("@" + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
|
||||
.foregroundColor(.gray)
|
||||
@@ -153,7 +145,12 @@ struct EventProfileName: View {
|
||||
.fontWeight(.bold)
|
||||
}
|
||||
|
||||
if let frend = friend_icon {
|
||||
if let _ = current_nip05 {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.foregroundColor(get_nip05_color(pubkey: pubkey, contacts: damus_state.contacts))
|
||||
}
|
||||
|
||||
if let frend = friend_icon, current_nip05 == nil {
|
||||
Label("", systemImage: frend)
|
||||
.foregroundColor(.gray)
|
||||
.font(.footnote)
|
||||
@@ -165,6 +162,11 @@ struct EventProfileName: View {
|
||||
return
|
||||
}
|
||||
display_name = Profile.displayName(profile: update.profile, pubkey: pubkey)
|
||||
nip05 = damus_state.profiles.is_validated(pubkey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func get_nip05_color(pubkey: String, contacts: Contacts) -> Color {
|
||||
return contacts.is_friend_or_self(pubkey) ? .accentColor : .gray
|
||||
}
|
||||
|
||||
@@ -51,15 +51,19 @@ struct InnerProfilePicView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
ZStack {
|
||||
Color(uiColor: .systemBackground)
|
||||
|
||||
KFAnimatedImage(url)
|
||||
.callbackQueue(.dispatch(.global(qos: .background)))
|
||||
.processingQueue(.dispatch(.global(qos: .background)))
|
||||
.appendProcessor(LargeImageProcessor())
|
||||
.configure { view in
|
||||
view.framePreloadCount = 1
|
||||
}
|
||||
.placeholder { _ in
|
||||
Placeholder
|
||||
}
|
||||
.cacheOriginalImage()
|
||||
.scaleFactor(UIScreen.main.scale)
|
||||
.loadDiskFileSynchronously()
|
||||
.fade(duration: 0.1)
|
||||
@@ -71,7 +75,6 @@ struct InnerProfilePicView: View {
|
||||
}
|
||||
|
||||
struct ProfilePicView: View {
|
||||
|
||||
let pubkey: String
|
||||
let size: CGFloat
|
||||
let highlight: Highlight
|
||||
@@ -103,6 +106,33 @@ struct ProfilePicView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct LargeImageProcessor: ImageProcessor {
|
||||
|
||||
let identifier: String = "com.damus.largeimageprocessor"
|
||||
let maxSize: Int = 1000000
|
||||
let downsampleSize = CGSize(width: 200, height: 200)
|
||||
|
||||
func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
|
||||
|
||||
switch item {
|
||||
case .image(let image):
|
||||
guard let data = image.kf.data(format: .unknown) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if data.count > maxSize {
|
||||
return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor)
|
||||
}
|
||||
return image
|
||||
case .data(let data):
|
||||
if data.count > maxSize {
|
||||
return KingfisherWrapper.downsampledImage(data: data, to: downsampleSize, scale: options.scaleFactor)
|
||||
}
|
||||
return KFCrossPlatformImage(data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func get_profile_url(picture: String?, pubkey: String, profiles: Profiles) -> URL {
|
||||
let pic = picture ?? profiles.lookup(id: pubkey)?.picture ?? robohash(pubkey)
|
||||
if let url = URL(string: pic) {
|
||||
|
||||
@@ -48,27 +48,24 @@ func follow_btn_enabled_state(_ fs: FollowState) -> Bool {
|
||||
struct ProfileNameView: View {
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
let contacts: Contacts
|
||||
let damus: DamusState
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let real_name = profile?.display_name {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text(real_name)
|
||||
.font(.title3.weight(.bold))
|
||||
|
||||
KeyView(pubkey: pubkey)
|
||||
.pubkey_context_menu(bech32_pubkey: pubkey)
|
||||
}
|
||||
ProfileName(pubkey: pubkey, profile: profile, prefix: "@", contacts: contacts, show_friend_confirmed: true)
|
||||
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 {
|
||||
HStack {
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: contacts, show_friend_confirmed: true)
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -84,30 +81,25 @@ struct EditButton: View {
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(destination: EditMetadataView(damus_state: damus_state)) {
|
||||
Text("Edit")
|
||||
.padding(.horizontal, 25)
|
||||
.padding(.vertical, 10)
|
||||
Text("Edit", comment: "Button to edit user's profile.")
|
||||
.frame(height: 30)
|
||||
.padding(.horizontal,25)
|
||||
.font(.caption.weight(.bold))
|
||||
.foregroundColor(fillColor())
|
||||
.background(emptyColor())
|
||||
.cornerRadius(20)
|
||||
.cornerRadius(24)
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.stroke(borderColor(), lineWidth: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fillColor() -> Color {
|
||||
colorScheme == .light ? .black : .white
|
||||
}
|
||||
|
||||
func emptyColor() -> Color {
|
||||
colorScheme == .light ? .white : .black
|
||||
colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite")
|
||||
}
|
||||
|
||||
func borderColor() -> Color {
|
||||
colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.2)
|
||||
colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,12 +113,29 @@ struct ProfileView: View {
|
||||
@State private var showingEditProfile = false
|
||||
@State var showing_select_wallet: Bool = false
|
||||
@State var is_zoomed: Bool = false
|
||||
@State var show_share_sheet: Bool = false
|
||||
@StateObject var user_settings = UserSettingsStore()
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
//@EnvironmentObject var profile: ProfileModel
|
||||
// We just want to have a white "< Home" text here, however,
|
||||
// setting the initialiser is causing issues, and it's late.
|
||||
// Ref: https://blog.techchee.com/navigation-bar-title-style-color-and-custom-back-button-in-swiftui/
|
||||
/*
|
||||
init(damus_state: DamusState, zoom_size: CGFloat = 350) {
|
||||
self.damus_state = damus_state
|
||||
self.zoom_size = zoom_size
|
||||
Theme.navigationBarColors(background: nil, titleColor: .white, tintColor: nil)
|
||||
}*/
|
||||
|
||||
func fillColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusLightGrey") : Color("DamusDarkGrey")
|
||||
}
|
||||
|
||||
func imageBorderColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusWhite") : Color("DamusBlack")
|
||||
}
|
||||
|
||||
func LNButton(lnurl: String, profile: Profile) -> some View {
|
||||
Button(action: {
|
||||
@@ -138,23 +147,38 @@ struct ProfileView: View {
|
||||
}) {
|
||||
Image(systemName: "bolt.circle")
|
||||
.symbolRenderingMode(.palette)
|
||||
.font(.system(size: 34).weight(.thin))
|
||||
.foregroundStyle(colorScheme == .light ? .black : .white, colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.2))
|
||||
.foregroundStyle(colorScheme == .dark ? .white : .black, colorScheme == .dark ? .white : .black)
|
||||
.font(.system(size: 32).weight(.thin))
|
||||
.contextMenu {
|
||||
Button {
|
||||
UIPasteboard.general.string = profile.lnurl ?? ""
|
||||
} label: {
|
||||
Label("Copy LNURL", systemImage: "doc.on.doc")
|
||||
Label(NSLocalizedString("Copy LNURL", comment: "Context menu option for copying a user's Lightning URL."), systemImage: "doc.on.doc")
|
||||
}
|
||||
}
|
||||
}.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
|
||||
|
||||
}
|
||||
.cornerRadius(24)
|
||||
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
|
||||
SelectWalletView(showingSelectWallet: $showing_select_wallet, invoice: lnurl)
|
||||
.environmentObject(user_settings)
|
||||
}
|
||||
}
|
||||
|
||||
static let markdown = Markdown()
|
||||
|
||||
|
||||
var ShareButton: some View {
|
||||
Button(action: {
|
||||
show_share_sheet = true
|
||||
}) {
|
||||
Image(systemName: "square.and.arrow.up.circle.fill")
|
||||
.symbolRenderingMode(.palette)
|
||||
.font(.system(size: 32))
|
||||
.padding()
|
||||
.foregroundStyle(.white, .black, .black.opacity(0.8))
|
||||
}
|
||||
}
|
||||
|
||||
var DMButton: some View {
|
||||
let dm_model = damus_state.dms.lookup_or_create(profile.pubkey)
|
||||
let dmview = DMChatView(damus_state: damus_state, pubkey: profile.pubkey)
|
||||
@@ -162,115 +186,133 @@ struct ProfileView: View {
|
||||
return NavigationLink(destination: dmview) {
|
||||
Image(systemName: "bubble.left.circle")
|
||||
.symbolRenderingMode(.palette)
|
||||
.font(.system(size: 34).weight(.thin))
|
||||
.foregroundStyle(colorScheme == .light ? .black : .white, colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.2))
|
||||
.font(.system(size: 32).weight(.thin))
|
||||
.foregroundStyle(colorScheme == .dark ? .white : .black, colorScheme == .dark ? .white : .black)
|
||||
}
|
||||
}
|
||||
|
||||
var TopSection: some View {
|
||||
VStack(alignment: .leading) {
|
||||
let data = damus_state.profiles.lookup(id: profile.pubkey)
|
||||
|
||||
HStack(alignment: .center) {
|
||||
ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .none, profiles: damus_state.profiles)
|
||||
.onTapGesture {
|
||||
is_zoomed.toggle()
|
||||
}
|
||||
.sheet(isPresented: $is_zoomed) {
|
||||
ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if let profile = data {
|
||||
if let lnurl = profile.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 {
|
||||
NavigationLink(destination: EditMetadataView(damus_state: damus_state)) {
|
||||
EditButton(damus_state: damus_state)
|
||||
}
|
||||
}
|
||||
ZStack(alignment: .top) {
|
||||
GeometryReader { geo in
|
||||
Image("profile-banner")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: geo.size.width, height: 150)
|
||||
.clipped()
|
||||
|
||||
ShareButton
|
||||
.offset(x: geo.size.width - 80.0, y: 50.0 )
|
||||
}
|
||||
|
||||
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts)
|
||||
.padding(.bottom)
|
||||
|
||||
Text(ProfileView.markdown.process(data?.about ?? ""))
|
||||
.font(.subheadline)
|
||||
|
||||
Divider()
|
||||
VStack(alignment: .leading) {
|
||||
let data = damus_state.profiles.lookup(id: profile.pubkey)
|
||||
let pfp_size: CGFloat = 90.0
|
||||
|
||||
HStack {
|
||||
if let contact = profile.contacts {
|
||||
let contacts = contact.referenced_pubkeys.map { $0.ref_id }
|
||||
let following_model = FollowingModel(damus_state: damus_state, contacts: contacts)
|
||||
NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model, whos: profile.pubkey)) {
|
||||
HStack {
|
||||
Text("\(profile.following)")
|
||||
.font(.subheadline.weight(.medium))
|
||||
Text("Following")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
let fview = FollowersView(damus_state: damus_state, whos: profile.pubkey)
|
||||
.environmentObject(followers)
|
||||
if followers.contacts != nil {
|
||||
NavigationLink(destination: fview) {
|
||||
FollowersCount
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
} else {
|
||||
FollowersCount
|
||||
HStack(alignment: .center) {
|
||||
ProfilePicView(pubkey: profile.pubkey, size: pfp_size, highlight: .custom(imageBorderColor(), 4.0), profiles: damus_state.profiles)
|
||||
.onTapGesture {
|
||||
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||
followers.contacts = []
|
||||
followers.subscribe()
|
||||
is_zoomed.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
if let relays = profile.relays {
|
||||
NavigationLink(destination: UserRelaysView(state: damus_state, pubkey: profile.pubkey, relays: Array(relays.keys).sorted())) {
|
||||
Text("\(relays.keys.count)")
|
||||
.font(.subheadline.weight(.medium))
|
||||
Text("Relays")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
.sheet(isPresented: $is_zoomed) {
|
||||
ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles)
|
||||
}
|
||||
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
|
||||
|
||||
Spacer()
|
||||
|
||||
Group {
|
||||
|
||||
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(data?.about ?? ""))
|
||||
.font(.subheadline)
|
||||
|
||||
Divider()
|
||||
|
||||
HStack {
|
||||
if let contact = profile.contacts {
|
||||
let contacts = contact.referenced_pubkeys.map { $0.ref_id }
|
||||
let following_model = FollowingModel(damus_state: damus_state, contacts: contacts)
|
||||
NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model, whos: profile.pubkey)) {
|
||||
HStack {
|
||||
Text("\(Text("\(profile.following)", comment: "Number of profiles a user is following.").font(.subheadline.weight(.medium))) \(Text("Following", comment: "Part of a larger sentence to describe how many profiles a user is following.").font(.subheadline).foregroundColor(.gray))", comment: "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'.")
|
||||
}
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
let fview = FollowersView(damus_state: damus_state, whos: profile.pubkey)
|
||||
.environmentObject(followers)
|
||||
if followers.contacts != nil {
|
||||
NavigationLink(destination: fview) {
|
||||
FollowersCount
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
} else {
|
||||
FollowersCount
|
||||
.onTapGesture {
|
||||
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||
followers.contacts = []
|
||||
followers.subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
if let relays = profile.relays {
|
||||
NavigationLink(destination: UserRelaysView(state: damus_state, pubkey: profile.pubkey, relays: Array(relays.keys).sorted())) {
|
||||
Text("\(Text("\(relays.keys.count)", comment: "Number of relay servers a user is connected.").font(.subheadline.weight(.medium))) \(Text("Relays", comment: "Part of a larger sentence to describe how many relay servers a user is connected.").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 'Relays'.")
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
}
|
||||
.padding(.horizontal,18)
|
||||
//.offset(y:120)
|
||||
.padding(.top,150)
|
||||
}
|
||||
}
|
||||
|
||||
var FollowersCount: some View {
|
||||
HStack {
|
||||
Text("\(followers.count_display)")
|
||||
.font(.subheadline.weight(.medium))
|
||||
Text("Followers")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
if followers.count_display == "?" {
|
||||
Image(systemName: "square.and.arrow.down")
|
||||
Text("Followers", comment: "Label describing followers of a user.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
Text("\(Text("\(followers.count_display)", comment: "Number of people following a user.").font(.subheadline.weight(.medium))) \(Text("Followers", comment: "Part of a larger sentence to describe how many people are following a user.").font(.subheadline).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many people are following a user. In source English, the first variable is the number of followers, and the second variable is 'Followers'.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
ScrollView {
|
||||
TopSection
|
||||
.padding(.horizontal)
|
||||
|
||||
Divider()
|
||||
|
||||
@@ -291,6 +333,14 @@ struct ProfileView: View {
|
||||
followers.unsubscribe()
|
||||
// our profilemodel needs a bit more help
|
||||
}
|
||||
.sheet(isPresented: $show_share_sheet) {
|
||||
if let npub = bech32_pubkey(profile.pubkey) {
|
||||
if let url = URL(string: "https://damus.io/" + npub) {
|
||||
ShareSheet(activityItems: [url])
|
||||
}
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,9 +356,9 @@ struct ProfileView_Previews: PreviewProvider {
|
||||
|
||||
func test_damus_state() -> DamusState {
|
||||
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||
let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel())
|
||||
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(), previews: PreviewCache())
|
||||
|
||||
let prof = Profile(name: "damus", display_name: "Damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", 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", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io")
|
||||
let tsprof = TimestampedProfile(profile: prof, timestamp: 0)
|
||||
damus.profiles.add(id: pubkey, profile: tsprof)
|
||||
return damus
|
||||
@@ -321,20 +371,72 @@ struct KeyView: View {
|
||||
|
||||
@State private var isCopied = false
|
||||
|
||||
func fillColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusLightGrey") : Color("DamusDarkGrey")
|
||||
}
|
||||
|
||||
func keyColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite")
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let col = id_to_color(pubkey)
|
||||
let bech32 = bech32_pubkey(pubkey) ?? pubkey
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32
|
||||
isCopied = true
|
||||
} label: {
|
||||
Label(isCopied ? "Copied" : "", systemImage: "key.fill")
|
||||
.font(isCopied ? .caption : .system(size: 15).weight(.light))
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.foregroundColor(isCopied ? .gray : col)
|
||||
HStack {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.frame(width: 275, height:22)
|
||||
.foregroundColor(fillColor())
|
||||
.overlay(
|
||||
HStack {
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
isCopied = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
isCopied = false
|
||||
}
|
||||
} label: {
|
||||
Label(NSLocalizedString("Public Key", comment: "Label indicating that the text is a user's public account key."), systemImage: "key.fill")
|
||||
.font(.custom("key", size: 12.0))
|
||||
.labelStyle(IconOnlyLabelStyle())
|
||||
.foregroundStyle(hex_to_rgb(pubkey))
|
||||
.symbolRenderingMode(.palette)
|
||||
}
|
||||
.padding(.leading,4)
|
||||
Text(abbrev_pubkey(bech32, amount: 16))
|
||||
.font(.footnote)
|
||||
.foregroundColor(keyColor())
|
||||
.offset(x:-3) // Not sure why this is needed.
|
||||
}
|
||||
)
|
||||
if isCopied != true {
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
isCopied = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
isCopied = false
|
||||
}
|
||||
} label: {
|
||||
Label {
|
||||
Text("Public key", comment: "Label indicating that the text is a user's public account key.")
|
||||
} icon: {
|
||||
Image("ic-copy")
|
||||
.contentShape(Rectangle())
|
||||
.frame(width: 20, height: 20)
|
||||
}
|
||||
.labelStyle(IconOnlyLabelStyle())
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
} else {
|
||||
HStack {
|
||||
Image("ic-tick")
|
||||
.frame(width: 20, height: 20)
|
||||
Text(NSLocalizedString("Copied", comment: "Label indicating that a user's key was copied."))
|
||||
.font(.footnote)
|
||||
.foregroundColor(Color("DamusGreen"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ struct RecommendedRelayView: View {
|
||||
Spacer()
|
||||
if let ev = damus.contacts.event, add_button {
|
||||
if let privkey = damus.keypair.privkey {
|
||||
Button("Add") {
|
||||
Button(NSLocalizedString("Add", comment: "Button to add recommended relay server.")) {
|
||||
guard let ev = add_relay(ev: ev, privkey: privkey, current_relays: damus.pool.descriptors, relay: relay, info: .rw) else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ struct RelayView: View {
|
||||
Button {
|
||||
UIPasteboard.general.setValue(relay, forPasteboardType: "public.plain-text")
|
||||
} label: {
|
||||
Label("Copy", systemImage: "doc.on.doc")
|
||||
Label(NSLocalizedString("Copy", comment: "Button to copy a relay server address."), systemImage: "doc.on.doc")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ struct RelayView: View {
|
||||
process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: new_ev)
|
||||
state.pool.send(.event(new_ev))
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
Label(NSLocalizedString("Delete", comment: "Button to delete a relay server that the user connects to."), systemImage: "trash")
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ struct ReplyQuoteView: View {
|
||||
let quoter: NostrEvent
|
||||
let event_id: String
|
||||
let profiles: Profiles
|
||||
let previews: PreviewCache
|
||||
|
||||
@EnvironmentObject var thread: ThreadModel
|
||||
|
||||
@@ -31,7 +32,7 @@ struct ReplyQuoteView: View {
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
NoteContentView(privkey: privkey, event: event, profiles: profiles, show_images: false, artifacts: .just_content(event.content), size: .normal)
|
||||
NoteContentView(privkey: privkey, event: event, profiles: profiles, previews: previews, show_images: false, artifacts: .just_content(event.content), size: .normal)
|
||||
.font(.callout)
|
||||
.foregroundColor(.accentColor)
|
||||
|
||||
@@ -58,7 +59,7 @@ 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)
|
||||
ReplyQuoteView(privkey: s.keypair.privkey, quoter: quoter, event_id: "pubkey2", profiles: s.profiles, previews: PreviewCache())
|
||||
.environmentObject(ThreadModel(event: quoter, damus_state: s))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ struct ReplyView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Replying to:")
|
||||
Text("Replying to:", comment: "Indicating that the user is replying to the following listed people.")
|
||||
HStack(alignment: .top) {
|
||||
let names = all_referenced_pubkeys(replying_to)
|
||||
.map { pubkey in
|
||||
@@ -33,7 +33,9 @@ struct ReplyView: View {
|
||||
.foregroundColor(.gray)
|
||||
.font(.footnote)
|
||||
}
|
||||
EventView(event: replying_to, highlight: .none, has_action_bar: false, damus: damus, show_friend_icon: true)
|
||||
ScrollView {
|
||||
EventView(event: replying_to, highlight: .none, has_action_bar: false, damus: damus, show_friend_icon: true)
|
||||
}
|
||||
PostView(replying_to: replying_to, references: gather_reply_ids(our_pubkey: damus.pubkey, from: replying_to))
|
||||
}
|
||||
.padding()
|
||||
|
||||
@@ -21,21 +21,21 @@ struct SaveKeysView: View {
|
||||
DamusGradient()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
Text("Welcome, \(account.rendered_name)!")
|
||||
Text("Welcome, \(account.rendered_name)!", comment: "Text to welcome user.")
|
||||
.font(.title.bold())
|
||||
.foregroundColor(.white)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Text("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.")
|
||||
Text("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.", comment: "Reminder to user that they should save their account information.")
|
||||
.foregroundColor(.white)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Text("Public Key")
|
||||
Text("Public Key", comment: "Label to indicate that text below is the user's public key used by others to uniquely refer to the user.")
|
||||
.font(.title2.bold())
|
||||
.foregroundColor(.white)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Text("This is your account ID, you can give this to your friends so that they can follow you. Click to copy.")
|
||||
Text("This is your account ID, you can give this to your friends so that they can follow you. Click to copy.", comment: "Label to describe that a public key is the user's account ID and what they can do with it.")
|
||||
.foregroundColor(.white)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
@@ -43,12 +43,12 @@ struct SaveKeysView: View {
|
||||
.padding(.bottom, 10)
|
||||
|
||||
if pub_copied {
|
||||
Text("Private Key")
|
||||
Text("Private Key", comment: "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.")
|
||||
.font(.title2.bold())
|
||||
.foregroundColor(.white)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Text("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!")
|
||||
Text("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!", comment: "Label to describe that a private key is the user's secret account key and what they should do with it.")
|
||||
.foregroundColor(.white)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
@@ -61,7 +61,7 @@ struct SaveKeysView: View {
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
} else if let err = error {
|
||||
Text("Error: \(err)")
|
||||
Text("Error: \(err)", comment: "Error message indicating why saving keys failed.")
|
||||
.foregroundColor(.red)
|
||||
DamusWhiteButton("Retry") {
|
||||
complete_account_creation(account)
|
||||
|
||||
@@ -16,12 +16,12 @@ struct SearchHomeView: View {
|
||||
var SearchInput: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
HStack{
|
||||
TextField("Search...", text: $search)
|
||||
TextField(NSLocalizedString("Search...", comment: "Placeholder text to prompt entry of search query."), text: $search)
|
||||
.padding(8)
|
||||
.padding(.leading, 35)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
Text("Cancel")
|
||||
Text("Cancel", comment: "Cancel out of search view.")
|
||||
.foregroundColor(.blue)
|
||||
.padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0))
|
||||
.opacity((search == "") ? 0.0 : 1.0)
|
||||
@@ -55,7 +55,7 @@ struct SearchHomeView: View {
|
||||
// Fetch new information by unsubscribing and resubscribing to the relay
|
||||
model.unsubscribe()
|
||||
model.subscribe()
|
||||
}.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
var MainContent: some View {
|
||||
|
||||