Compare commits

..

1 Commits

Author SHA1 Message Date
tyiu bb430fbd65 Fix flakey TimeAgo test by anchoring locale to en_US 2023-01-01 22:38:25 -05:00
127 changed files with 773 additions and 4285 deletions
-33
View File
@@ -1,33 +0,0 @@
name: Nostr PR Notifier
on:
pull_request:
types: [opened, reopened]
jobs:
Notify:
runs-on: ubuntu-latest
env:
# Set in Github repo > Settings > Secrets and Variables
NOSTR_KEY: ${{ secrets.NOSTR_KEY }}
# Event vars
PR_URL: ${{ github.event.pull_request.html_url }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
PR_BODY: ${{ github.event.pull_request.body }}
steps:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: '1.19.0'
- run: go install github.com/fiatjaf/noscl@latest
- run: which noscl
- run: noscl setprivate $NOSTR_KEY
- run: noscl relay add wss://relay.damus.io
- run: noscl relay add wss://brb.io
- run: noscl relay add wss://relay.nostr.info
- run: noscl relay add wss://nostr.zebedee.cloud
- run: noscl relay add wss://nostr.orangepill.dev
- run: noscl relay add wss://nostr.v0l.io
- run: noscl relay add wss://nostr.fmt.wiz.biz
- run: |
msg=$(printf "Pull request opened by $PR_AUTHOR\n$PR_TITLE\n$PR_URL") \
&& noscl publish "$msg"
+1 -88
View File
@@ -1,93 +1,8 @@
## [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
- Parse links in profiles (Lionello Lunesu)
- Parse links in profiles (William Casarin)
- Added Breez wallet to wallet selector (Lee Salminen)
- Added Bitcoin Beach wallet to wallet selector (Lee Salminen)
- Added ability to copy relay urls (Matt Ward)
@@ -351,5 +266,3 @@
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2
+2 -2
View File
@@ -56,8 +56,8 @@ 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: 1 tildes `~strikethrough~`
- Code: 1 back-tick `` `code` ``
- Strikethrough: 2 tildes `~~strikethrough~~`
- Code: 1 back-tick ``code``
#### 💬 Encrypted DMs (chat app, bottom navigation)
- Tap the chat icon and you'll notice there's nothing to see at first. Go to a user profile and tap the 💬 chat icon next to the follow button to begin a DM
@@ -1,905 +0,0 @@
<?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" 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="CFBundleDisplayName" xml:space="preserve">
<source>Damus</source>
<target>Damus</target>
<note>Bundle display name</note>
</trans-unit>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>damus</source>
<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-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>
<body>
<trans-unit id=" " xml:space="preserve">
<source> </source>
<target> </target>
<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>Number of people following a user.</note>
</trans-unit>
<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>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>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>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>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>Fraction of how many of the user's relay servers that are operational.</note>
</trans-unit>
<trans-unit id="&amp;nbsp;" xml:space="preserve">
<source>&amp;nbsp;</source>
<target>&amp;nbsp;</target>
<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>
<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="&lt; e &gt;" xml:space="preserve">
<source>&lt; e &gt;</source>
<target>&lt; e &gt;</target>
<note>Placeholder for event mention.</note>
</trans-unit>
<trans-unit id="@" xml:space="preserve">
<source>@</source>
<target>@</target>
<note>Prefix character to username.</note>
</trans-unit>
<trans-unit id="About" xml:space="preserve">
<source>About</source>
<target>About</target>
<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>
<target>About Me</target>
<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>
<target>Absolute Boss</target>
<note>Placeholder text for About Me description.</note>
</trans-unit>
<trans-unit id="Account ID" xml:space="preserve">
<source>Account ID</source>
<target>Account ID</target>
<note>Label to indicate the public ID of the account.</note>
</trans-unit>
<trans-unit id="Add" xml:space="preserve">
<source>Add</source>
<target>Add</target>
<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>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>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>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>
<target>Bitcoin Beach</target>
<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>
<target>Bitcoin Lightning Tips</target>
<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>
<target>Blixt Wallet</target>
<note>Dropdown option label for Lightning wallet, Blixt Wallet</note>
</trans-unit>
<trans-unit id="Blue Wallet" xml:space="preserve">
<source>Blue Wallet</source>
<target>Blue Wallet</target>
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
</trans-unit>
<trans-unit id="Boost" xml:space="preserve">
<source>Boost</source>
<target>Boost</target>
<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>Text indicating that the post was boosted (i.e. re-shared).</note>
</trans-unit>
<trans-unit id="Breez" xml:space="preserve">
<source>Breez</source>
<target>Breez</target>
<note>Dropdown option label for Lightning wallet, Breez.</note>
</trans-unit>
<trans-unit id="Broadcast" xml:space="preserve">
<source>Broadcast</source>
<target>Broadcast</target>
<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>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>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>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>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>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>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>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>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>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>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>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>Title of section for copying a Lightning invoice identifier.</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve">
<source>Create</source>
<target>Create</target>
<note>Button to create account.</note>
</trans-unit>
<trans-unit id="Create Account" xml:space="preserve">
<source>Create Account</source>
<target>Create Account</target>
<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>
<target>Creator(s) of Bitcoin. Absolute legend.</target>
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
</trans-unit>
<trans-unit id="DM" xml:space="preserve">
<source>DM</source>
<target>DM</target>
<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>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>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>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>Button to dismiss a text field alert.</note>
</trans-unit>
<trans-unit id="Display Name" xml:space="preserve">
<source>Display Name</source>
<target>Display Name</target>
<note>Label to prompt display name entry.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
<target>Done</target>
<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>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>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>Title of navigation view for Edit Profile.</note>
</trans-unit>
<trans-unit id="Encrypted" xml:space="preserve">
<source>Encrypted</source>
<target>Encrypted</target>
<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>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>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>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>Filter state for seeing either only posts, or posts &amp; replies.</note>
</trans-unit>
<trans-unit id="Follow" xml:space="preserve">
<source>Follow</source>
<target>Follow</target>
<note>Button to follow a user.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve">
<source>Followers</source>
<target>Followers</target>
<note>Label describing followers of a user.</note>
</trans-unit>
<trans-unit id="Following" xml:space="preserve">
<source>Following</source>
<target>Following</target>
<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>
<target>Following...</target>
<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>
<target>Follows</target>
<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>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>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>Navigation link to go to profile.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve">
<source>Home</source>
<target>Home</target>
<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>
<target>LNLink</target>
<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>
<target>Let's go!</target>
<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>
<target>Lightning Address or LNURL</target>
<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>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>
<target>Local default</target>
<note>Dropdown option label for system default for Lightning wallet.</note>
</trans-unit>
<trans-unit id="Login" xml:space="preserve">
<source>Login</source>
<target>Login</target>
<note>Button to log into account.</note>
</trans-unit>
<trans-unit id="Logout" xml:space="preserve">
<source>Logout</source>
<target>Logout</target>
<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>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>
<target>Muun</target>
<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>
<target>NIP-05 Verification</target>
<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>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>Navigation title for notifications.</note>
</trans-unit>
<trans-unit id="Pay" xml:space="preserve">
<source>Pay</source>
<target>Pay</target>
<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>Navigation bar title for view to pay Lightning invoice.</note>
</trans-unit>
<trans-unit id="Phoenix" xml:space="preserve">
<source>Phoenix</source>
<target>Phoenix</target>
<note>Dropdown option label for Lightning wallet, Phoenix.</note>
</trans-unit>
<trans-unit id="Post" xml:space="preserve">
<source>Post</source>
<target>Post</target>
<note>Button to post a note.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve">
<source>Posts</source>
<target>Posts</target>
<note>Label for filter for seeing only posts (instead of posts and replies).</note>
</trans-unit>
<trans-unit id="Posts &amp; Replies" xml:space="preserve">
<source>Posts &amp; Replies</source>
<target>Posts &amp; Replies</target>
<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>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>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>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>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>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>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>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>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>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>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 %@ &amp; %@" xml:space="preserve">
<source>Replying to %1$@ &amp; %2$@</source>
<target>Replying to %1$@ &amp; %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>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>Section title for resetting the user</note>
</trans-unit>
<trans-unit id="River" xml:space="preserve">
<source>River</source>
<target>River</target>
<note>Dropdown option label for Lightning wallet, River</note>
</trans-unit>
<trans-unit id="Satoshi Nakamoto" xml:space="preserve">
<source>Satoshi Nakamoto</source>
<target>Satoshi Nakamoto</target>
<note>Name of Bitcoin creator(s).</note>
</trans-unit>
<trans-unit id="Save" xml:space="preserve">
<source>Save</source>
<target>Save</target>
<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>Navigation link to search hashtag.</note>
</trans-unit>
<trans-unit id="Search..." xml:space="preserve">
<source>Search...</source>
<target>Search...</target>
<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>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>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>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>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>Navigation title for Settings view.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Share</target>
<note>Button to share an image.</note>
</trans-unit>
<trans-unit id="Show" xml:space="preserve">
<source>Show</source>
<target>Show</target>
<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>Toggle to show or hide selection of wallet.</note>
</trans-unit>
<trans-unit id="Strike" xml:space="preserve">
<source>Strike</source>
<target>Strike</target>
<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>
<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>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>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>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>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>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>
<target>Type your post here...</target>
<note>Text box prompt to ask user to type their post.</note>
</trans-unit>
<trans-unit id="Unfollow" xml:space="preserve">
<source>Unfollow</source>
<target>Unfollow</target>
<note>Button to unfollow a user.</note>
</trans-unit>
<trans-unit id="Unfollowing" xml:space="preserve">
<source>Unfollowing</source>
<target>Unfollowing</target>
<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>
<target>Unfollowing...</target>
<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>
<target>Unfollows</target>
<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 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>
<target>Wallet Of Satoshi</target>
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
</trans-unit>
<trans-unit id="Wallet Selector" xml:space="preserve">
<source>Wallet Selector</source>
<target>Wallet Selector</target>
<note>Section title for selection of wallet.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve">
<source>Website</source>
<target>Website</target>
<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>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>Text to welcome user.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source>
<target>Your Name</target>
<note>Label for Your Name section of user profile form.</note>
</trans-unit>
<trans-unit id="Zebedee" xml:space="preserve">
<source>Zebedee</source>
<target>Zebedee</target>
<note>Dropdown option label for Lightning wallet, Zebedee.</note>
</trans-unit>
<trans-unit id="Zeus LN" xml:space="preserve">
<source>Zeus LN</source>
<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>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>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>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 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>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>
<target>replying_to_one_and_others</target>
<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>
<target>replying_to_two_and_others</target>
<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>
<target>satoshi</target>
<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>
<target>wss://some.relay.com</target>
<note>Placeholder example for relay server address.</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<target>you</target>
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
</trans-unit>
<trans-unit id="🤙" xml:space="preserve">
<source>🤙</source>
<target>🤙</target>
<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="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>
<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> &amp; 1 other</source>
<target> &amp; 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> &amp; %d others</source>
<target> &amp; %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> &amp; 1 other</source>
<target> &amp; 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> &amp; %d others</source>
<target> &amp; %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>
@@ -1,6 +0,0 @@
/* 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.";
@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>replying_to_one_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Replying to %@%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string></string>
<key>one</key>
<string> &amp; 1 other</string>
<key>other</key>
<string> &amp; %d others</string>
</dict>
</dict>
<key>replying_to_two_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Replying to %@, %@%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string></string>
<key>one</key>
<string> &amp; 1 other</string>
<key>other</key>
<string> &amp; %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,12 +0,0 @@
{
"developmentRegion" : "en-US",
"project" : "damus.xcodeproj",
"targetLocale" : "en-US",
"toolInfo" : {
"toolBuildNumber" : "14C18",
"toolID" : "com.apple.dt.xcode",
"toolName" : "Xcode",
"toolVersion" : "14.2"
},
"version" : "1.0"
}
@@ -1,743 +0,0 @@
<?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="&amp;nbsp;" xml:space="preserve">
<source>&amp;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="&lt; e &gt;" xml:space="preserve">
<source>&lt; e &gt;</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 &amp; 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 &amp; Replies" xml:space="preserve">
<source>Posts &amp; 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 %@ &amp; %@" xml:space="preserve">
<source>Replying to %1$@ &amp; %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> &amp; 1 other</source>
<target> &amp; 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> &amp; %d others</source>
<target> &amp; %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> &amp; 1 other</source>
<target> &amp; 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> &amp; %d others</source>
<target> &amp; %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>
@@ -1,6 +0,0 @@
/* 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.";
@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>replying_to_one_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Replying to %@%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string></string>
<key>one</key>
<string> &amp; 1 other</string>
<key>other</key>
<string> &amp; %d others</string>
</dict>
</dict>
<key>replying_to_two_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Replying to %@, %@%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string></string>
<key>one</key>
<string> &amp; 1 other</string>
<key>other</key>
<string> &amp; %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,12 +0,0 @@
{
"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"
}
+48 -83
View File
@@ -11,7 +11,6 @@
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
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 */; };
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 */; };
@@ -51,7 +50,6 @@
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 */; };
@@ -115,7 +113,6 @@
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,12 +135,9 @@
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 */; };
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.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 */
@@ -170,8 +164,6 @@
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>"; };
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>"; };
@@ -213,7 +205,6 @@
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>"; };
@@ -307,7 +298,6 @@
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,11 +323,8 @@
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>"; };
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
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 */
@@ -472,50 +459,49 @@
4C75EFA227FA576C0006080F /* Views */ = {
isa = PBXGroup;
children = (
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
4C363A8728236948006E126D /* BlocksView.swift */,
4C285C8128385570008A31F1 /* CarouselView.swift */,
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 */,
4C0A3F8B280F5FCA000448DE /* ChatroomView.swift */,
4C0A3F90280F6528000448DE /* ChatView.swift */,
4CE4F9DD2852768D00C00DD9 /* ConfigView.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 */,
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */,
4C216F31286E388800040376 /* DMChatView.swift */,
4C216F33286F5ACD00040376 /* DMView.swift */,
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
3169CAE4294E699400EE4006 /* Empty Views */,
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
4C75EFB82804A2740006080F /* EventView.swift */,
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
4C90BD17283A9EE5008EE7EF /* LoginView.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 */,
4C3AC7A42836987600E1F516 /* MainTabView.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 */,
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
647D9A8C2968520300A295DE /* SideMenuView.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -554,9 +540,6 @@
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
3165648A295B70D500C64604 /* LinkView.swift */,
4C3A1D3629637E0500558C0F /* PreviewCache.swift */,
64FBD06E296255C400D9D3B2 /* Theme.swift */,
4CB8838529656C8B00DC99E7 /* NIP05.swift */,
);
path = Util;
sourceTree = "<group>";
@@ -610,7 +593,6 @@
4CE6DEE827F7A08100C66700 /* ContentView.swift */,
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
4CE6DEEC27F7A08200C66700 /* Preview Content */,
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
);
path = damus;
sourceTree = "<group>";
@@ -626,7 +608,6 @@
4CE6DEF627F7A08200C66700 /* damusTests */ = {
isa = PBXGroup;
children = (
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */,
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */,
4C363A9F2828A8DD006E126D /* LikeTests.swift */,
4C363A9D2828A822006E126D /* ReplyTests.swift */,
@@ -742,18 +723,18 @@
};
buildConfigurationList = 4CE6DEDE27F7A08100C66700 /* Build configuration list for PBXProject "damus" */;
compatibilityVersion = "Xcode 13.0";
developmentRegion = "en-US";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
"es-419",
"en-US",
);
mainGroup = 4CE6DEDA27F7A08100C66700;
packageReferences = (
4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */,
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */,
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
);
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
@@ -774,7 +755,6 @@
files = (
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */,
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */,
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -826,7 +806,6 @@
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,
4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */,
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */,
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */,
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */,
@@ -857,10 +836,8 @@
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 */,
@@ -882,7 +859,6 @@
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 */,
@@ -930,7 +906,6 @@
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 */,
@@ -963,24 +938,11 @@
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */ = {
isa = PBXVariantGroup;
children = (
3A5C4575296A879E0032D398 /* es-419 */,
3A2B8B0A296A8982009CC16D /* en-US */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
4CE6DF0527F7A08200C66700 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
@@ -1027,14 +989,13 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
@@ -1043,7 +1004,6 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
@@ -1084,13 +1044,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
@@ -1104,14 +1063,13 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -1144,14 +1102,13 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -1293,6 +1250,14 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/joshuajhomann/Shimmer";
requirement = {
branch = master;
kind = branch;
};
};
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/onevcat/Kingfisher";
@@ -17,6 +17,15 @@
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
}
},
{
"identity" : "shimmer",
"kind" : "remoteSourceControl",
"location" : "https://github.com/joshuajhomann/Shimmer",
"state" : {
"branch" : "master",
"revision" : "2fde687b3f1d9c5409c53da095d3686361e41343"
}
},
{
"identity" : "starscream",
"kind" : "remoteSourceControl",
@@ -1,33 +1,6 @@
{
"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"
}
],
@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,38 +0,0 @@
{
"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
}
}
@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "ic-copy.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "ic-key.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 B

@@ -1,52 +0,0 @@
{
"images" : [
{
"filename" : "ic-message-black.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "ic-message-white 1.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "ic-nipverified.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 950 B

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "ic-qr.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "profile-banner.jpeg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "blixt-wallet.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

-23
View File
@@ -1,23 +0,0 @@
{
"images" : [
{
"filename" : "damus-home@1x.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "damus-home@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "damus-home@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "ic-lightning.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

-21
View File
@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "ic-tick.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 671 B

-21
View File
@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "river.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

-23
View File
@@ -1,23 +0,0 @@
{
"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
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

+4 -9
View File
@@ -41,24 +41,19 @@ struct ImageContextMenuModifier: ViewModifier {
Button {
UIPasteboard.general.url = url
} label: {
Label(NSLocalizedString("Copy Image URL", comment: "Context menu option to copy the URL of an image into clipboard."), systemImage: "doc.on.doc")
Label("Copy Image URL", systemImage: "doc.on.doc")
}
if let someImage = image {
Button {
UIPasteboard.general.image = someImage
} label: {
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")
Label("Copy Image", systemImage: "photo.on.rectangle")
}
}
Button {
showShareSheet = true
} label: {
Label(NSLocalizedString("Share", comment: "Button to share an image."), systemImage: "square.and.arrow.up")
Label("Share", systemImage: "square.and.arrow.up")
}
}
}
@@ -143,7 +138,7 @@ struct ImageCarousel: View {
}
.id(url.absoluteString)
.contextMenu {
Button(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image to clipboard.")) {
Button("Copy Image") {
UIPasteboard.general.string = url.absoluteString
}
}
+4 -15
View File
@@ -11,20 +11,9 @@ func open_with_wallet(wallet: Wallet.Model, invoice: String) {
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {
guard let store_link = wallet.appStoreLink else {
// TODO: do something here if we don't have an appstore link
return
if let url = URL(string: wallet.appStoreLink), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
guard let url = URL(string: store_link) else {
return
}
guard UIApplication.shared.canOpenURL(url) else {
return
}
UIApplication.shared.open(url)
}
}
@@ -47,7 +36,7 @@ struct InvoiceView: View {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(colorScheme == .light ? .black : .white)
.overlay {
Text("Pay", comment: "Button to pay a Lightning invoice.")
Text("Pay")
.fontWeight(.medium)
.foregroundColor(colorScheme == .light ? .white : .black)
}
@@ -67,7 +56,7 @@ struct InvoiceView: View {
HStack {
Label("", systemImage: "bolt.fill")
.foregroundColor(.orange)
Text("Lightning Invoice", comment: "Indicates that the view is for paying a Lightning invoice.")
Text("Lightning Invoice")
}
Divider()
Text(invoice.description)
+2 -2
View File
@@ -20,7 +20,7 @@ struct TextFieldAlert<Presenting>: View where Presenting: View {
.disabled(isShowing)
VStack {
Text(self.title)
TextField(NSLocalizedString("Relay", comment: "Text field for relay server. Used for testing purposes."), text: self.$text)
TextField("Relay", text: self.$text)
Divider()
HStack {
Button(action: {
@@ -28,7 +28,7 @@ struct TextFieldAlert<Presenting>: View where Presenting: View {
self.isShowing.toggle()
}
}) {
Text("Dismiss", comment: "Button to dismiss a text field alert.")
Text("Dismiss")
}
}
}
+69 -101
View File
@@ -16,7 +16,7 @@ var BOOTSTRAP_RELAYS = [
"wss://relay.nostr.bg",
"wss://nostr.oxtr.dev",
"wss://nostr.v0l.io",
"wss://brb.io",
"wss://nostr-2.zebedee.cloud",
]
struct TimestampedProfile {
@@ -44,15 +44,6 @@ 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 {
@@ -80,7 +71,6 @@ struct ContentView: View {
@State var thread_open: Bool = false
@State var search_open: Bool = false
@State var filter_state : FilterState = .posts_and_replies
@State private var isSideBarOpened = false
@StateObject var home: HomeModel = HomeModel()
@StateObject var user_settings = UserSettingsStore()
@@ -92,16 +82,17 @@ struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
var PostingTimelineView: some View {
VStack {
TabView(selection: $filter_state) {
contentTimelineView(filter: FilterState.posts.filter)
.tag(FilterState.posts)
.id(FilterState.posts)
contentTimelineView(filter: FilterState.posts_and_replies.filter)
.tag(FilterState.posts_and_replies)
.id(FilterState.posts_and_replies)
}
.tabViewStyle(.page(indexDisplayMode: .never))
VStack{
ZStack {
if let damus = self.damus_state {
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
}
if privkey != nil {
PostButtonContainer {
self.active_sheet = .post
}
}
}.ignoresSafeArea(.keyboard, edges: .bottom)
}
.safeAreaInset(edge: .top) {
VStack(spacing: 0) {
@@ -113,32 +104,26 @@ struct ContentView: View {
}
.background(colorScheme == .dark ? Color.black : Color.white)
}
.ignoresSafeArea(.keyboard)
}
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)
}
if privkey != nil {
PostButtonContainer(userSettings: user_settings) {
self.active_sheet = .post
}
}
}
}
var FiltersView: some View {
VStack{
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)
Picker("Filter State", selection: $filter_state) {
Text("Posts").tag(FilterState.posts)
Text("Posts & Replies").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) {
@@ -159,7 +144,7 @@ struct ContentView: View {
case .notifications:
TimelineView(events: $home.notifications, loading: $home.loading, damus: damus, show_friend_icon: true, filter: { _ in true })
.navigationTitle(NSLocalizedString("Notifications", comment: "Navigation title for notifications."))
.navigationTitle("Notifications")
case .dms:
DirectMessagesView(damus_state: damus_state!)
@@ -169,20 +154,7 @@ struct ContentView: View {
EmptyView()
}
}
.navigationBarTitle(selected_timeline == .home ? NSLocalizedString("Home", comment: "Navigation bar title for Home view where posts and replies appear from those who the user is following.") : NSLocalizedString("Global", comment: "Navigation bar title for Global view where posts from all connected relay servers appear."), displayMode: .inline)
.toolbar {
ToolbarItem(placement: .principal) {
if selected_timeline == .home {
Image("damus-home")
.resizable()
.frame(width:30,height:30)
.shadow(color: Color("DamusPurple"), radius: 2)
} else {
Text("Global")
}
}
}
.navigationBarTitle(selected_timeline == .home ? "Home" : "Global", displayMode: .inline)
}
var MaybeSearchView: some View {
@@ -221,51 +193,46 @@ struct ContentView: View {
VStack(alignment: .leading, spacing: 0) {
if let damus = self.damus_state {
NavigationView {
ZStack {
VStack {
MainContent(damus: damus)
.toolbar() {
ToolbarItem(placement: .navigationBarLeading) {
Button {
isSideBarOpened.toggle()
} label: {
let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!)
let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey)
if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture {
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture)
} else {
Image(systemName: "person.fill")
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
HStack(alignment: .center) {
if home.signal.signal != home.signal.max_signal {
Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
.font(.callout)
.foregroundColor(.gray)
}
MainContent(damus: damus)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!)
let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey)
let prof_dest = ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers_model)
}
NavigationLink(destination: prof_dest) {
/// Verify that the user has a profile picture, if not display a generic SF Symbol
/// (Resolves an in-app error where ``Robohash`` pictures are not generated so the button dissapears
if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture {
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture)
} else {
Image(systemName: "person.fill")
}
}
.buttonStyle(PlainButtonStyle())
}
ToolbarItem(placement: .navigationBarTrailing) {
HStack(alignment: .center) {
if home.signal.signal != home.signal.max_signal {
Text("\(home.signal.signal)/\(home.signal.max_signal)")
.font(.callout)
.foregroundColor(.gray)
}
NavigationLink(destination: ConfigView(state: damus_state!).environmentObject(user_settings)) {
Label("", systemImage: "gear")
}
.buttonStyle(PlainButtonStyle())
}
}
}
Color.clear
.overlay(
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened)
)
}
.navigationBarHidden(isSideBarOpened ? true: false) // Would prefer a different way of doing this.
}
.navigationViewStyle(.stack)
TabBar(new_events: $home.new_events, selected: $selected_timeline, isSidebarVisible: $isSideBarOpened, action: switch_timeline)
.padding([.bottom], 8)
}
TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
.padding([.bottom], 8)
}
.onAppear() {
self.connect()
@@ -306,6 +273,7 @@ struct ContentView: View {
guard let privkey = self.privkey else {
return
}
let ev = notif.object as! NostrEvent
let boost = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev)
self.damus_state?.pool.send(.event(boost))
@@ -338,10 +306,10 @@ struct ContentView: View {
let pk = target.pubkey
if let ev = unfollow_user(pool: damus.pool,
our_contacts: damus.contacts.event,
pubkey: damus.pubkey,
privkey: privkey,
unfollow: pk) {
our_contacts: damus.contacts.event,
pubkey: damus.pubkey,
privkey: privkey,
unfollow: pk) {
notify(.unfollowed, pk)
damus.contacts.event = ev
@@ -360,10 +328,10 @@ struct ContentView: View {
}
if let ev = follow_user(pool: damus.pool,
our_contacts: damus.contacts.event,
pubkey: damus.pubkey,
privkey: privkey,
follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) {
our_contacts: damus.contacts.event,
pubkey: damus.pubkey,
privkey: privkey,
follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) {
notify(.followed, fnotify.pubkey)
damus_state?.contacts.event = ev
@@ -433,11 +401,10 @@ struct ContentView: View {
self.damus_state = DamusState(pool: pool, keypair: keypair,
likes: EventCounter(our_pubkey: pubkey),
boosts: EventCounter(our_pubkey: pubkey),
contacts: Contacts(our_pubkey: pubkey),
contacts: Contacts(),
tips: TipCounter(our_pubkey: pubkey),
profiles: Profiles(),
dms: home.dms,
previews: PreviewCache()
dms: home.dms
)
home.damus_state = self.damus_state!
@@ -453,6 +420,7 @@ struct ContentView_Previews: PreviewProvider {
}
}
func get_since_time(last_event: NostrEvent?) -> Int64? {
if let last_event = last_event {
return last_event.created_at - 60 * 10
-4
View File
@@ -15,11 +15,8 @@
</array>
</dict>
</array>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>&quot;Granting Damus access to your photo library allows you to save photos.</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>river</string>
<string>bitcoinbeach</string>
<string>breez</string>
<string>muun</string>
@@ -32,7 +29,6 @@
<string>strike</string>
<string>bluewallet</string>
<string>walletofsatoshi</string>
<string>blixtwallet</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
-9
View File
@@ -11,13 +11,8 @@ 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())
@@ -61,10 +56,6 @@ 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
}
+1 -3
View File
@@ -6,7 +6,6 @@
//
import Foundation
import LinkPresentation
struct DamusState {
let pool: RelayPool
@@ -17,13 +16,12 @@ 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(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(), previews: PreviewCache())
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())
}
}
-16
View File
@@ -544,9 +544,7 @@ 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
@@ -556,20 +554,6 @@ 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) {
+4 -14
View File
@@ -187,22 +187,12 @@ enum Amount: Equatable {
func amount_sats_str() -> String {
switch self {
case .any:
return NSLocalizedString("Any", comment: "Any amount of sats")
return "Any"
case .specific(let amt):
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.minimumFractionDigits = 0
numberFormatter.maximumFractionDigits = 3
numberFormatter.roundingMode = .down
let sats = NSNumber(value: (Double(amt) / 1000.0))
let formattedSats = numberFormatter.string(from: sats) ?? sats.stringValue
if formattedSats == numberFormatter.string(from: 1) {
return NSLocalizedString("\(formattedSats) sat", comment: "Amount of 1 sat.")
if amt < 1000 {
return "\(Double(amt) / 1000.0) sats"
}
return NSLocalizedString("\(formattedSats) sats", comment: "Amount of sats.")
return "\(amt / 1000) sats"
}
}
}
+4 -12
View File
@@ -61,7 +61,7 @@ class ProfileModel: ObservableObject, Equatable {
profile_filter.authors = [pubkey]
text_filter.authors = [pubkey]
text_filter.limit = 500
text_filter.limit = 1000
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
@@ -70,18 +70,12 @@ class ProfileModel: ObservableObject, Equatable {
}
func handle_profile_contact_event(_ ev: NostrEvent) {
process_contact_event(pool: damus.pool, contacts: damus.contacts, pubkey: damus.pubkey, ev: ev)
// only use new stuff
if let current_ev = self.contacts {
guard ev.created_at > current_ev.created_at else {
return
}
}
self.contacts = ev
self.following = count_pubkeys(ev.tags)
self.relays = decode_json_relays(ev.content)
if damus.contacts.is_friend(ev.pubkey) {
self.damus.contacts.add_friend_contact(ev)
}
}
func add_event(_ ev: NostrEvent) {
@@ -96,8 +90,6 @@ class ProfileModel: ObservableObject, Equatable {
let _ = insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at})
} else if ev.known_kind == .contacts {
handle_profile_contact_event(ev)
} else if ev.known_kind == .metadata {
process_metadata_event(profiles: damus.profiles, ev: ev)
}
seen_event.insert(ev.id)
}
+47 -50
View File
@@ -41,40 +41,40 @@ class SearchHomeModel: ObservableObject {
}
func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) {
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) {
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 {
return
}
seen_pubkey.insert(ev.pubkey)
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
$0.created_at > $1.created_at
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
}
}
}
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)
case .notice(let msg):
print("search home notice: \(msg)")
case .eose(let sub_id):
loading = false
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
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
}
break
}
}
}
@@ -112,30 +112,27 @@ 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
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 {
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 {
return
}
if ev.known_kind == .metadata {
process_metadata_event(profiles: damus_state.profiles, ev: ev)
}
print("done loading \(authors.count) profiles from \(relay_id)")
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
}
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])
}
}
+1 -1
View File
@@ -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):
+3 -12
View File
@@ -20,22 +20,13 @@ class UserSettingsStore: ObservableObject {
}
}
@Published var left_handed: Bool {
didSet {
UserDefaults.standard.set(left_handed, forKey: "left_handed")
}
}
init() {
if let defaultWalletName = UserDefaults.standard.string(forKey: "default_wallet"),
let default_wallet = Wallet(rawValue: defaultWalletName)
{
let default_wallet = Wallet(rawValue: defaultWalletName) {
self.default_wallet = default_wallet
} else {
default_wallet = .system_default_wallet
self.default_wallet = .system_default_wallet
}
show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false
self.show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
}
}
+13 -22
View File
@@ -16,7 +16,7 @@ enum Wallet: String, CaseIterable, Identifiable {
var tag: String
var displayName : String
var link : String
var appStoreLink : String?
var appStoreLink : String
var image: String
}
@@ -33,53 +33,44 @@ enum Wallet: String, CaseIterable, Identifiable {
case phoenix
case breez
case bitcoinbeach
case blixtwallet
case river
var model: Model {
switch self {
case .system_default_wallet:
return .init(index: -1, tag: "systemdefaultwallet", displayName: NSLocalizedString("Local default", comment: "Dropdown option label for system default for Lightning wallet."),
return .init(index: -1, tag: "systemdefaultwallet", displayName: "Local default",
link: "lightning:", appStoreLink: "lightning:", image: "")
case .strike:
return .init(index: 0, tag: "strike", displayName: NSLocalizedString("Strike", comment: "Dropdown option label for Lightning wallet, Strike."), link: "strike:",
return .init(index: 0, tag: "strike", displayName: "Strike", link: "strike:",
appStoreLink: "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", image: "strike")
case .cashapp:
return .init(index: 1, tag: "cashapp", displayName: NSLocalizedString("Cash App", comment: "Dropdown option label for Lightning wallet, Cash App."), link: "squarecash://",
return .init(index: 1, tag: "cashapp", displayName: "Cash App", link: "squarecash://",
appStoreLink: "https://apps.apple.com/us/app/cash-app/id711923939", image: "cashapp")
case .muun:
return .init(index: 2, tag: "muun", displayName: NSLocalizedString("Muun", comment: "Dropdown option label for Lightning wallet, Muun."), link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun")
return .init(index: 2, tag: "muun", displayName: "Muun", link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun")
case .bluewallet:
return .init(index: 3, tag: "bluewallet", displayName: NSLocalizedString("Blue Wallet", comment: "Dropdown option label for Lightning wallet, Blue Wallet."), link: "bluewallet:lightning:",
return .init(index: 3, tag: "bluewallet", displayName: "Blue Wallet", link: "bluewallet:lightning:",
appStoreLink: "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", image: "bluewallet")
case .walletofsatoshi:
return .init(index: 4, tag: "walletofsatoshi", displayName: NSLocalizedString("Wallet Of Satoshi", comment: "Dropdown option label for Lightning wallet, Wallet Of Satoshi."), link: "walletofsatoshi:lightning:",
return .init(index: 4, tag: "walletofsatoshi", displayName: "Wallet Of Satoshi", link: "walletofsatoshi:lightning:",
appStoreLink: "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", image: "walletofsatoshi")
case .zebedee:
return .init(index: 5, tag: "zebedee", displayName: NSLocalizedString("Zebedee", comment: "Dropdown option label for Lightning wallet, Zebedee."), link: "zebedee:lightning:",
return .init(index: 5, tag: "zebedee", displayName: "Zebedee", link: "zebedee:lightning:",
appStoreLink: "https://apps.apple.com/us/app/zebedee-wallet/id1484394401", image: "zebedee")
case .zeusln:
return .init(index: 6, tag: "zeusln", displayName: NSLocalizedString("Zeus LN", comment: "Dropdown option label for Lightning wallet, Zeus LN."), link: "zeusln:lightning:",
return .init(index: 6, tag: "zeusln", displayName: "Zeus LN", link: "zeusln:lightning:",
appStoreLink: "https://apps.apple.com/us/app/zeus-ln/id1456038895", image: "zeusln")
case .lnlink:
return .init(index: 7, tag: "lnlink", displayName: NSLocalizedString("LNLink", comment: "Dropdown option label for Lightning wallet, LNLink."), link: "lnlink:lightning:",
return .init(index: 7, tag: "lnlink", displayName: "LNLink", link: "lnlink:lightning:",
appStoreLink: "https://testflight.apple.com/join/aNY4yuuZ", image: "lnlink")
case .phoenix:
return .init(index: 8, tag: "phoenix", displayName: NSLocalizedString("Phoenix", comment: "Dropdown option label for Lightning wallet, Phoenix."), link: "phoenix://",
return .init(index: 8, tag: "phoenix", displayName: "Phoenix", link: "phoenix://",
appStoreLink: "https://apps.apple.com/us/app/phoenix-wallet/id1544097028", image: "phoenix")
case .breez:
return .init(index: 9, tag: "breez", displayName: NSLocalizedString("Breez", comment: "Dropdown option label for Lightning wallet, Breez."), link: "breez:",
return .init(index: 9, tag: "breez", displayName: "Breez", link: "breez:",
appStoreLink: "https://apps.apple.com/us/app/breez-lightning-client-pos/id1463604142", image: "breez")
case .bitcoinbeach:
return .init(index: 10, tag: "bitcoinbeach", displayName: NSLocalizedString("Bitcoin Beach", comment: "Dropdown option label for Lightning wallet, Bitcoin Beach."), link: "bitcoinbeach://",
return .init(index: 10, tag: "bitcoinbeach", displayName: "Bitcoin Beach", link: "bitcoinbeach://",
appStoreLink: "https://apps.apple.com/sv/app/bitcoin-beach-wallet/id1531383905", image: "bbw")
case .blixtwallet:
return .init(index: 11, tag: "blixtwallet", displayName: NSLocalizedString("Blixt Wallet", comment: "Dropdown option label for Lightning wallet, Blixt Wallet"), link: "blixtwallet:lightning:",
appStoreLink: nil, image: "blixt-wallet")
case .river:
return .init(index: 12, tag: "river", displayName: NSLocalizedString("River", comment: "Dropdown option label for Lightning wallet, River"), link: "river://",
appStoreLink: "https://apps.apple.com/us/app/river-buy-mine-bitcoin/id1536176542", image: "river")
}
}
+4 -6
View File
@@ -446,13 +446,11 @@ func hex_encode(_ data: Data) -> String {
func random_bytes(count: Int) -> Data {
var bytes = [Int8](repeating: 0, count: count)
guard
SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) == errSecSuccess
else {
fatalError("can't copy secure random data")
var data = Data(count: count)
_ = data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
}
return Data(bytes: bytes, count: count)
return data
}
func refid_to_tag(_ ref: ReferencedId) -> [String] {
-25
View File
@@ -80,32 +80,7 @@ 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
View File
@@ -11,17 +11,6 @@ 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()
-5
View File
@@ -11,11 +11,6 @@ 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
+1 -1
View File
@@ -38,7 +38,7 @@ class RelayConnection: WebSocketDelegate {
self.connect(force: true)
}
}
func connect(force: Bool = false){
if !force && (self.isConnected || self.isConnecting) {
return
+2 -57
View File
@@ -28,20 +28,9 @@ 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 }
@@ -159,38 +148,13 @@ 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 {
guard relay.connection.isConnected else {
queue_req(r: req, relay: relay.id)
continue
if relay.connection.isConnected {
relay.connection.send(req)
}
relay.connection.send(req)
}
}
@@ -229,28 +193,9 @@ 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)
+1 -1
View File
@@ -11,7 +11,7 @@ public class Constants {
static let PUB_KEY = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
static let EXAMPLE_DEMOS: DamusState = .empty
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_EVENTS = [
NostrEvent(id: UUID().description, content: "Nostr - Damus... Haha get it? Bonjour Le Monde mon Ami! C'est la tres importante", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
+13 -11
View File
@@ -12,24 +12,26 @@ 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
let meta: Metadata
var metadata: LPLinkMetadata?
var url: URL?
func makeUIView(context: Context) -> CustomLinkView {
switch meta {
case .linkmeta(let linkmeta):
return CustomLinkView(metadata: linkmeta)
case .url(let url):
return CustomLinkView(url: url)
if let metadata {
let linkView = CustomLinkView(metadata: metadata)
return linkView
}
if let url {
let linkView = CustomLinkView(url: url)
return linkView
}
return CustomLinkView()
}
func updateUIView(_ uiView: CustomLinkView, context: Context) {
+6 -10
View File
@@ -15,14 +15,12 @@ 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)
// TODO: escape unintentional markdown
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
if let txt = try? AttributedString(markdown: escaped, options: md_opts) {
if let txt = try? AttributedString(markdown: content, options: md_opts) {
return txt
} else {
return AttributedString(stringLiteral: content)
@@ -35,13 +33,11 @@ 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)
, 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))")
guard let range = Range(match.range, in: input) else { continue }
let url = input[range]
output.replaceSubrange(range, with: "[\(url)](\(Markdown.withScheme(url)))")
}
// TODO: escape unintentional markdown
return Markdown.parse(content: output)
}
}
-69
View File
@@ -1,69 +0,0 @@
//
// 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)")
}
var siteUrl: URL? {
URL(string: "https://\(host)")
}
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
View File
@@ -1,35 +0,0 @@
//
// 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
View File
@@ -1,28 +0,0 @@
//
// 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
}
}
+4 -4
View File
@@ -16,10 +16,10 @@ struct AddRelayView: View {
var body: some View {
VStack(alignment: .leading) {
Form {
Section(NSLocalizedString("Add Relay", comment: "Label for section for adding a relay server.")) {
Section("Add Relay") {
ZStack(alignment: .leading) {
HStack{
TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $relay)
TextField("wss://some.relay.com", text: $relay)
.padding(2)
.padding(.leading, 25)
.autocorrectionDisabled(true)
@@ -47,7 +47,7 @@ struct AddRelayView: View {
VStack {
HStack {
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted relay.")) {
Button("Cancel") {
show_add_relay = false
action(nil)
}
@@ -55,7 +55,7 @@ struct AddRelayView: View {
Spacer()
Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted relay.")) {
Button("Add") {
show_add_relay = false
action(relay)
relay = ""
+4 -4
View File
@@ -15,13 +15,13 @@ struct CarouselItem: Identifiable {
}
let carousel_items = [
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("digital-nomad"), text: Text("Welcome to the social network \(Text("you").italic()) control.")),
CarouselItem(image: Image("encrypted-message"),
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.")),
text: Text("\(Text("Encrypted").bold()). End-to-End encrypted private messaging. Keep Big Tech out of your DMs")),
CarouselItem(image: Image("undercover"),
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.")),
text: Text("\(Text("Private").bold()). Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.")),
CarouselItem(image: Image("bitcoin-p2p"),
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."))
text: Text("\(Text("Earn Money").bold()). Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet."))
]
struct CarouselView: View {
+3 -3
View File
@@ -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), damus: damus_state, show_friend_confirmed: true)
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), contacts: damus_state.contacts, 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, previews: damus_state.previews)
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles)
.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, previews: damus_state.previews, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
NoteContentView(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)
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
let bar = make_actionbar_model(ev: event, damus: damus_state)
+1 -1
View File
@@ -24,7 +24,7 @@ struct ChatroomView: View {
next_ev: ind == count-1 ? nil : thread.events[ind+1],
damus_state: damus
)
.event_context_menu(ev, pubkey: ev.pubkey, privkey: damus.keypair.privkey)
.event_context_menu(ev, privkey: damus.keypair.privkey)
.onTapGesture {
if thread.initial_event.id == ev.id {
//dismiss()
+39 -54
View File
@@ -6,7 +6,6 @@
//
import AVFoundation
import SwiftUI
import Kingfisher
struct ConfigView: View {
let state: DamusState
@@ -55,30 +54,19 @@ struct ConfigView: View {
var body: some View {
ZStack(alignment: .leading) {
Form {
Section {
Section("Relays") {
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") {
List(recommended, id: \.url) { r in
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
}
}
if recommended.count > 0 {
Section(NSLocalizedString("Recommended Relays", comment: "Section title for recommend relay servers that could be added as part of configuration")) {
List(recommended, id: \.url) { r in
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
}
}
}
Section(NSLocalizedString("Public Account ID", comment: "Section title for the user's public account ID.")) {
Section("Public Account ID") {
HStack {
Text(state.keypair.pubkey_bech32)
@@ -88,10 +76,10 @@ struct ConfigView: View {
}
if let sec = state.keypair.privkey_bech32 {
Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) {
Section("Secret Account Login Key") {
HStack {
if show_privkey == false {
SecureField(NSLocalizedString("PrivateKey", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
SecureField("PrivateKey", text: $privkey)
.disabled(true)
} else {
Text(sec)
@@ -101,13 +89,13 @@ struct ConfigView: View {
CopyButton(is_pk: false)
}
Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey)
Toggle("Show", isOn: $show_privkey)
}
}
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"),
Section("Wallet Selector") {
Toggle("Show wallet selector", isOn: $user_settings.show_wallet_selector).toggleStyle(.switch)
Picker("Select default wallet",
selection: $user_settings.default_wallet) {
ForEach(Wallet.allCases, id: \.self) { wallet in
Text(wallet.model.displayName)
@@ -115,53 +103,50 @@ struct ConfigView: View {
}
}
}
Section(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen")) {
Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $user_settings.left_handed)
.toggleStyle(.switch)
}
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.")) {
Section("Reset") {
Button("Logout") {
confirm_logout = true
}
}
}
VStack {
HStack {
Spacer()
Button(action: { show_add_relay = true }) {
Label("", systemImage: "plus")
.foregroundColor(.accentColor)
.padding()
}
}
Spacer()
}
}
.navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view."))
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.large)
.alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) {
Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user.")) {
.alert("Logout", isPresented: $confirm_logout) {
Button("Cancel") {
confirm_logout = false
}
Button(NSLocalizedString("Logout", comment: "Button for logging out the user.")) {
Button("Logout") {
notify(.logout, ())
}
} message: {
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.")
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account")
}
.sheet(isPresented: $show_add_relay) {
AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in
guard var relay = m_relay else {
guard let 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
}
@@ -176,9 +161,9 @@ struct ConfigView: View {
return
}
state.pool.connect(to: [relay])
state.pool.connect(to: [new_relay])
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: info) else {
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: new_relay, info: info) else {
return
}
+11 -11
View File
@@ -35,31 +35,31 @@ struct CreateAccountView: View {
HStack(alignment: .top) {
VStack {
Text(" ", comment: "Blank space to separate profile picture from profile editor form.")
Text(" ")
.foregroundColor(.white)
}
VStack {
SignupForm {
FormLabel(NSLocalizedString("Username", comment: "Label to prompt username entry."))
FormLabel("Username")
HStack(spacing: 0.0) {
Text("@", comment: "Prefix character to username.")
Text("@")
.foregroundColor(.white)
.padding(.leading, -25.0)
FormTextInput(NSLocalizedString("satoshi", comment: "Example username of Bitcoin creator(s), Satoshi Nakamoto."), text: $account.nick_name)
FormTextInput("satoshi", text: $account.nick_name)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
FormLabel(NSLocalizedString("Display Name", comment: "Label to prompt display name entry."), optional: true)
FormTextInput(NSLocalizedString("Satoshi Nakamoto", comment: "Name of Bitcoin creator(s)."), text: $account.real_name)
FormLabel("Display Name", optional: true)
FormTextInput("Satoshi Nakamoto", text: $account.real_name)
.textInputAutocapitalization(.words)
FormLabel(NSLocalizedString("About", comment: "Label to prompt for about text entry for user to describe about themself."), optional: true)
FormTextInput(NSLocalizedString("Creator(s) of Bitcoin. Absolute legend.", comment: "Example description about Bitcoin creator(s), Satoshi Nakamoto."), text: $account.about)
FormLabel("About", optional: true)
FormTextInput("Creator(s) of Bitcoin. Absolute legend.", text: $account.about)
FormLabel(NSLocalizedString("Account ID", comment: "Label to indicate the public ID of the account."))
FormLabel("Account ID")
.onTapGesture {
regen_key()
}
@@ -75,7 +75,7 @@ struct CreateAccountView: View {
NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) {
EmptyView()
}
DamusWhiteButton(NSLocalizedString("Create", comment: "Button to create account.")) {
DamusWhiteButton("Create") {
self.is_done = true
}
.padding()
@@ -151,7 +151,7 @@ func FormLabel(_ title: String, optional: Bool = false) -> some View {
.bold()
.foregroundColor(.white)
if optional {
Text("optional", comment: "Label indicating that a form input is optional.")
Text("optional")
.font(.callout)
.foregroundColor(.white.opacity(0.5))
}
+4 -8
View File
@@ -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, pubkey: ev.pubkey, privkey: damus_state.keypair.privkey)
.event_context_menu(ev, privkey: damus_state.keypair.privkey)
}
EndBlock(height: 80)
}
@@ -27,10 +27,6 @@ struct DMChatView: View {
}
.onAppear {
scroller.scrollTo("endblock")
}.onChange(of: dms.events.count) { _ in
withAnimation {
scroller.scrollTo("endblock")
}
}
}
}
@@ -44,7 +40,7 @@ struct DMChatView: View {
HStack {
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles)
ProfileName(pubkey: pubkey, profile: profile, damus: damus_state, show_friend_confirmed: true)
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true)
}
}
.buttonStyle(PlainButtonStyle())
@@ -142,14 +138,14 @@ struct DMChatView: View {
Footer
}
Text("Send a message to start the conversation...", comment: "Text prompt for user to send a message to the other user.")
Text("Send a message to start the conversation...")
.lineLimit(nil)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
.opacity(((dms.events.count == 0) ? 1.0 : 0.0))
.foregroundColor(.gray)
}
.navigationTitle(NSLocalizedString("DM", comment: "Navigation title for DM view, which is the English abbreviation for Direct Message."))
.navigationTitle("DM")
.toolbar { Header }
}
}
+6 -9
View File
@@ -10,28 +10,25 @@ import SwiftUI
struct DMView: View {
let event: NostrEvent
let damus_state: DamusState
var is_ours: Bool {
event.pubkey == damus_state.pubkey
}
var body: some View {
HStack {
if is_ours {
Spacer(minLength: UIScreen.main.bounds.width * 0.2)
Spacer()
}
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, previews: damus_state.previews, 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, 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))
.cornerRadius(8.0)
.tint(is_ours ? Color.white : Color.accentColor)
if !is_ours {
Spacer(minLength: UIScreen.main.bounds.width * 0.2)
}
}
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ struct DirectMessagesView: View {
var body: some View {
MainContent
.navigationTitle(NSLocalizedString("Encrypted DMs", comment: "Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message."))
.navigationTitle("Encrypted DMs")
}
}
+28 -19
View File
@@ -15,6 +15,11 @@ 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"]
@@ -96,44 +101,48 @@ struct EditMetadataView: View {
}
var nip05_parts: NIP05? {
return NIP05.parse(nip05)
let parts = nip05.split(separator: "@")
guard parts.count == 2 else {
return nil
}
return NIP05(username: String(parts[0]), host: String(parts[1]))
}
var body: some View {
VStack(alignment: .leading) {
HStack {
Spacer()
InnerProfilePicView(url: URL(string: picture), fallbackUrl: nil, pubkey: damus_state.pubkey, size: PPM_SIZE, highlight: .none)
InnerProfilePicView(url: URL(string: picture), pubkey: damus_state.pubkey, size: PPM_SIZE, highlight: .none)
Spacer()
}
Form {
Section(NSLocalizedString("Your Name", comment: "Label for Your Name section of user profile form.")) {
Section("Your Name") {
TextField("Satoshi Nakamoto", text: $display_name)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
Section(NSLocalizedString("Username", comment: "Label for Username section of user profile form.")) {
Section("Username") {
TextField("satoshi", text: $name)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
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)
Section ("Profile Picture") {
TextField("https://example.com/pic.jpg", text: $picture)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
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)
Section("Website") {
TextField("https://jb55.com", text: $website)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
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.")
Section("About Me") {
let placeholder = "Absolute Boss"
ZStack(alignment: .topLeading) {
TextEditor(text: $about)
.textInputAutocapitalization(.sentences)
@@ -146,33 +155,33 @@ struct EditMetadataView: View {
}
}
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)
Section("Bitcoin Lightning Tips") {
TextField("Lightning Address or LNURL", text: $ln)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
Section(content: {
TextField(NSLocalizedString("jb55@jb55.com", comment: "Placeholder example text for identifier used for NIP-05 verification."), text: $nip05)
TextField("jb55@jb55.com", text: $nip05)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}, header: {
Text("NIP-05 Verification", comment: "Label for NIP-05 Verification section of user profile form.")
Text("NIP-05 Verification")
}, footer: {
if let parts = nip05_parts {
Text("'\(parts.username)' at '\(parts.host)' will be used for verification", comment: "Description of how the nip05 identifier would be used for verification.")
Text("'\(parts.username)' at '\(parts.host)' will be used for verification")
} else {
Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.", comment: "Description of why the nip05 identifier is invalid.")
Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.")
}
})
Button(NSLocalizedString("Save", comment: "Button for saving profile.")) {
Button("Save") {
save()
dismiss()
}
}
}
.navigationTitle(NSLocalizedString("Edit Profile", comment: "Title of navigation view for Edit Profile."))
.navigationTitle("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!", comment: "Indicates that there are no notes in the timeline to view.")
Text("Nothing to see here. Check back later!")
.multilineTextAlignment(.center)
.font(.callout.weight(.medium))
}
+8 -23
View File
@@ -24,7 +24,6 @@ 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 {
@@ -41,7 +40,6 @@ struct EventActionBar: View {
EventActionButton(img: "bubble.left", col: nil) {
notify(.reply, event)
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
HStack(alignment: .bottom) {
@@ -57,7 +55,6 @@ struct EventActionBar: View {
}
}
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
HStack(alignment: .bottom) {
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
@@ -72,12 +69,6 @@ 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) {
@@ -95,22 +86,15 @@ struct EventActionBar: View {
}
*/
}
.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) {
.alert("Boost", isPresented: $confirm_boost) {
Button("Cancel") {
confirm_boost = false
}
Button(NSLocalizedString("Boost", comment: "Button to confirm boosting a post.")) {
Button("Boost") {
send_boost()
}
} message: {
Text("Are you sure you want to boost this post?", comment: "Alert message to ask if user wants to boost a post.")
Text("Are you sure you want to boost this post?")
}
.onReceive(handle_notify(.liked)) { n in
let liked = n.object as! Counted
@@ -158,6 +142,7 @@ func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) ->
.font(.footnote.weight(.medium))
.foregroundColor(col == nil ? Color.gray : col!)
}
.padding(.trailing, 40)
}
struct LikeButton: View {
@@ -169,11 +154,11 @@ struct LikeButton: View {
var body: some View {
Button(action: action) {
if liked {
Text("🤙", comment: "Button with emoji to like an event.")
Text("🤙")
} else {
Image("shaka")
.renderingMode(.template)
.foregroundColor(.gray)
Label("&nbsp;", systemImage: "hand.thumbsup")
.font(.footnote.weight(.medium))
.foregroundColor(Color.gray)
}
}
}
+2 -2
View File
@@ -59,7 +59,7 @@ struct EventDetailView: View {
Group {
switch cev {
case .collapsed(let c):
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))
Text("··· \(c.count) other notes ···")
.padding([.top,.bottom], 8)
.font(.footnote)
.foregroundColor(.gray)
@@ -114,7 +114,7 @@ struct EventDetailView: View {
scroll_after_load(thread: thread, proxy: proxy)
}
}
.navigationBarTitle(NSLocalizedString("Thread", comment: "Navigation bar title for note thread."))
.navigationBarTitle("Thread")
}
+81 -43
View File
@@ -103,13 +103,12 @@ struct BuilderEventView: View {
var body: some View {
VStack {
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 {
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)
}.buttonStyle(.plain)
}
}
.frame(minWidth: 0, maxWidth: .infinity)
@@ -129,10 +128,11 @@ 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) {
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
self.event = event
self.highlight = highlight
self.has_action_bar = has_action_bar
@@ -140,9 +140,10 @@ 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) {
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
self.event = event
self.highlight = .none
self.has_action_bar = false
@@ -150,6 +151,7 @@ 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) {
@@ -160,6 +162,7 @@ struct EventView: View {
self.pubkey = pubkey
self.show_friend_icon = show_friend_icon
self.size = size
self.embedded = embedded
}
var body: some View {
@@ -176,10 +179,10 @@ struct EventView: View {
Image(systemName: "arrow.2.squarepath")
.font(.footnote.weight(.bold))
.foregroundColor(Color.gray)
ProfileName(pubkey: event.pubkey, profile: prof, damus: damus, show_friend_confirmed: true)
ProfileName(pubkey: event.pubkey, profile: prof, contacts: damus.contacts, show_friend_confirmed: true)
.font(.footnote.weight(.bold))
.foregroundColor(Color.gray)
Text("Boosted", comment: "Text indicating that the post was boosted (i.e. re-shared).")
Text("Boosted")
.font(.footnote.weight(.bold))
.foregroundColor(Color.gray)
}
@@ -206,8 +209,10 @@ 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))
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
if !embedded {
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
}
}
Spacer()
@@ -227,7 +232,7 @@ struct EventView: View {
}
}
EventProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: show_friend_icon, size: size)
EventProfileName(pubkey: pubkey, profile: profile, contacts: damus.contacts, show_friend_confirmed: show_friend_icon, size: size)
if size != .selected {
Text("\(format_relative_time(event.created_at))")
.font(eventviewsize_to_font(size))
@@ -244,28 +249,55 @@ 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, previews: damus.previews, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
.frame(maxWidth: .infinity, alignment: .leading)
.allowsHitTesting(!embedded)
if has_action_bar {
if size == .selected {
Text("\(format_date(event.created_at))")
.padding(.top, 10)
.font(.footnote)
.foregroundColor(.gray)
if !embedded {
let blocks = event.blocks(damus.keypair.privkey).filter { block in
guard case .mention(let mention) = block else {
return false
}
Divider()
.padding([.bottom], 4)
} else {
Rectangle().frame(height: 2).opacity(0)
guard case .event = mention.type else {
return false
}
if mention.ref.key != "e" {
return false
}
return true
}
let bar = make_actionbar_model(ev: event, damus: damus)
EventActionBar(damus_state: damus, event: event, bar: bar)
/// MARK: - Preview
if let firstBlock = blocks.first, case .mention(let mention) = firstBlock, mention.ref.key == "e" {
BuilderEventView(damus: damus, event_id: mention.ref.id)
}
}
Divider()
.padding([.top], 4)
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)
}
}
.padding([.leading], 2)
}
@@ -274,7 +306,7 @@ struct EventView: View {
.id(event.id)
.frame(maxWidth: .infinity, minHeight: PFP_SIZE)
.padding([.bottom], 2)
.event_context_menu(event, pubkey: pubkey, privkey: damus.keypair.privkey)
.event_context_menu(event, privkey: damus.keypair.privkey)
}
}
@@ -308,41 +340,41 @@ extension View {
Button {
UIPasteboard.general.string = bech32_pubkey
} label: {
Label(NSLocalizedString("Copy Account ID", comment: "Context menu option for copying the ID of the account that created the note."), systemImage: "doc.on.doc")
Label("Copy Account ID", systemImage: "doc.on.doc")
}
}
}
func event_context_menu(_ event: NostrEvent, pubkey: String, privkey: String?) -> some View {
func event_context_menu(_ event: NostrEvent, privkey: String?) -> some View {
return self.contextMenu {
Button {
UIPasteboard.general.string = event.get_content(privkey)
} label: {
Label(NSLocalizedString("Copy Text", comment: "Context menu option for copying the text from an note."), systemImage: "doc.on.doc")
Label("Copy Text", systemImage: "doc.on.doc")
}
Button {
UIPasteboard.general.string = bech32_pubkey(pubkey) ?? pubkey
UIPasteboard.general.string = bech32_pubkey(event.pubkey) ?? event.pubkey
} label: {
Label(NSLocalizedString("Copy User ID", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "tag")
Label("Copy User ID", systemImage: "tag")
}
Button {
UIPasteboard.general.string = bech32_note_id(event.id) ?? event.id
} label: {
Label(NSLocalizedString("Copy Note ID", comment: "Context menu option for copying the ID of the note."), systemImage: "tag")
Label("Copy Note ID", systemImage: "tag")
}
Button {
UIPasteboard.general.string = event_to_json(ev: event)
} label: {
Label(NSLocalizedString("Copy Note JSON", comment: "Context menu option for copying the JSON text from the note."), systemImage: "note")
Label("Copy Note JSON", systemImage: "note")
}
Button {
NotificationCenter.default.post(name: .broadcast_event, object: event)
} label: {
Label(NSLocalizedString("Broadcast", comment: "Context menu option for broadcasting the user's note to all of the user's connected relay servers."), systemImage: "globe")
Label("Broadcast", systemImage: "globe")
}
}
@@ -369,7 +401,7 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String {
let n = desc.others
if desc.pubkeys.count == 0 {
return NSLocalizedString("Reply to self", comment: "Label to indicate that the user is replying to themself.")
return "Reply to self"
}
let names: [String] = pubkeys.map {
@@ -379,14 +411,20 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String {
if names.count == 2 {
if n > 2 {
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)
let and_other = reply_others_desc(n: n, n_pubkeys: pubkeys.count)
return "Replying to \(names[0]), \(names[1])\(and_other)"
}
return String(format: NSLocalizedString("Replying to %@ & %@", comment: "Label to indicate that the user is replying to 2 users."), names[0], names[1])
return "Replying to \(names[0]) & \(names[1])"
}
let othersCount = n - pubkeys.count
return String(format: NSLocalizedString("replying_to_one_and_others", comment: "Label to indicate that the user is replying to 1 user and others."), names[0], othersCount)
let and_other = reply_others_desc(n: n, n_pubkeys: pubkeys.count)
return "Replying to \(names[0])\(and_other)"
}
func reply_others_desc(n: Int, n_pubkeys: Int) -> String {
let other = n - n_pubkeys
let plural = other == 1 ? "" : "s"
return n > 1 ? " & \(other) other\(plural)" : ""
}
+9 -14
View File
@@ -19,11 +19,10 @@ 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 ? filledTextColor() : borderColor())
.foregroundColor(follow_state == .unfollows ? emptyColor() : fillColor())
.background(follow_state == .unfollows ? fillColor() : emptyColor())
.cornerRadius(20)
.overlay {
@@ -49,20 +48,16 @@ struct FollowButtonView: View {
}
}
func filledTextColor() -> Color {
colorScheme == .light ? Color("DamusWhite") : Color("DamusBlack")
}
func fillColor() -> Color {
colorScheme == .light ? Color("DamusBlack") : Color("DamusWhite")
colorScheme == .light ? .black : .white
}
func emptyColor() -> Color {
Color.black.opacity(0)
colorScheme == .light ? .white : .black
}
func borderColor() -> Color {
colorScheme == .light ? Color("DamusDarkGrey") : Color("DamusLightGrey")
colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.2)
}
}
@@ -70,16 +65,16 @@ struct FollowButtonPreviews: View {
let target: FollowTarget = .pubkey("")
var body: some View {
VStack {
Text("Unfollows", comment: "Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.")
Text("Unfollows")
FollowButtonView(target: target, follow_state: .unfollows)
Text("Following", comment: "Text to indicate that the button next to it is in a state that indicates that it is in the process of following a profile.")
Text("Following")
FollowButtonView(target: target, follow_state: .following)
Text("Follows", comment: "Text to indicate that button next to it is in a state that will follow a profile when tapped.")
Text("Follows")
FollowButtonView(target: target, follow_state: .follows)
Text("Unfollowing", comment: "Text to indicate that the button next to it is in a state that indicates that it is in the process of unfollowing a profile.")
Text("Unfollowing")
FollowButtonView(target: target, follow_state: .unfollowing)
}
}
+3 -3
View File
@@ -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, damus: damus_state, show_friend_confirmed: false, show_nip5_domain: false)
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, 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(NSLocalizedString("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers", comment: "Navigation bar title for view that shows who is following a user."))
.navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers")
.onAppear {
followers.subscribe()
}
@@ -91,7 +91,7 @@ struct FollowingView: View {
.onDisappear {
following.unsubscribe()
}
.navigationBarTitle(NSLocalizedString("\(who) following", comment: "Navigation bar title for view that shows who a user is following."))
.navigationBarTitle("\(who) following")
}
}
+8 -8
View File
@@ -122,21 +122,21 @@ struct LoginView: View {
ZStack(alignment: .top) {
DamusGradient()
VStack {
Text("Login", comment: "Title of view to log into an account.")
Text("Login")
.foregroundColor(.white)
.font(.title)
.padding()
Text("Enter your account key to login:", comment: "Prompt for user to enter an account key to login.")
Text("Enter your account key to login:")
.foregroundColor(.white)
.padding()
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)
KeyInput("nsec1...", 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.", comment: "Warning that the inputted account key for login is an old-style and asking user to verify if it is 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.")
.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.", comment: "Warning that the inputted account key is a public key and the result of what happens because of it.")
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.")
.foregroundColor(.white)
.padding()
}
if let p = parsed {
DamusWhiteButton(NSLocalizedString("Login", comment: "Button to log into account.")) {
DamusWhiteButton("Login") {
if !process_login(p, is_pubkey: self.is_pubkey) {
self.error = NSLocalizedString("Invalid key", comment: "Error message indicating that an invalid account key was entered for login.")
self.error = "Invalid key"
}
}
}
@@ -175,7 +175,7 @@ struct PubkeySwitch: View {
var body: some View {
HStack {
Toggle(isOn: $isOn) {
Text("Public Key?", comment: "Prompt to ask user if the key they entered is a public key.")
Text("Public Key?")
.foregroundColor(.white)
}
}
+5 -7
View File
@@ -31,9 +31,9 @@ func timeline_bit(_ timeline: Timeline) -> Int {
struct TabButton: View {
let timeline: Timeline
let img: String
@Binding var selected: Timeline?
@Binding var new_events: NewEventsBits
@Binding var isSidebarVisible: Bool
let action: (Timeline) -> ()
@@ -56,7 +56,6 @@ struct TabButton: View {
Button(action: {
action(timeline)
new_events = NewEventsBits(prev: new_events, unsetting: timeline)
isSidebarVisible = false
}) {
Label("", systemImage: selected == timeline ? "\(img).fill" : img)
.contentShape(Rectangle())
@@ -70,7 +69,6 @@ struct TabButton: View {
struct TabBar: View {
@Binding var new_events: NewEventsBits
@Binding var selected: Timeline?
@Binding var isSidebarVisible: Bool
let action: (Timeline) -> ()
@@ -78,10 +76,10 @@ struct TabBar: View {
VStack {
Divider()
HStack {
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("1")
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("2")
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("3")
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, isSidebarVisible: $isSidebarVisible, action: action).keyboardShortcut("4")
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("1")
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("2")
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("3")
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action).keyboardShortcut("4")
}
}
}
+1 -1
View File
@@ -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 >", comment: "Placeholder for event mention.")
Text("< e >")
//EventBlockView(pubkey: mention.ref.ref_id, relay: mention.ref.relay_id)
}
}

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