Compare commits

..

1 Commits

Author SHA1 Message Date
cc95402581 Swap order of Logout and Cancel alert buttons
To conform to Apple's Human Interface Guidelines
2022-12-27 08:27:38 -04:00
105 changed files with 320 additions and 3203 deletions

View File

@@ -1,105 +1,3 @@
## [1.0.0] - 2023-01-01
### Added
- Parse links in profiles (Lionello Lunesu)
- 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)
- Added option to choose default wallet (Suhail Saqan)
### Changed
- Switch like from ❤️ to 🤙 (William Casarin)
- Internationalize relative dates (Terry Yiu)
### Fixed
- Fix but where text was not showing after invoices (William Casarin)
- Load profiles in DMs and notifications (William Casarin)
- Fix expanding profile picture (nosestr bug) (Joel Klabo)
- Fix padding on threads and search results views (OlegAba)
- Don't badge DMs if sent by you (Joel Klabo)
- Reset relay in Add Relay view after adding (Joel Klabo)
[1.0.0]: https://github.com/damus-io/damus/releases/tag/v1.0.0
## [0.1.8-9] - 2022-12-29
### Added
- Relay list on user profiles
### Changed
- Show recommended relays in config. Currently just a fixed set. (William Casarin)
- Ensure contact relay list is kept in sync with internal relay pool (William Casarin)
### Fixed
- Fixed issue where contact list would sometimes revert to an older version (William Casarin)
- Don't show boosts in threads (Thomas)
[0.1.8-9]: https://github.com/damus-io/damus/releases/tag/v0.1.8-9
## [0.1.8-6] - 2022-12-28
### Added
- Lightning wallet selector (Suhail Saqan)
- Cmd-{1,2,3,4} to switch between tabs on MacOS (Jonathan Milligan)
- Shift-Cmd-N to create a post on MacOS (Jonathan Milligan)
- Link Previews! (Sam DuBois)
- Added paste and delete buttons to add relay field (Suhail Saqan)
### Changed
- Blur and opaque non-friend images rather than only display the link (Sam DuBois)
- Remove URLs in content text when image is displayed (Sam DuBois)
- Show non-image URLs as clickable link views (Sam DuBois)
- Adjusted Pay button on invoices. (Sam DuBois)
### Fixed
- Fix crash with @ sign in some posts (Pablo Fernandez)
- Swapped order of Logout and Cancel alert buttons (Terry Yiu)
- Fixed padding issue on tabbar on some devices (Sam DuBois)
- Fix post button moving after selecting from search result (OlegAba)
- Don't show white background on images in dark mode (William Casarin)
[0.1.8-6]: https://github.com/damus-io/damus/releases/tag/v0.1.8-6
## [0.1.8-5] - 2022-12-27
### Added
- Added the ability to zoom profile pic on profile page
### Changed
- Improve visual composition of threads
- Show npub abbreviations instead of old-style hex
- Added search placeholder and larger cancel button
- Swap order of Boost and Cancel alert buttons
- Rename "Copy Note" to "Copy Note JSON"
### Fixed
- Don't cutoff gifs
- Fixed bug where booster's names are not displayed
[0.1.8-5]: https://github.com/damus-io/damus/releases/tag/v0.1.8-5
## [0.1.8-4] - 2022-12-26
### Added
@@ -266,3 +164,4 @@
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2

View File

@@ -1,804 +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.lproj/InfoPlist.strings" source-language="en" target-language="en" 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>
</body>
</file>
<file original="damus/en.lproj/Localizable.strings" source-language="en" target-language="en" 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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@" xml:space="preserve">
<source>%@</source>
<target>%@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ following" xml:space="preserve">
<source>%@ following</source>
<target>%@ following</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@'s Followers" xml:space="preserve">
<source>%@'s Followers</source>
<target>%@'s Followers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." xml:space="preserve">
<source>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</source>
<target>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs" xml:space="preserve">
<source>%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs</source>
<target>%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@. Tip your friend's posts and stack sats with Bitcoin⚡, the native currency of the internet." xml:space="preserve">
<source>%@. Tip your friend's posts and stack sats with Bitcoin⚡, the native currency of the internet.</source>
<target>%@. Tip your friend's posts and stack sats with Bitcoin⚡, the native currency of the internet.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld" xml:space="preserve">
<source>%lld</source>
<target>%lld</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld/%lld" xml:space="preserve">
<source>%lld/%lld</source>
<target>%lld/%lld</target>
<note>No comment provided by engineer.</note>
</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="+" xml:space="preserve">
<source>+</source>
<target>+</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="&lt; e &gt;" xml:space="preserve">
<source>&lt; e &gt;</source>
<target>&lt; e &gt;</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="@" xml:space="preserve">
<source>@</source>
<target>@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<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>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add Relay" xml:space="preserve">
<source>Add Relay</source>
<target>Add Relay</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Are you sure you want to boost this post?" xml:space="preserve">
<source>Are you sure you want to boost this post?</source>
<target>Are you sure you want to boost this post?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus." xml:space="preserve">
<source>Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus.</source>
<target>Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus.</target>
<note>No comment provided by engineer.</note>
</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>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Boosted" xml:space="preserve">
<source>Boosted</source>
<target>Boosted</target>
<note>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Cancel</target>
<note>No comment provided by engineer.</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="Copied" xml:space="preserve">
<source>Copied</source>
<target>Copied</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Copy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy Account ID" xml:space="preserve">
<source>Copy Account ID</source>
<target>Copy Account ID</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy Image" xml:space="preserve">
<source>Copy Image</source>
<target>Copy Image</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy Image URL" xml:space="preserve">
<source>Copy Image URL</source>
<target>Copy Image URL</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy LNURL" xml:space="preserve">
<source>Copy LNURL</source>
<target>Copy LNURL</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy Note ID" xml:space="preserve">
<source>Copy Note ID</source>
<target>Copy Note ID</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy Note JSON" xml:space="preserve">
<source>Copy Note JSON</source>
<target>Copy Note JSON</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy Text" xml:space="preserve">
<source>Copy Text</source>
<target>Copy Text</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy User ID" xml:space="preserve">
<source>Copy User ID</source>
<target>Copy User ID</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy invoice" xml:space="preserve">
<source>Copy invoice</source>
<target>Copy invoice</target>
<note>No comment provided by engineer.</note>
</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Damus" xml:space="preserve">
<source>Damus</source>
<target>Damus</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Default Wallet" xml:space="preserve">
<source>Default Wallet</source>
<target>Default Wallet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Delete</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss" xml:space="preserve">
<source>Dismiss</source>
<target>Dismiss</target>
<note>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Earn Money" xml:space="preserve">
<source>Earn Money</source>
<target>Earn Money</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit" xml:space="preserve">
<source>Edit</source>
<target>Edit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit Profile" xml:space="preserve">
<source>Edit Profile</source>
<target>Edit Profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypted" xml:space="preserve">
<source>Encrypted</source>
<target>Encrypted</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypted DMs" xml:space="preserve">
<source>Encrypted DMs</source>
<target>Encrypted DMs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Enter your account key to login:" xml:space="preserve">
<source>Enter your account key to login:</source>
<target>Enter your account key to login:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Error: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Filter State" xml:space="preserve">
<source>Filter State</source>
<target>Filter State</target>
<note>No comment provided by engineer.</note>
</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Following" xml:space="preserve">
<source>Following</source>
<target>Following</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Global" xml:space="preserve">
<source>Global</source>
<target>Global</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Goto post %@" xml:space="preserve">
<source>Goto post %@</source>
<target>Goto post %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Goto profile %@" xml:space="preserve">
<source>Goto profile %@</source>
<target>Goto profile %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve">
<source>Home</source>
<target>Home</target>
<note>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Lightning Invoice" xml:space="preserve">
<source>Lightning Invoice</source>
<target>Lightning Invoice</target>
<note>No comment provided by engineer.</note>
</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Logout" xml:space="preserve">
<source>Logout</source>
<target>Logout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
<target>Make sure your nsec account key is saved before you logout or you will lose access to this account</target>
<note>No comment provided by engineer.</note>
</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
<source>Nothing to see here. Check back later!</source>
<target>Nothing to see here. Check back later!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source>
<target>Notifications</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Pay" xml:space="preserve">
<source>Pay</source>
<target>Pay</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Pay the lightning invoice" xml:space="preserve">
<source>Pay the lightning invoice</source>
<target>Pay the lightning invoice</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Posts" xml:space="preserve">
<source>Posts</source>
<target>Posts</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Posts &amp; Replies" xml:space="preserve">
<source>Posts &amp; Replies</source>
<target>Posts &amp; Replies</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private" xml:space="preserve">
<source>Private</source>
<target>Private</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private Key" xml:space="preserve">
<source>Private Key</source>
<target>Private Key</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PrivateKey" xml:space="preserve">
<source>PrivateKey</source>
<target>PrivateKey</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile Picture" xml:space="preserve">
<source>Profile Picture</source>
<target>Profile Picture</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Public Account ID" xml:space="preserve">
<source>Public Account ID</source>
<target>Public Account ID</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Public Key" xml:space="preserve">
<source>Public Key</source>
<target>Public Key</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Public Key?" xml:space="preserve">
<source>Public Key?</source>
<target>Public Key?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recommended Relays" xml:space="preserve">
<source>Recommended Relays</source>
<target>Recommended Relays</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relay" xml:space="preserve">
<source>Relay</source>
<target>Relay</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relays" xml:space="preserve">
<source>Relays</source>
<target>Relays</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reply to self" xml:space="preserve">
<source>Reply to self</source>
<target>Reply to self</target>
<note>Label to indicate that the user is replying to themself.</note>
</trans-unit>
<trans-unit id="Replying to:" xml:space="preserve">
<source>Replying to:</source>
<target>Replying to:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>Reset</target>
<note>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Search hashtag: #%@" xml:space="preserve">
<source>Search hashtag: #%@</source>
<target>Search hashtag: #%@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Search..." xml:space="preserve">
<source>Search...</source>
<target>Search...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Secret Account Login Key" xml:space="preserve">
<source>Secret Account Login Key</source>
<target>Secret Account Login Key</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Select a lightning wallet" xml:space="preserve">
<source>Select a lightning wallet</source>
<target>Select a lightning wallet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Select default wallet" xml:space="preserve">
<source>Select default wallet</source>
<target>Select default wallet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send a message to start the conversation..." xml:space="preserve">
<source>Send a message to start the conversation...</source>
<target>Send a message to start the conversation...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Settings" xml:space="preserve">
<source>Settings</source>
<target>Settings</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Share</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show" xml:space="preserve">
<source>Show</source>
<target>Show</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source>
<target>Show wallet selector</target>
<note>No comment provided by engineer.</note>
</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key." xml:space="preserve">
<source>This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key.</source>
<target>This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This is your account ID, you can give this to your friends so that they can follow you. Click to copy." xml:space="preserve">
<source>This is your account ID, you can give this to your friends so that they can follow you. Click to copy.</source>
<target>This is your account ID, you can give this to your friends so that they can follow you. Click to copy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!" xml:space="preserve">
<source>This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!</source>
<target>This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Thread" xml:space="preserve">
<source>Thread</source>
<target>Thread</target>
<note>No comment provided by engineer.</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>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Username" xml:space="preserve">
<source>Username</source>
<target>Username</target>
<note>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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve">
<source>Website</source>
<target>Website</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Welcome to the social network %@ control." xml:space="preserve">
<source>Welcome to the social network %@ control.</source>
<target>Welcome to the social network %@ control.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Welcome, %@!" xml:space="preserve">
<source>Welcome, %@!</source>
<target>Welcome, %@!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your Name" xml:space="preserve">
<source>Your Name</source>
<target>Your Name</target>
<note>No comment provided by engineer.</note>
</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="https://example.com/pic.jpg" xml:space="preserve">
<source>https://example.com/pic.jpg</source>
<target>https://example.com/pic.jpg</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="https://jb55.com" xml:space="preserve">
<source>https://jb55.com</source>
<target>https://jb55.com</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="jb55@jb55.com" xml:space="preserve">
<source>jb55@jb55.com</source>
<target>jb55@jb55.com</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="none" xml:space="preserve">
<source>none</source>
<target>none</target>
<note>No comment provided by engineer.</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="optional" xml:space="preserve">
<source>optional</source>
<target>optional</target>
<note>No comment provided by engineer.</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>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<target>you</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="··· %lld other notes ···" xml:space="preserve">
<source>··· %lld other notes ···</source>
<target>··· %lld other notes ···</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="🤙" xml:space="preserve">
<source>🤙</source>
<target>🤙</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="damus/en.lproj/Localizable.stringsdict" source-language="en" target-language="en" 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="/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>

View File

@@ -1,4 +0,0 @@
/* Bundle display name */
"CFBundleDisplayName" = "Damus";
/* Bundle name */
"CFBundleName" = "damus";

View File

@@ -1,42 +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>
</dict>
</plist>

View File

@@ -1,12 +0,0 @@
{
"developmentRegion" : "en",
"project" : "damus.xcodeproj",
"targetLocale" : "en",
"toolInfo" : {
"toolBuildNumber" : "14C18",
"toolID" : "com.apple.dt.xcode",
"toolName" : "Xcode",
"toolVersion" : "14.2"
},
"version" : "1.0"
}

View File

@@ -151,7 +151,7 @@ static bool from_numbers(u64 *res,
return false;
if (!from_number(&p1, s1, len1, tens_factor)
|| !from_number(&p2, s2, len2, tens_factor - (int)len2))
|| !from_number(&p2, s2, len2, tens_factor - len2))
return false;
if (add_overflows_u64(p1, p2))
@@ -453,7 +453,7 @@ bool amount_msat_to_u32(struct amount_msat msat, u32 *millisatoshis)
{
if (amount_msat_greater_eq(msat, AMOUNT_MSAT(0x100000000)))
return false;
*millisatoshis = (u32)msat.millisatoshis;
*millisatoshis = msat.millisatoshis;
return true;
}

View File

@@ -297,7 +297,7 @@ static char *decode_c(struct bolt11 *b11,
if (!pull_uint(hu5, data, data_len, &c, data_length * 5))
return tal_fmt(b11, "c: length %zu chars is excessive",
*data_len);
b11->min_final_cltv_expiry = (u32)c;
b11->min_final_cltv_expiry = c;
/* Can overflow, since c is 64 bits but value must be < 32 bits */
if (b11->min_final_cltv_expiry != c)
return tal_fmt(b11, "c: %"PRIu64" is too large", c);

View File

@@ -241,7 +241,7 @@ static int parse_invoice(struct cursor *cur, struct block *block) {
block->block.invoice.invstr.end = (const char*)end;
block->block.invoice.bolt11 = bolt11;
cur->p = end;
cur->p += end - start;
return 1;
}

View File

@@ -30,7 +30,7 @@ void hash_u5(struct hash_u5 *hu5, const u8 *u5, size_t len)
u5++;
if (hu5->num_bits >= 32) {
be32 be32 = cpu_to_be32((u32)(hu5->buf >> (hu5->num_bits-32)));
be32 be32 = cpu_to_be32(hu5->buf >> (hu5->num_bits-32));
sha256_update(&hu5->hash, &be32, sizeof(be32));
hu5->num_bits -= 32;
}
@@ -40,7 +40,7 @@ void hash_u5(struct hash_u5 *hu5, const u8 *u5, size_t len)
void hash_u5_done(struct hash_u5 *hu5, struct sha256 *res)
{
if (hu5->num_bits) {
be32 be32 = cpu_to_be32((u32)(hu5->buf << (32 - hu5->num_bits)));
be32 be32 = cpu_to_be32(hu5->buf << (32 - hu5->num_bits));
sha256_update(&hu5->hash, &be32, (hu5->num_bits + 7) / 8);
}

View File

@@ -37,7 +37,7 @@ static inline bool assign_overflow_u16(u16 *dst, uint64_t v)
static inline bool assign_overflow_u32(u32 *dst, uint64_t v)
{
*dst = (u32)v;
*dst = v;
return *dst == v;
}
#endif /* LIGHTNING_COMMON_OVERFLOWS_H */

View File

@@ -7,14 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
3165648B295B70D500C64604 /* LinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3165648A295B70D500C64604 /* LinkView.swift */; };
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 */; };
3AB18056296375CA00FD1BD8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AB18052296375CA00FD1BD8 /* InfoPlist.strings */; };
3AB18057296375CA00FD1BD8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AB18054296375CA00FD1BD8 /* Localizable.strings */; };
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
4C06670628FCB08600038D2A /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
@@ -52,7 +47,6 @@
4C363AA228296A7E006E126D /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA128296A7E006E126D /* SearchView.swift */; };
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 */; };
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 */; };
@@ -114,8 +108,6 @@
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
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 */; };
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 */; };
@@ -137,12 +129,8 @@
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */; };
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 */; };
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 */; };
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 */
/* Begin PBXContainerItemProxy section */
@@ -163,18 +151,9 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
3165648A295B70D500C64604 /* LinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkView.swift; sourceTree = "<group>"; };
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
3A4325A92961E11400BFCD9D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
3AB1803D29636FB100FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
3AB18058296377E500FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3AB18059296377E700FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
3AB1805A2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3AB1805B2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
3AB1805C2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
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>"; };
4C06670828FDE64700038D2A /* damus-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "damus-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -214,7 +193,6 @@
4C363AA128296A7E006E126D /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
@@ -306,8 +284,6 @@
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
@@ -332,11 +308,7 @@
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileName.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@@ -460,8 +432,6 @@
4C64987D286D082C00EAE2B3 /* DirectMessagesModel.swift */,
4C216F372871EDE300040376 /* DirectMessageModel.swift */,
4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */,
BA693073295D649800ADDB87 /* UserSettingsStore.swift */,
4FE60CDC295E1C5E00105A1F /* Wallet.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -508,10 +478,6 @@
4C216F33286F5ACD00040376 /* DMView.swift */,
4C06670028FC7C5900038D2A /* RelayView.swift */,
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -539,7 +505,6 @@
4C7FF7D628233637009601DB /* Util */ = {
isa = PBXGroup;
children = (
4C3A1D322960DB0500558C0F /* Markdown.swift */,
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
4C363A8328233689006E126D /* Parser.swift */,
@@ -549,7 +514,6 @@
4C90BD19283AA67F008EE7EF /* Bech32.swift */,
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
3165648A295B70D500C64604 /* LinkView.swift */,
);
path = Util;
sourceTree = "<group>";
@@ -603,9 +567,6 @@
4CE6DEE827F7A08100C66700 /* ContentView.swift */,
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
4CE6DEEC27F7A08200C66700 /* Preview Content */,
3AB18052296375CA00FD1BD8 /* InfoPlist.strings */,
3AB18054296375CA00FD1BD8 /* Localizable.strings */,
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
);
path = damus;
sourceTree = "<group>";
@@ -626,7 +587,6 @@
4C363A9D2828A822006E126D /* ReplyTests.swift */,
4CE6DEF727F7A08200C66700 /* damusTests.swift */,
4C3EA67A28FF7B3900C48A62 /* InvoiceTests.swift */,
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */,
);
path = damusTests;
sourceTree = "<group>";
@@ -741,8 +701,6 @@
knownRegions = (
en,
Base,
es,
fr,
);
mainGroup = 4CE6DEDA27F7A08100C66700;
packageReferences = (
@@ -768,11 +726,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3AB18057296375CA00FD1BD8 /* Localizable.strings in Resources */,
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */,
3AB18056296375CA00FD1BD8 /* InfoPlist.strings in Resources */,
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */,
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -814,7 +769,6 @@
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
4C285C8A2838B985008A31F1 /* ProfilePictureSelector.swift in Sources */,
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
@@ -836,10 +790,8 @@
4C363A8428233689006E126D /* Parser.swift in Sources */,
4CE4F9E328528C5200C00DD9 /* AddRelayView.swift in Sources */,
4C363A9A28283854006E126D /* Reply.swift in Sources */,
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
4C3EA66828FF5F9900C48A62 /* hex.c in Sources */,
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */,
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
4C75EFB128049D510006080F /* NostrResponse.swift in Sources */,
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */,
@@ -852,7 +804,6 @@
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */,
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
@@ -883,7 +834,6 @@
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */,
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */,
3165648B295B70D500C64604 /* LinkView.swift in Sources */,
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */,
4C3EA66028FF5E7700C48A62 /* node_id.c in Sources */,
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
@@ -898,17 +848,14 @@
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */,
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */,
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */,
4C06670B28FDE64700038D2A /* damus.c in Sources */,
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */,
4C99737B28C92A9200E53835 /* ChatroomMetadata.swift in Sources */,
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
4C75EFB528049D790006080F /* Relay.swift in Sources */,
@@ -923,7 +870,6 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
@@ -956,43 +902,11 @@
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */ = {
isa = PBXVariantGroup;
children = (
3A4325A92961E11400BFCD9D /* en */,
3AB1803D29636FB100FD1BD8 /* es */,
3AB1805C2963EF7E00FD1BD8 /* fr */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
};
3AB18052296375CA00FD1BD8 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
3AB18058296377E500FD1BD8 /* es */,
3AB1805A2963EF7E00FD1BD8 /* fr */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
3AB18054296375CA00FD1BD8 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
3AB18059296377E700FD1BD8 /* es */,
3AB1805B2963EF7E00FD1BD8 /* fr */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
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";
@@ -1039,7 +953,7 @@
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;
@@ -1054,7 +968,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";
@@ -1095,7 +1008,7 @@
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;
@@ -1114,14 +1027,13 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 4;
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;
@@ -1135,7 +1047,7 @@
"$(inherited)",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 0.1.8;
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -1154,14 +1066,13 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 4;
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;
@@ -1175,7 +1086,7 @@
"$(inherited)",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 1.0.0;
MARKETING_VERSION = 0.1.8;
PRODUCT_BUNDLE_IDENTIFIER = com.jb55.damus2;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;

View File

@@ -40,8 +40,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/SparrowTek/Vault",
"state" : {
"revision" : "87db56c3c8b6421c65b0745f73e08b0dc56f79d4",
"version" : "1.0.3"
"revision" : "f5707fac23f4a17b3e5ed32dd444f502773615ae",
"version" : "1.0.2"
}
}
],

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "bbw.jpg",
"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: 14 KiB

View File

@@ -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

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "bluewallet.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: 61 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "breez.jpg",
"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: 21 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "cashapp.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: 44 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "lnlink.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: 547 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "muun.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: 25 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "phoenix.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: 2.9 KiB

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

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "strike.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: 23 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "walletofsatoshi.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: 42 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "zebedee.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: 78 KiB

View File

@@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "zeus.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: 109 KiB

View File

@@ -122,7 +122,6 @@ struct ImageCarousel: View {
TabView {
ForEach(urls, id: \.absoluteString) { url in
Rectangle()
.foregroundColor(Color.clear)
.overlay {
KFAnimatedImage(url)
.configure { view in
@@ -132,16 +131,11 @@ struct ImageCarousel: View {
.loadDiskFileSynchronously()
.scaleFactor(UIScreen.main.scale)
.fade(duration: 0.1)
.aspectRatio(contentMode: .fit)
.aspectRatio(contentMode: .fill)
.tabItem {
Text(url.absoluteString)
}
.id(url.absoluteString)
.contextMenu {
Button("Copy Image") {
UIPasteboard.general.string = url.absoluteString
}
}
}
}
}

View File

@@ -7,63 +7,25 @@
import SwiftUI
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
}
guard let url = URL(string: store_link) else {
return
}
guard UIApplication.shared.canOpenURL(url) else {
return
}
UIApplication.shared.open(url)
}
}
struct InvoiceView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.openURL) private var openURL
let invoice: Invoice
@State var showing_select_wallet: Bool = false
@ObservedObject var user_settings = UserSettingsStore()
var PayButton: some View {
Button {
if user_settings.show_wallet_selector {
showing_select_wallet = true
} else {
open_with_wallet(wallet: user_settings.default_wallet.model, invoice: invoice.string)
Button("Pay") {
guard let url = URL(string: "lightning:" + invoice.string) else {
return
}
} label: {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(colorScheme == .light ? .black : .white)
.overlay {
Text("Pay")
.fontWeight(.medium)
.foregroundColor(colorScheme == .light ? .white : .black)
}
}
//.buttonStyle(.bordered)
.onTapGesture {
// Temporary solution so that the "pay" button can be clicked (Yes we need an empty tap gesture)
UIApplication.shared.open(url)
}
.buttonStyle(.bordered)
}
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 10)
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.secondary.opacity(0.1))
VStack(alignment: .leading, spacing: 12) {
VStack(alignment: .trailing, spacing: 12) {
HStack {
Label("", systemImage: "bolt.fill")
.foregroundColor(.orange)
@@ -71,21 +33,17 @@ struct InvoiceView: View {
}
Divider()
Text(invoice.description)
Text(invoice.amount.amount_sats_str())
Text("\(invoice.amount / 1000) sats")
.font(.title)
PayButton
.frame(height: 50)
.zIndex(10.0)
.zIndex(5.0)
}
.padding(30)
}
.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
SelectWalletView(showingSelectWallet: $showing_select_wallet, invoice: invoice.string).environmentObject(user_settings)
.padding()
}
}
}
let test_invoice = Invoice(description: "this is a description", amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119)
let test_invoice = Invoice(description: "this is a description", amount: 10000, string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119)
struct InvoiceView_Previews: PreviewProvider {
static var previews: some View {

View File

@@ -23,14 +23,13 @@ struct InvoicesView: View {
.id(invoice.string)
}
}
.frame(height: 240)
.frame(height: 200)
.tabViewStyle(PageTabViewStyle())
}
}
struct InvoicesView_Previews: PreviewProvider {
static var previews: some View {
InvoicesView(invoices: [Invoice.init(description: "description", amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)])
.frame(width: 300)
InvoicesView(invoices: [Invoice.init(description: "description", amount: 10000, string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)])
}
}

View File

@@ -13,10 +13,7 @@ var BOOTSTRAP_RELAYS = [
"wss://relay.damus.io",
"wss://nostr-relay.wlvs.space",
"wss://nostr.fmt.wiz.biz",
"wss://relay.nostr.bg",
"wss://nostr.oxtr.dev",
"wss://nostr.v0l.io",
"wss://brb.io",
]
struct TimestampedProfile {
@@ -72,7 +69,6 @@ struct ContentView: View {
@State var search_open: Bool = false
@State var filter_state : FilterState = .posts_and_replies
@StateObject var home: HomeModel = HomeModel()
@StateObject var user_settings = UserSettingsStore()
// connect retry timer
let timer = Timer.publish(every: 4, on: .main, in: .common).autoconnect()
@@ -82,14 +78,17 @@ struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
var PostingTimelineView: some View {
VStack {
TabView(selection: $filter_state) {
ContentTimelineView
.tag(FilterState.posts)
ContentTimelineView
.tag(FilterState.posts_and_replies)
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
}
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
.safeAreaInset(edge: .top) {
VStack(spacing: 0) {
@@ -103,19 +102,6 @@ struct ContentView: View {
}
}
var ContentTimelineView: some View {
ZStack {
if let damus = self.damus_state {
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
}
if privkey != nil {
PostButtonContainer {
self.active_sheet = .post
}
}
}
}
var FiltersView: some View {
VStack{
Picker("Filter State", selection: $filter_state) {
@@ -180,7 +166,8 @@ struct ContentView: View {
var MaybeThreadView: some View {
Group {
if let evid = self.active_event_id {
BuildThreadV2View(damus: damus_state!, event_id: evid)
let thread_model = ThreadModel(evid: evid, damus_state: damus_state!)
ThreadView(thread: thread_model, damus: damus_state!, is_chatroom: false)
} else {
EmptyView()
}
@@ -230,7 +217,7 @@ struct ContentView: View {
.foregroundColor(.gray)
}
NavigationLink(destination: ConfigView(state: damus_state!).environmentObject(user_settings)) {
NavigationLink(destination: ConfigView(state: damus_state!)) {
Label("", systemImage: "gear")
}
.buttonStyle(PlainButtonStyle())
@@ -242,7 +229,6 @@ struct ContentView: View {
}
TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
.padding([.bottom], 8)
}
.onAppear() {
self.connect()

View File

@@ -15,23 +15,6 @@
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>river</string>
<string>bitcoinbeach</string>
<string>breez</string>
<string>muun</string>
<string>zeusln</string>
<string>zebedee</string>
<string>lightning</string>
<string>squarecash</string>
<string>phoenix</string>
<string>lnlink</string>
<string>strike</string>
<string>bluewallet</string>
<string>walletofsatoshi</string>
<string>blixtwallet</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View File

@@ -139,12 +139,16 @@ func decode_json_relays(_ content: String) -> [String: RelayInfo]? {
return decode_json(content)
}
func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: String, relay: String) -> NostrEvent? {
var relays = ensure_relay_info(relays: current_relays, content: ev.content)
func remove_relay(ev: NostrEvent, privkey: String, relay: String) -> NostrEvent? {
let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw)
var relays = ensure_relay_info(relays: [damus_relay], content: ev.content)
guard relays.index(forKey: relay) != nil else {
return nil
}
relays.removeValue(forKey: relay)
print("remove_relay \(relays)")
guard let content = encode_json(relays) else {
return nil
}
@@ -155,9 +159,10 @@ func remove_relay(ev: NostrEvent, current_relays: [RelayDescriptor], privkey: St
return new_ev
}
func add_relay(ev: NostrEvent, privkey: String, current_relays: [RelayDescriptor], relay: String, info: RelayInfo) -> NostrEvent? {
var relays = ensure_relay_info(relays: current_relays, content: ev.content)
func add_relay(ev: NostrEvent, privkey: String, relay: String, info: RelayInfo) -> NostrEvent? {
let damus_relay = RelayDescriptor(url: URL(string: "wss://relay.damus.io")!, info: .rw)
var relays = ensure_relay_info(relays: [damus_relay], content: ev.content)
guard relays.index(forKey: relay) == nil else {
return nil
}

View File

@@ -44,7 +44,6 @@ class HomeModel: ObservableObject {
let notifications_subid = UUID().description
let dms_subid = UUID().description
let init_subid = UUID().description
let profiles_subid = UUID().description
@Published var new_events: NewEventsBits = NewEventsBits()
@Published var notifications: [NostrEvent] = []
@@ -235,15 +234,7 @@ class HomeModel: ObservableObject {
//self.events.insert(NostrEvent(content: "NOTICE from \(relay_id): \(msg)", pubkey: "system"), at: 0)
print(msg)
case .eose(let sub_id):
if sub_id == dms_subid {
let dms = dms.dms.flatMap { $0.1.events }
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: dms, damus_state: damus_state)
} else if sub_id == notifications_subid {
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: notifications, damus_state: damus_state)
}
case .eose:
self.loading = false
break
}
@@ -269,9 +260,6 @@ class HomeModel: ObservableObject {
var contacts_filter = NostrFilter.filter_kinds([0])
contacts_filter.authors = friends
var our_contacts_filter = NostrFilter.filter_kinds([3, 0])
our_contacts_filter.authors = [damus_state.pubkey]
var dms_filter = NostrFilter.filter_kinds([
NostrKind.dm.rawValue,
@@ -309,7 +297,7 @@ class HomeModel: ObservableObject {
var home_filters = [home_filter]
var notifications_filters = [notifications_filter]
var contacts_filters = [contacts_filter, our_contacts_filter]
var contacts_filters = [contacts_filter]
var dms_filters = [dms_filter, our_dms_filter]
let last_of_kind = relay_id.flatMap { last_event_of_kind[$0] } ?? [:]
@@ -347,14 +335,12 @@ class HomeModel: ObservableObject {
return m[kind]
}
func handle_last_event(ev: NostrEvent, timeline: Timeline, shouldNotify: Bool = true) {
func handle_last_event(ev: NostrEvent, timeline: Timeline) {
let last_ev = get_last_event(timeline)
if last_ev == nil || last_ev!.created_at < ev.created_at {
save_last_event(ev, timeline: timeline)
if shouldNotify {
new_events = NewEventsBits(prev: new_events, setting: timeline)
}
new_events = NewEventsBits(prev: new_events, setting: timeline)
}
}
@@ -426,13 +412,13 @@ class HomeModel: ObservableObject {
}
if inserted {
handle_last_event(ev: ev, timeline: .dms, shouldNotify: !ours)
handle_last_event(ev: ev, timeline: .dms)
dms.dms = dms.dms.sorted { a, b in
if a.1.events.count > 0 && b.1.events.count > 0 {
return a.1.events.last!.created_at > b.1.events.last!.created_at
}
return false
return true
}
}
}
@@ -457,33 +443,18 @@ func add_contact_if_friend(contacts: Contacts, ev: NostrEvent) {
contacts.add_friend_contact(ev)
}
func load_our_contacts(contacts: Contacts, our_pubkey: String, m_old_ev: NostrEvent?, ev: NostrEvent) {
var new_pks = Set<String>()
func load_our_contacts(contacts: Contacts, our_pubkey: String, ev: NostrEvent) {
guard ev.pubkey == our_pubkey else {
return
}
contacts.event = ev
// our contacts
for tag in ev.tags {
if tag.count >= 2 && tag[0] == "p" {
new_pks.insert(tag[1])
}
}
var old_pks = Set<String>()
// find removed contacts
if let old_ev = m_old_ev {
for tag in old_ev.tags {
if tag.count >= 2 && tag[0] == "p" {
old_pks.insert(tag[1])
}
}
}
let diff = new_pks.symmetricDifference(old_pks)
for pk in diff {
if new_pks.contains(pk) {
notify(.followed, pk)
contacts.add_friend_pubkey(pk)
} else {
notify(.unfollowed, pk)
contacts.remove_friend(pk)
if tag.count > 1 && tag[0] == "p" {
// TODO: validate pubkey?
contacts.add_friend_pubkey(tag[1])
}
}
}
@@ -569,68 +540,51 @@ func robohash(_ pk: String) -> String {
return "https://robohash.org/" + pk
}
func load_our_stuff(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) {
guard ev.pubkey == pubkey else {
return
}
// only use new stuff
if let current_ev = contacts.event {
guard ev.created_at > current_ev.created_at else {
return
}
}
let m_old_ev = contacts.event
contacts.event = ev
load_our_contacts(contacts: contacts, our_pubkey: pubkey, m_old_ev: m_old_ev, ev: ev)
load_our_relays(contacts: contacts, our_pubkey: pubkey, pool: pool, m_old_ev: m_old_ev, ev: ev)
}
func process_contact_event(pool: RelayPool, contacts: Contacts, pubkey: String, ev: NostrEvent) {
load_our_stuff(pool: pool, contacts: contacts, pubkey: pubkey, ev: ev)
load_our_contacts(contacts: contacts, our_pubkey: pubkey, ev: ev)
load_our_relays(our_pubkey: pubkey, pool: pool, ev: ev)
add_contact_if_friend(contacts: contacts, ev: ev)
}
func load_our_relays(contacts: Contacts, our_pubkey: String, pool: RelayPool, m_old_ev: NostrEvent?, ev: NostrEvent) {
let bootstrap_dict: [String: RelayInfo] = [:]
let old_decoded = m_old_ev.flatMap { decode_json_relays($0.content) } ?? BOOTSTRAP_RELAYS.reduce(into: bootstrap_dict) { (d, r) in
d[r] = .rw
func load_our_relays(our_pubkey: String, pool: RelayPool, ev: NostrEvent) {
guard ev.pubkey == our_pubkey else {
return
}
guard let decoded = decode_json_relays(ev.content) else {
return
}
var changed = false
var new = Set<String>()
for key in decoded.keys {
new.insert(key)
}
var old = Set<String>()
for key in old_decoded.keys {
old.insert(key)
}
let diff = old.symmetricDifference(new)
for d in diff {
changed = true
if new.contains(d) {
if let url = URL(string: d) {
try? pool.add_relay(url, info: decoded[d] ?? .rw)
if let url = URL(string: key) {
if let _ = try? pool.add_relay(url, info: decoded[key]!) {
pool.connect(to: [key])
}
} else {
pool.remove_relay(d)
}
}
if changed {
notify(.relays_changed, ())
}
}
func remove_bootstrap_nodes(_ damus_state: DamusState) {
guard let contacts = damus_state.contacts.event else {
return
}
guard let relays = decode_json_relays(contacts.content) else {
return
}
let descriptors = relays.reduce(into: []) { arr, kv in
guard let url = URL(string: kv.key) else {
return
}
arr.append(RelayDescriptor(url: url, info: kv.value))
}
for relay in BOOTSTRAP_RELAYS {
if !(descriptors.contains { ($0 as! RelayDescriptor).url.absoluteString == relay }) {
damus_state.pool.remove_relay(relay)
}
}
}

View File

@@ -34,7 +34,7 @@ struct IdBlock: Identifiable {
struct Invoice {
let description: String
let amount: Amount
let amount: Int64
let string: String
let expiry: UInt64
let payment_hash: Data
@@ -115,7 +115,7 @@ func parse_mentions(content: String, tags: [[String]]) -> [Block] {
blocks_init(&bs)
let bytes = content.utf8CString
let _ = bytes.withUnsafeBufferPointer { p in
bytes.withUnsafeBufferPointer { p in
damus_parse_content(&bs, p.baseAddress)
}
@@ -180,23 +180,6 @@ func maybe_pointee<T>(_ p: UnsafeMutablePointer<T>!) -> T? {
return p.pointee
}
enum Amount: Equatable {
case any
case specific(Int64)
func amount_sats_str() -> String {
switch self {
case .any:
return "Any"
case .specific(let amt):
if amt < 1000 {
return "\(Double(amt) / 1000.0) sats"
}
return "\(amt / 1000) sats"
}
}
}
func convert_invoice_block(_ b: invoice_block) -> Block? {
guard let invstr = strblock_to_string(b.invstr) else {
return nil
@@ -211,8 +194,12 @@ func convert_invoice_block(_ b: invoice_block) -> Block? {
description = String(cString: b11.description)
}
let amount: Amount = maybe_pointee(b11.msat).map { .specific(Int64($0.millisatoshis)) } ?? .any
guard let msat = maybe_pointee(b11.msat) else {
return nil
}
let amount = Int64(msat.millisatoshis)
let payment_hash = Data(bytes: &b11.payment_hash, count: 32)
let hex = hex_encode(payment_hash)
let created_at = b11.timestamp
tal_free(b.bolt11)

View File

@@ -11,7 +11,6 @@ class ProfileModel: ObservableObject, Equatable {
@Published var events: [NostrEvent] = []
@Published var contacts: NostrEvent? = nil
@Published var following: Int = 0
@Published var relays: [String: RelayInfo]? = nil
let pubkey: String
let damus: DamusState
@@ -61,7 +60,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 +69,11 @@ 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 +88,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)
}

View File

@@ -1,32 +0,0 @@
//
// UserSettingsStore.swift
// damus
//
// Created by Suhail Saqan on 12/29/22.
//
import Foundation
class UserSettingsStore: ObservableObject {
@Published var default_wallet: Wallet {
didSet {
UserDefaults.standard.set(default_wallet.rawValue, forKey: "default_wallet")
}
}
@Published var show_wallet_selector: Bool {
didSet {
UserDefaults.standard.set(show_wallet_selector, forKey: "show_wallet_selector")
}
}
init() {
if let defaultWalletName = UserDefaults.standard.string(forKey: "default_wallet"),
let default_wallet = Wallet(rawValue: defaultWalletName) {
self.default_wallet = default_wallet
} else {
self.default_wallet = .system_default_wallet
}
self.show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
}
}

View File

@@ -1,89 +0,0 @@
//
// Wallet.swift
// damus
//
// Created by Benjamin Hakes on 12/29/22.
//
import Foundation
enum Wallet: String, CaseIterable, Identifiable {
var id: String { self.rawValue }
struct Model: Identifiable, Hashable {
var id: String { self.tag }
var index: Int
var tag: String
var displayName : String
var link : String
var appStoreLink : String?
var image: String
}
// New url prefixes needed to be added to LSApplicationQueriesSchemes
case system_default_wallet
case strike
case cashapp
case muun
case bluewallet
case walletofsatoshi
case zebedee
case zeusln
case lnlink
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."),
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:",
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://",
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")
case .bluewallet:
return .init(index: 3, tag: "bluewallet", displayName: NSLocalizedString("Blue Wallet", comment: "Dropdown option label for Lightning wallet, Blue Wallet."), link: "bluewallet:lightning:",
appStoreLink: "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", image: "bluewallet")
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:",
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:",
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:",
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:",
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://",
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:",
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://",
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")
}
}
static var allModels: [Model] {
return Self.allCases.map { $0.model }
}
}

View File

@@ -89,8 +89,7 @@ struct Profile: Codable {
}
static func displayName(profile: Profile?, pubkey: String) -> String {
let pk = bech32_nopre_pubkey(pubkey) ?? pubkey
return profile?.name ?? abbrev_pubkey(pk)
return profile?.name ?? abbrev_pubkey(pubkey)
}
}

View File

@@ -45,19 +45,11 @@ struct EventId: Identifiable, CustomStringConvertible {
}
}
class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Hashable, Comparable {
class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable {
static func == (lhs: NostrEvent, rhs: NostrEvent) -> Bool {
return lhs.id == rhs.id
}
static func < (lhs: NostrEvent, rhs: NostrEvent) -> Bool {
return lhs.created_at < rhs.created_at
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
var id: String
var sig: String
var tags: [[String]]
@@ -272,7 +264,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
return (self.flags & 1) != 0
}
init(content: String, pubkey: String, kind: Int = 1, tags: [[String]] = [], createdAt: Int64 = Int64(Date().timeIntervalSince1970)) {
init(content: String, pubkey: String, kind: Int = 1, tags: [[String]] = []) {
self.id = ""
self.sig = ""
@@ -280,7 +272,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible, Equatable, Has
self.pubkey = pubkey
self.kind = kind
self.tags = tags
self.created_at = createdAt
self.created_at = Int64(Date().timeIntervalSince1970)
}
/// Intiialization statement used to specificy ID
@@ -448,7 +440,7 @@ func hex_encode(_ data: Data) -> String {
func random_bytes(count: Int) -> Data {
var data = Data(count: count)
_ = data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes.baseAddress!)
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
}
return data
}
@@ -558,7 +550,7 @@ func make_like_event(pubkey: String, privkey: String, liked: NostrEvent) -> Nost
var tags: [[String]] = liked.tags.filter { tag in tag.count >= 2 && (tag[0] == "e" || tag[0] == "p") }
tags.append(["e", liked.id])
tags.append(["p", liked.pubkey])
let ev = NostrEvent(content: "🤙", pubkey: pubkey, kind: 7, tags: tags)
let ev = NostrEvent(content: "", pubkey: pubkey, kind: 7, tags: tags)
ev.calculate_id()
ev.sign(privkey: privkey)

View File

@@ -28,14 +28,12 @@ class Relay: Identifiable {
let descriptor: RelayDescriptor
let connection: RelayConnection
var last_pong: UInt32
var flags: Int
init(descriptor: RelayDescriptor, connection: RelayConnection) {
self.flags = 0
self.descriptor = descriptor
self.connection = connection
self.last_pong = 0
}
func mark_broken() {

View File

@@ -179,23 +179,8 @@ class RelayPool {
return nil
}
func record_last_pong(relay_id: String, event: NostrConnectionEvent) {
if case .ws_event(let ws_event) = event {
if case .pong = ws_event {
for relay in relays {
if relay.id == relay_id {
relay.last_pong = UInt32(Date.now.timeIntervalSince1970)
return
}
}
}
}
}
func handle_event(relay_id: String, event: NostrConnectionEvent) {
record_last_pong(relay_id: relay_id, event: event)
// handle reconnect logic, etc?
for handler in handlers {
handler.callback(relay_id, event)
@@ -205,7 +190,6 @@ class RelayPool {
func add_rw_relay(_ pool: RelayPool, _ url: String) {
let url_ = URL(string: url)!
try? pool.add_relay(url_, info: RelayInfo.rw)
try! pool.add_relay(url_, info: RelayInfo.rw)
}

View File

@@ -66,13 +66,6 @@ func bech32_pubkey(_ pubkey: String) -> String? {
return bech32_encode(hrp: "npub", bytes)
}
func bech32_nopre_pubkey(_ pubkey: String) -> String? {
guard let bytes = hex_decode(pubkey) else {
return nil
}
return bech32_encode(hrp: "", bytes)
}
func bech32_note_id(_ evid: String) -> String? {
guard let bytes = hex_decode(evid) else {
return nil

View File

@@ -1,39 +0,0 @@
//
// LinkView.swift
// damus
//
// Created by Sam DuBois on 12/27/22.
//
import SwiftUI
import LinkPresentation
class CustomLinkView: LPLinkView {
override var intrinsicContentSize: CGSize { CGSize(width: 0, height: super.intrinsicContentSize.height) }
}
struct LinkViewRepresentable: UIViewRepresentable {
typealias UIViewType = CustomLinkView
var metadata: LPLinkMetadata?
var url: URL?
func makeUIView(context: Context) -> CustomLinkView {
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) {
}
}

View File

@@ -1,43 +0,0 @@
//
// Markdown.swift
// damus
//
// Created by Lionello Lunesu on 2022-12-28.
//
import Foundation
public struct Markdown {
private let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
/// Ensure the specified URL has a scheme by prepending "https://" if it's absent.
static func withScheme(_ url: any StringProtocol) -> any StringProtocol {
return url.contains("://") ? url : "https://" + url
}
public static func parse(content: String) -> AttributedString {
// Similar to the parsing in NoteContentView
let md_opts: AttributedString.MarkdownParsingOptions =
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
if let txt = try? AttributedString(markdown: content, options: md_opts) {
return txt
} else {
return AttributedString(stringLiteral: content)
}
}
/// Process the input text and add markdown for any embedded URLs.
public func process(_ input: String) -> AttributedString {
let matches = detector.matches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count))
var output = input
// Start with the last match, because replacing the first would invalidate all subsequent indices
for match in matches.reversed() {
guard let range = Range(match.range, in: input) else { continue }
let url = input[range]
output.replaceSubrange(range, with: "[\(url)](\(Markdown.withScheme(url)))")
}
// TODO: escape unintentional markdown
return Markdown.parse(content: output)
}
}

View File

@@ -13,12 +13,6 @@ extension Notification.Name {
}
}
extension Notification.Name {
static var relays_changed: Notification.Name {
return Notification.Name("relays_changed")
}
}
extension Notification.Name {
static var select_event: Notification.Name {
return Notification.Name("select_event")

View File

@@ -97,12 +97,6 @@ func parse_digit(_ p: Parser) -> Int? {
func parse_hex_char(_ p: Parser) -> Character? {
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos)
// Check that we're within the bounds of p.str's length
if p.pos >= p.str.count {
return nil
}
if let c = p.str[ind].unicodeScalars.first {
// hex chars
let d = c.value

View File

@@ -7,48 +7,40 @@
import Foundation
public func time_ago_since(_ date: Date, _ calendar: Calendar = Calendar.current) -> String {
public func time_ago_since(_ date: Date) -> String {
let calendar = Calendar.current
let now = Date()
let unitFlags: NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfMonth, .month, .year]
let unitFlags: NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfYear, .month, .year]
let components = (calendar as NSCalendar).components(unitFlags, from: date, to: now, options: [])
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .abbreviated
formatter.maximumUnitCount = 1
formatter.allowedUnits = unitFlags
// Manually format date component from only the most significant time unit because
// DateComponentsFormatter rounds up by default.
if let year = components.year, year >= 1 {
return formatter.string(from: DateComponents(calendar: calendar, year: year))!
return "\(year)yr"
}
if let month = components.month, month >= 1 {
return formatter.string(from: DateComponents(calendar: calendar, month: month))!
return "\(month)mth"
}
if let week = components.weekOfMonth, week >= 1 {
return formatter.string(from: DateComponents(calendar: calendar, weekOfMonth: week))!
if let week = components.weekOfYear, week >= 1 {
return "\(week)wk"
}
if let day = components.day, day >= 1 {
return formatter.string(from: DateComponents(calendar: calendar, day: day))!
return "\(day)d"
}
if let hour = components.hour, hour >= 1 {
return formatter.string(from: DateComponents(calendar: calendar, hour: hour))!
return "\(hour)h"
}
if let minute = components.minute, minute >= 1 {
return formatter.string(from: DateComponents(calendar: calendar, minute: minute))!
return "\(minute)m"
}
if let second = components.second, second >= 3 {
return formatter.string(from: DateComponents(calendar: calendar, second: second))!
return "\(second)s"
}
return NSLocalizedString("now", comment: "String indicating that a given timestamp just occurred")
return "now"
}

View File

@@ -17,31 +17,9 @@ struct AddRelayView: View {
VStack(alignment: .leading) {
Form {
Section("Add Relay") {
ZStack(alignment: .leading) {
HStack{
TextField("wss://some.relay.com", text: $relay)
.padding(2)
.padding(.leading, 25)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
Label("", systemImage: "xmark.circle.fill")
.foregroundColor(.blue)
.padding(.trailing, -25.0)
.opacity((relay == "") ? 0.0 : 1.0)
.onTapGesture {
self.relay = ""
}
}
Label("", systemImage: "doc.on.clipboard")
.padding(.leading, -10)
.onTapGesture {
if let pastedrelay = UIPasteboard.general.string {
self.relay = pastedrelay
}
}
}
TextField("wss://some.relay.com", text: $relay)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
}
@@ -58,7 +36,6 @@ struct AddRelayView: View {
Button("Add") {
show_add_relay = false
action(relay)
relay = ""
}
.buttonStyle(.borderedProminent)
.contentShape(Rectangle())

View File

@@ -106,7 +106,7 @@ struct ChatView: View {
}
}
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event), artifacts: .just_content(event.content))
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
let bar = make_actionbar_model(ev: event, damus: damus_state)

View File

@@ -17,15 +17,12 @@ struct ConfigView: View {
@State var privkey: String
@State var privkey_copied: Bool = false
@State var pubkey_copied: Bool = false
@State var relays: [RelayDescriptor]
@EnvironmentObject var user_settings: UserSettingsStore
let generator = UIImpactFeedbackGenerator(style: .light)
init(state: DamusState) {
self.state = state
_privkey = State(initialValue: self.state.keypair.privkey_bech32 ?? "")
_relays = State(initialValue: state.pool.descriptors)
}
// TODO: (jb55) could be more general but not gonna worry about it atm
@@ -41,28 +38,16 @@ struct ConfigView: View {
}
}
var recommended: [RelayDescriptor] {
let rs: [RelayDescriptor] = []
return BOOTSTRAP_RELAYS.reduce(into: rs) { (xs, x) in
if let _ = state.pool.get_relay(x) {
} else {
xs.append(RelayDescriptor(url: URL(string: x)!, info: .rw))
}
}
}
var body: some View {
ZStack(alignment: .leading) {
Form {
Section("Relays") {
List(Array(relays), id: \.url) { relay in
RelayView(state: state, relay: relay.url.absoluteString)
}
}
Section("Recommended Relays") {
List(recommended, id: \.url) { r in
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
if let ev = state.contacts.event {
Section("Relays") {
if let relays = decode_json_relays(ev.content) {
List(Array(relays.keys.sorted()), id: \.self) { relay in
RelayView(state: state, ev: ev, relay: relay)
}
}
}
}
@@ -93,17 +78,6 @@ struct ConfigView: View {
}
}
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)
.tag(wallet.model.tag)
}
}
}
Section("Reset") {
Button("Logout") {
confirm_logout = true
@@ -139,6 +113,7 @@ struct ConfigView: View {
}
.sheet(isPresented: $show_add_relay) {
AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in
guard let relay = m_relay else {
return
}
@@ -163,21 +138,17 @@ struct ConfigView: View {
state.pool.connect(to: [new_relay])
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: new_relay, info: info) else {
guard let new_ev = add_relay(ev: ev, privkey: privkey, relay: new_relay, info: info) else {
return
}
process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: ev)
state.contacts.event = new_ev
state.pool.send(.event(new_ev))
}
}
.onReceive(handle_notify(.switched_timeline)) { _ in
dismiss()
}
.onReceive(handle_notify(.relays_changed)) { _ in
self.relays = state.pool.descriptors
}
}
}

View File

@@ -40,26 +40,26 @@ struct CreateAccountView: View {
}
VStack {
SignupForm {
FormLabel(NSLocalizedString("Username", comment: "Label to prompt username entry."))
FormLabel("Username")
HStack(spacing: 0.0) {
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()

View File

@@ -21,9 +21,7 @@ struct DMView: View {
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, 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_images(contacts: damus_state.contacts, ev: event), artifacts: .just_content(event.get_content(damus_state.keypair.privkey)))
.foregroundColor(is_ours ? Color.white : Color.primary)
.padding(10)
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))

View File

@@ -9,19 +9,10 @@ import SwiftUI
struct DirectMessagesView: View {
let damus_state: DamusState
@State var open_dm: Bool = false
@State var pubkey: String = ""
@State var active_model: DirectMessageModel = DirectMessageModel()
@EnvironmentObject var model: DirectMessagesModel
var MainContent: some View {
ScrollView {
let chat = DMChatView(damus_state: damus_state, pubkey: pubkey)
.environmentObject(active_model)
NavigationLink(destination: chat, isActive: $open_dm) {
EmptyView()
}
LazyVStack {
if model.dms.isEmpty, !model.loading {
EmptyTimelineView()
@@ -39,12 +30,12 @@ struct DirectMessagesView: View {
func MaybeEvent(_ tup: (String, DirectMessageModel)) -> some View {
Group {
if let ev = tup.1.events.last {
EventView(damus: damus_state, event: ev, pubkey: tup.0, show_friend_icon: true)
.onTapGesture {
pubkey = tup.0
active_model = tup.1
open_dm = true
}
let chat = DMChatView(damus_state: damus_state, pubkey: tup.0)
.environmentObject(tup.1)
NavigationLink(destination: chat) {
EventView(damus: damus_state, event: ev, pubkey: tup.0, show_friend_icon: true)
}
.buttonStyle(PlainButtonStyle())
} else {
EmptyView()
}

View File

@@ -142,7 +142,7 @@ struct EditMetadataView: View {
}
Section("About Me") {
let placeholder = NSLocalizedString("Absolute Boss", comment: "Placeholder text for About Me description.")
let placeholder = "Absolute Boss"
ZStack(alignment: .topLeading) {
TextEditor(text: $about)
.textInputAutocapitalization(.sentences)
@@ -169,9 +169,9 @@ struct EditMetadataView: View {
Text("NIP-05 Verification")
}, footer: {
if let parts = nip05_parts {
Text(String.localizedStringWithFormat("'%@' at '%@' will be used for verification", parts.username, parts.host))
Text("'\(parts.username)' at '\(parts.host)' will be used for verification")
} else {
Text(String.localizedStringWithFormat("'%@' is an invalid nip05 identifier. It should look like an email.", nip05))
Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.")
}
})

View File

@@ -59,9 +59,9 @@ struct EventActionBar: View {
HStack(alignment: .bottom) {
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
.font(.footnote.weight(.medium))
.foregroundColor(bar.liked ? Color.orange : Color.gray)
.foregroundColor(bar.liked ? Color.red : Color.gray)
LikeButton(liked: bar.liked) {
EventActionButton(img: bar.liked ? "heart.fill" : "heart", col: bar.liked ? Color.red : nil) {
if bar.liked {
notify(.delete, bar.our_like)
} else {
@@ -86,6 +86,7 @@ struct EventActionBar: View {
}
*/
}
.padding(.top, 1)
.alert("Boost", isPresented: $confirm_boost) {
Button("Cancel") {
confirm_boost = false
@@ -138,32 +139,13 @@ struct EventActionBar: View {
func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> some View {
Button(action: action) {
Label("&nbsp;", systemImage: img)
Label("", systemImage: img)
.font(.footnote.weight(.medium))
.foregroundColor(col == nil ? Color.gray : col!)
}
.padding(.trailing, 40)
}
struct LikeButton: View {
let liked: Bool
let action: () -> ()
@Environment(\.colorScheme) var colorScheme
var body: some View {
Button(action: action) {
if liked {
Text("🤙")
} else {
Label("&nbsp;", systemImage: "hand.thumbsup")
.font(.footnote.weight(.medium))
.foregroundColor(Color.gray)
}
}
}
}
struct EventActionBar_Previews: PreviewProvider {
static var previews: some View {

View File

@@ -36,90 +36,6 @@ enum Highlight {
}
}
enum EventViewKind {
case small
case normal
case big
case selected
}
func eventviewsize_to_font(_ size: EventViewKind) -> Font {
switch size {
case .small:
return .body
case .normal:
return .body
case .big:
return .headline
case .selected:
return .custom("selected", size: 21.0)
}
}
struct BuilderEventView: View {
let damus: DamusState
let event_id: String
@State var event: NostrEvent?
@State var subscription_uuid: String = UUID().description
func unsubscribe() {
damus.pool.unsubscribe(sub_id: subscription_uuid)
}
func subscribe(filters: [NostrFilter]) {
damus.pool.register_handler(sub_id: subscription_uuid, handler: handle_event)
damus.pool.send(.subscribe(.init(filters: filters, sub_id: subscription_uuid)))
}
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
guard case .nostr_event(let nostr_response) = ev else {
return
}
guard case .event(let id, let nostr_event) = nostr_response else {
return
}
// Is current event
if id == subscription_uuid {
if event != nil {
return
}
event = nostr_event
unsubscribe()
}
}
func load() {
subscribe(filters: [
NostrFilter(
ids: [self.event_id],
limit: 1
)
])
}
var body: some View {
VStack {
if event == nil {
ProgressView().padding()
} else {
NavigationLink(destination: BuildThreadV2View(damus: damus, event_id: event!.id)) {
EventView(damus: damus, event: event!, show_friend_icon: true, size: .small, embedded: true)
}.buttonStyle(.plain)
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.border(Color.gray.opacity(0.2), width: 1)
.cornerRadius(2)
.onAppear {
self.load()
}
}
}
struct EventView: View {
let event: NostrEvent
let highlight: Highlight
@@ -127,42 +43,34 @@ struct EventView: View {
let damus: DamusState
let pubkey: String
let show_friend_icon: Bool
let size: EventViewKind
let embedded: Bool
@EnvironmentObject var action_bar: ActionBarModel
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool) {
self.event = event
self.highlight = highlight
self.has_action_bar = has_action_bar
self.damus = damus
self.pubkey = event.pubkey
self.show_friend_icon = show_friend_icon
self.size = size
self.embedded = embedded
}
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool) {
self.event = event
self.highlight = .none
self.has_action_bar = false
self.damus = damus
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) {
init(damus: DamusState, event: NostrEvent, pubkey: String, show_friend_icon: Bool) {
self.event = event
self.highlight = .none
self.has_action_bar = false
self.damus = damus
self.pubkey = pubkey
self.show_friend_icon = show_friend_icon
self.size = size
self.embedded = embedded
}
var body: some View {
@@ -200,44 +108,25 @@ struct EventView: View {
func TextEvent(_ event: NostrEvent, pubkey: String) -> some View {
let content = event.get_content(damus.keypair.privkey)
return HStack(alignment: .top) {
let profile = damus.profiles.lookup(id: pubkey)
if size != .selected {
VStack {
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
if !embedded {
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
}
}
Spacer()
VStack {
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
}
Spacer()
}
VStack(alignment: .leading) {
HStack(alignment: .center) {
if size == .selected {
VStack {
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
NavigationLink(destination: pv) {
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
}
}
}
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))
.foregroundColor(.gray)
}
EventProfileName(pubkey: pubkey, profile: profile, contacts: damus.contacts, show_friend_confirmed: show_friend_icon)
Text("\(format_relative_time(event.created_at))")
.font(.body)
.foregroundColor(.gray)
}
if event.is_reply(damus.keypair.privkey) {
@@ -247,57 +136,16 @@ struct EventView: View {
.frame(maxWidth: .infinity, alignment: .leading)
}
let should_show_img = should_show_images(contacts: damus.contacts, ev: event, our_pubkey: damus.pubkey)
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, show_images: should_show_images(contacts: damus.contacts, ev: event), artifacts: .just_content(content))
.frame(maxWidth: .infinity, alignment: .leading)
.allowsHitTesting(!embedded)
if !embedded {
let blocks = event.blocks(damus.keypair.privkey).filter { block in
guard case .mention(let mention) = block else {
return false
}
guard case .event = mention.type else {
return false
}
if mention.ref.key != "e" {
return false
}
return true
}
/// MARK: - Preview
if let firstBlock = blocks.first, case .mention(let mention) = firstBlock, mention.ref.key == "e" {
BuilderEventView(damus: damus, event_id: mention.ref.id)
}
if has_action_bar {
let bar = make_actionbar_model(ev: event, damus: damus)
EventActionBar(damus_state: damus, event: event, bar: bar)
}
if !embedded {
if has_action_bar {
if size == .selected {
Text("\(format_date(event.created_at))")
.padding(.top, 10)
.font(.footnote)
.foregroundColor(.gray)
Divider()
.padding([.bottom], 4)
} else {
Rectangle().frame(height: 2).opacity(0)
}
let bar = make_actionbar_model(ev: event, damus: damus)
EventActionBar(damus_state: damus, event: event, bar: bar)
}
Divider()
.padding([.top], 4)
}
Divider()
.padding([.top], 4)
}
.padding([.leading], 2)
}
@@ -311,10 +159,7 @@ struct EventView: View {
}
// blame the porn bots for this code
func should_show_images(contacts: Contacts, ev: NostrEvent, our_pubkey: String) -> Bool {
if ev.pubkey == our_pubkey {
return true
}
func should_show_images(contacts: Contacts, ev: NostrEvent) -> Bool {
if contacts.is_in_friendosphere(ev.pubkey) {
return true
}
@@ -386,22 +231,13 @@ func format_relative_time(_ created_at: Int64) -> String
return time_ago_since(Date(timeIntervalSince1970: Double(created_at)))
}
func format_date(_ created_at: Int64) -> String {
let date = Date(timeIntervalSince1970: TimeInterval(created_at))
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.dateStyle = .short
return dateFormatter.string(from: date)
}
func reply_desc(profiles: Profiles, event: NostrEvent) -> String {
let desc = make_reply_description(event.tags)
let pubkeys = desc.pubkeys
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 {
@@ -411,14 +247,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.localizedStringWithFormat("Replying to %@ & %@", 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)" : ""
}
@@ -443,23 +285,6 @@ func make_actionbar_model(ev: NostrEvent, damus: DamusState) -> ActionBarModel {
struct EventView_Previews: PreviewProvider {
static var previews: some View {
VStack {
EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true, size: .small)
EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true, size: .normal)
EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true, size: .big)
EventView(
event: NostrEvent(
content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool",
pubkey: "pk",
createdAt: Int64(Date().timeIntervalSince1970 - 100)
),
highlight: .none,
has_action_bar: true,
damus: test_damus_state(),
show_friend_icon: true,
size: .selected
)
}
EventView(damus: test_damus_state(), event: NostrEvent(content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool", pubkey: "pk"), show_friend_icon: true)
}
}

View File

@@ -10,11 +10,9 @@ import SwiftUI
struct FollowUserView: View {
let target: FollowTarget
let damus_state: DamusState
static let markdown = Markdown()
var body: some View {
HStack {
HStack(alignment: .top) {
let pmodel = ProfileModel(pubkey: target.pubkey, damus: damus_state)
let followers = FollowersModel(damus_state: damus_state, target: target.pubkey)
let pv = ProfileView(damus_state: damus_state, profile: pmodel, followers: followers)
@@ -25,10 +23,8 @@ struct FollowUserView: View {
VStack(alignment: .leading) {
let profile = damus_state.profiles.lookup(id: target.pubkey)
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
if let about = profile?.about {
Text(FollowUserView.markdown.process(about))
.lineLimit(3)
.font(.footnote)
if let about = profile.flatMap { $0.about } {
Text(about)
}
}
@@ -55,7 +51,6 @@ struct FollowersView: View {
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
}
}
.padding()
}
.navigationBarTitle("\(Profile.displayName(profile: profile, pubkey: whos))'s Followers")
.onAppear {
@@ -83,7 +78,6 @@ struct FollowingView: View {
FollowUserView(target: .pubkey(pk), damus_state: damus_state)
}
}
.padding()
}
.onAppear {
following.subscribe()

View File

@@ -76,11 +76,14 @@ struct TabBar: View {
VStack {
Divider()
HStack {
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")
TabButton(timeline: .home, img: "house", selected: $selected, new_events: $new_events, action: action)
TabButton(timeline: .dms, img: "bubble.left.and.bubble.right", selected: $selected, new_events: $new_events, action: action)
TabButton(timeline: .search, img: "magnifyingglass.circle", selected: $selected, new_events: $new_events, action: action)
TabButton(timeline: .notifications, img: "bell", selected: $selected, new_events: $new_events, action: action)
}
}
}
}

View File

@@ -14,8 +14,7 @@ struct MentionView: View {
var body: some View {
switch mention.type {
case .pubkey:
let pk = bech32_pubkey(mention.ref.ref_id) ?? mention.ref.ref_id
PubkeyView(pubkey: pk, relay: mention.ref.relay_id)
PubkeyView(pubkey: mention.ref.ref_id, relay: mention.ref.relay_id)
case .event:
Text("< e >")
//EventBlockView(pubkey: mention.ref.ref_id, relay: mention.ref.relay_id)

View File

@@ -6,16 +6,14 @@
//
import SwiftUI
import LinkPresentation
struct NoteArtifacts {
let content: String
let images: [URL]
let invoices: [Invoice]
let links: [URL]
static func just_content(_ content: String) -> NoteArtifacts {
NoteArtifacts(content: content, images: [], invoices: [], links: [])
NoteArtifacts(content: content, images: [], invoices: [])
}
}
@@ -23,7 +21,6 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
let blocks = ev.blocks(privkey)
var invoices: [Invoice] = []
var img_urls: [URL] = []
var link_urls: [URL] = []
let txt = blocks.reduce("") { str, block in
switch block {
case .mention(let m):
@@ -36,20 +33,14 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
invoices.append(invoice)
return str
case .url(let url):
// Handle Image URLs
if is_image_url(url) {
// Append Image
img_urls.append(url)
} else {
link_urls.append(url)
}
return str
return str + url.absoluteString
}
}
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices, links: link_urls)
return NoteArtifacts(content: txt, images: img_urls, invoices: invoices)
}
func is_image_url(_ url: URL) -> Bool {
@@ -66,43 +57,30 @@ struct NoteContentView: View {
@State var artifacts: NoteArtifacts
@State var metaData: LPLinkMetadata? = nil
let size: EventViewKind
func MainContent() -> some View {
let md_opts: AttributedString.MarkdownParsingOptions =
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
return VStack(alignment: .leading) {
Text(Markdown.parse(content: artifacts.content))
.font(eventviewsize_to_font(size))
if let txt = try? AttributedString(markdown: artifacts.content, options: md_opts) {
Text(txt)
.font(.body)
} else {
Text(artifacts.content)
.font(.body)
}
if show_images && artifacts.images.count > 0 {
ImageCarousel(urls: artifacts.images)
} else if !show_images && artifacts.images.count > 0 {
ImageCarousel(urls: artifacts.images)
.blur(radius: 10)
.overlay {
Rectangle()
.opacity(0.50)
}
.cornerRadius(10)
}
if artifacts.invoices.count > 0 {
InvoicesView(invoices: artifacts.invoices)
}
if show_images, self.metaData != nil {
LinkViewRepresentable(metadata: self.metaData)
} else {
ForEach(artifacts.links, id:\.self) { link in
LinkViewRepresentable(url: link)
.frame(height: 50)
}
.frame(width: 200)
}
}
}
var body: some View {
MainContent()
.animation(.easeInOut, value: metaData)
.onAppear() {
self.artifacts = render_note_content(ev: event, profiles: profiles, privkey: privkey)
}
@@ -122,28 +100,6 @@ struct NoteContentView: View {
}
}
}
.task {
if show_images, artifacts.links.count == 1 {
self.metaData = await getMetaData(for: artifacts.links.first!)
}
}
}
func getMetaData(for url: URL) async -> LPLinkMetadata? {
// iOS 15 is crashing for some reason
guard #available(iOS 16, *) else {
return nil
}
let provider = LPMetadataProvider()
do {
return try await provider.startFetchingMetadata(for: url)
} catch {
return nil
}
}
}
@@ -159,8 +115,8 @@ func mention_str(_ m: Mention, profiles: Profiles) -> String {
let disp = Profile.displayName(profile: profile, pubkey: pk)
return "[@\(disp)](nostr:\(encode_pubkey_uri(m.ref)))"
case .event:
let bevid = bech32_note_id(m.ref.ref_id) ?? m.ref.ref_id
return "[@\(abbrev_pubkey(bevid))](nostr:\(encode_event_id_uri(m.ref)))"
let evid = m.ref.ref_id
return "[&\(abbrev_pubkey(evid))](nostr:\(encode_event_id_uri(m.ref)))"
}
}
@@ -169,7 +125,7 @@ struct NoteContentView_Previews: PreviewProvider {
static var previews: some View {
let state = test_damus_state()
let content = "hi there https://jb55.com/s/Oct12-150217.png 5739a762ef6124dd.jpg"
let artifacts = NoteArtifacts(content: content, images: [], invoices: [], links: [])
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, show_images: true, artifacts: artifacts, size: .normal)
let artifacts = NoteArtifacts(content: content, images: [], invoices: [])
NoteContentView(privkey: "", event: NostrEvent(content: content, pubkey: "pk"), profiles: state.profiles, show_images: true, artifacts: artifacts)
}
}

View File

@@ -23,7 +23,6 @@ func PostButton(action: @escaping () -> ()) -> some View {
radius: 3,
x: 3,
y: 3)
.keyboardShortcut("n", modifiers: [.command, .shift])
}
func PostButtonContainer(action: @escaping () -> ()) -> some View {

View File

@@ -12,7 +12,7 @@ enum NostrPostResult {
case cancel
}
let POST_PLACEHOLDER = NSLocalizedString("Type your post here...", comment: "Text box prompt to ask user to type their post.")
let POST_PLACEHOLDER = "Type your post here..."
struct PostView: View {
@State var post: String = ""

View File

@@ -73,12 +73,14 @@ struct ProfileName: View {
var body: some View {
HStack {
Text(prefix + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
.font(.body)
.fontWeight(prefix == "@" ? .none : .bold)
if let friend = friend_icon {
Image(systemName: friend)
if let frend = friend_icon {
Label("", systemImage: frend)
.foregroundColor(.gray)
.font(.footnote)
}
}
.onReceive(handle_notify(.profile_updated)) { notif in
@@ -102,24 +104,20 @@ struct EventProfileName: View {
@State var display_name: String?
let size: EventViewKind
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
init(pubkey: String, profile: Profile?, contacts: Contacts, show_friend_confirmed: Bool) {
self.pubkey = pubkey
self.profile = profile
self.prefix = ""
self.contacts = contacts
self.show_friend_confirmed = show_friend_confirmed
self.size = size
}
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool, size: EventViewKind = .normal) {
init(pubkey: String, profile: Profile?, prefix: String, contacts: Contacts, show_friend_confirmed: Bool) {
self.pubkey = pubkey
self.profile = profile
self.prefix = prefix
self.contacts = contacts
self.show_friend_confirmed = show_friend_confirmed
self.size = size
}
var friend_icon: String? {
@@ -146,10 +144,10 @@ struct EventProfileName: View {
Text("@" + String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
.foregroundColor(.gray)
.font(eventviewsize_to_font(size))
.font(.body)
} else {
Text(String(display_name ?? Profile.displayName(profile: profile, pubkey: pubkey)))
.font(eventviewsize_to_font(size))
.font(.body)
.fontWeight(.bold)
}

View File

@@ -33,6 +33,8 @@ func pfp_line_width(_ h: Highlight) -> CGFloat {
}
struct InnerProfilePicView: View {
@Environment(\.redactionReasons) private var reasons
let url: URL?
let pubkey: String
let size: CGFloat
@@ -52,17 +54,21 @@ struct InnerProfilePicView: View {
var body: some View {
Group {
KFAnimatedImage(url)
.configure { view in
view.framePreloadCount = 1
}
.placeholder { _ in
Placeholder
}
.cacheOriginalImage()
.scaleFactor(UIScreen.main.scale)
.loadDiskFileSynchronously()
.fade(duration: 0.1)
if reasons.isEmpty {
KFAnimatedImage(url)
.configure { view in
view.framePreloadCount = 1
}
.placeholder { _ in
Placeholder
}
.cacheOriginalImage()
.scaleFactor(UIScreen.main.scale)
.loadDiskFileSynchronously()
.fade(duration: 0.1)
} else {
KFImage(url)
}
}
.frame(width: size, height: size)
.clipShape(Circle())

View File

@@ -22,13 +22,13 @@ enum FollowState {
func follow_btn_txt(_ fs: FollowState) -> String {
switch fs {
case .follows:
return NSLocalizedString("Unfollow", comment: "Button to unfollow a user.")
return "Unfollow"
case .following:
return NSLocalizedString("Following...", comment: "Label to indicate that the user is in the process of following another user.")
return "Following..."
case .unfollowing:
return NSLocalizedString("Unfollowing...", comment: "Label to indicate that the user is in the process of unfollowing another user.")
return "Unfollowing..."
case .unfollows:
return NSLocalizedString("Follow", comment: "Button to follow a user.")
return "Follow"
}
}
@@ -119,22 +119,16 @@ struct ProfileView: View {
@StateObject var profile: ProfileModel
@StateObject var followers: FollowersModel
@State private var showingEditProfile = false
@State var showing_select_wallet: Bool = false
@State var is_zoomed: Bool = false
@StateObject var user_settings = UserSettingsStore()
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
//@EnvironmentObject var profile: ProfileModel
func LNButton(lnurl: String, profile: Profile) -> some View {
func LNButton(_ url: URL, profile: Profile) -> some View {
Button(action: {
if user_settings.show_wallet_selector {
showing_select_wallet = true
} else {
open_with_wallet(wallet: user_settings.default_wallet.model, invoice: lnurl)
}
UIApplication.shared.open(url)
}) {
Image(systemName: "bolt.circle")
.symbolRenderingMode(.palette)
@@ -144,17 +138,12 @@ struct ProfileView: View {
Button {
UIPasteboard.general.string = profile.lnurl ?? ""
} label: {
Label("Copy LNURL", systemImage: "doc.on.doc")
Label("Copy LNUrl", systemImage: "doc.on.doc")
}
}
}.sheet(isPresented: $showing_select_wallet, onDismiss: {showing_select_wallet = false}) {
SelectWalletView(showingSelectWallet: $showing_select_wallet, invoice: lnurl)
.environmentObject(user_settings)
}
}
static let markdown = Markdown()
var DMButton: some View {
let dm_model = damus_state.dms.lookup_or_create(profile.pubkey)
let dmview = DMChatView(damus_state: damus_state, pubkey: profile.pubkey)
@@ -172,24 +161,25 @@ struct ProfileView: View {
let data = damus_state.profiles.lookup(id: profile.pubkey)
HStack(alignment: .center) {
ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .none, profiles: damus_state.profiles)
ProfilePicView(pubkey: profile.pubkey, size: PFP_SIZE, highlight: .custom(Color.black, 2), profiles: damus_state.profiles)
.onTapGesture {
is_zoomed.toggle()
}
.sheet(isPresented: $is_zoomed) {
ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles)
ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .custom(Color.black, 2), profiles: damus_state.profiles)
}
Spacer()
if let profile = data {
if let lnurl = profile.lnurl {
LNButton(lnurl: lnurl, profile: profile)
if let lnuri = profile.lightning_uri {
LNButton(lnuri, profile: profile)
}
}
DMButton
if profile.pubkey != damus_state.pubkey {
FollowButtonView(
target: profile.get_follow_target(),
@@ -206,7 +196,7 @@ struct ProfileView: View {
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts)
.padding(.bottom)
Text(ProfileView.markdown.process(data?.about ?? ""))
Text(data?.about ?? "")
.font(.subheadline)
Divider()
@@ -241,17 +231,6 @@ struct ProfileView: View {
followers.subscribe()
}
}
if let relays = profile.relays {
NavigationLink(destination: UserRelaysView(state: damus_state, pubkey: profile.pubkey, relays: Array(relays.keys).sorted())) {
Text("\(relays.keys.count)")
.font(.subheadline.weight(.medium))
Text("Relays")
.font(.subheadline)
.foregroundColor(.gray)
}
.buttonStyle(PlainButtonStyle())
}
}
}
}

View File

@@ -1,50 +0,0 @@
//
// RecommendedRelayView.swift
// damus
//
// Created by William Casarin on 2022-12-29.
//
import SwiftUI
struct RecommendedRelayView: View {
let damus: DamusState
let relay: String
let add_button: Bool
init(damus: DamusState, relay: String) {
self.damus = damus
self.relay = relay
self.add_button = true
}
init(damus: DamusState, relay: String, add_button: Bool) {
self.damus = damus
self.relay = relay
self.add_button = add_button
}
var body: some View {
HStack {
Text(relay)
Spacer()
if let ev = damus.contacts.event, add_button {
if let privkey = damus.keypair.privkey {
Button("Add") {
guard let ev = add_relay(ev: ev, privkey: privkey, current_relays: damus.pool.descriptors, relay: relay, info: .rw) else {
return
}
process_contact_event(pool: damus.pool, contacts: damus.contacts, pubkey: damus.pubkey, ev: ev)
damus.pool.send(.event(ev))
}
}
}
}
}
}
struct RecommendedRelayView_Previews: PreviewProvider {
static var previews: some View {
RecommendedRelayView(damus: test_damus_state(), relay: "wss://relay.damus.io")
}
}

View File

@@ -9,6 +9,7 @@ import SwiftUI
struct RelayView: View {
let state: DamusState
let ev: NostrEvent
let relay: String
let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
@@ -44,48 +45,25 @@ struct RelayView: View {
}
.swipeActions {
if let privkey = state.keypair.privkey {
RemoveAction(privkey: privkey)
}
}
.contextMenu {
CopyAction(relay: relay)
if let privkey = state.keypair.privkey {
RemoveAction(privkey: privkey)
Button {
guard let new_ev = remove_relay( ev: ev, privkey: privkey, relay: relay) else {
return
}
state.contacts.event = new_ev
state.pool.send(.event(new_ev))
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
}
func CopyAction(relay: String) -> some View {
Button {
UIPasteboard.general.setValue(relay, forPasteboardType: "public.plain-text")
} label: {
Label("Copy", systemImage: "doc.on.doc")
}
}
func RemoveAction(privkey: String) -> some View {
Button {
guard let ev = state.contacts.event else {
return
}
let descriptors = state.pool.descriptors
guard let new_ev = remove_relay( ev: ev, current_relays: descriptors, privkey: privkey, relay: relay) else {
return
}
process_contact_event(pool: state.pool, contacts: state.contacts, pubkey: state.pubkey, ev: new_ev)
state.pool.send(.event(new_ev))
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
struct RelayView_Previews: PreviewProvider {
static var previews: some View {
RelayView(state: test_damus_state(), relay: "wss://relay.damus.io", conn_color: .red)
RelayView(state: test_damus_state(), ev: NostrEvent(content: "content", pubkey: "pk"), relay: "wss://relay.damus.io", conn_color: .red)
}
}

View File

@@ -31,7 +31,7 @@ struct ReplyQuoteView: View {
.foregroundColor(.gray)
}
NoteContentView(privkey: privkey, event: event, profiles: profiles, show_images: false, artifacts: .just_content(event.content), size: .normal)
NoteContentView(privkey: privkey, event: event, profiles: profiles, show_images: false, artifacts: .just_content(event.content))
.font(.callout)
.foregroundColor(.accentColor)

View File

@@ -67,7 +67,7 @@ struct SaveKeysView: View {
complete_account_creation(account)
}
} else {
DamusWhiteButton(NSLocalizedString("Let's go!", comment: "Button to complete account creation and start using the app.")) {
DamusWhiteButton("Let's go!") {
complete_account_creation(account)
}
}
@@ -147,24 +147,20 @@ struct SaveKeyView: View {
var body: some View {
HStack {
Spacer()
VStack {
spacerBlock(width: 0, height: 0)
Button(action: copy_text) {
Label("", systemImage: is_copied ? "checkmark.circle.fill" : "doc.on.doc")
.foregroundColor(is_copied ? .green : .white)
.background {
if is_copied {
Circle()
.foregroundColor(.white)
.frame(width: 25, height: 25, alignment: .center)
.padding(.leading, -8)
.padding(.top, 1)
} else {
EmptyView()
}
Button(action: copy_text) {
Label("", systemImage: is_copied ? "checkmark.circle.fill" : "doc.on.doc")
.foregroundColor(is_copied ? .green : .white)
.background {
if is_copied {
Circle()
.foregroundColor(.white)
.frame(width: 25, height: 25, alignment: .center)
.padding(.leading, -8)
.padding(.top, 1)
} else {
EmptyView()
}
}
}
}
Text(text)
@@ -178,15 +174,8 @@ struct SaveKeyView: View {
.onTapGesture {
copy_text()
}
spacerBlock(width: 0, height: 0) /// set a 'width' > 0 here to vary key Text's aspect ratio
}
}
@ViewBuilder private func spacerBlock(width: CGFloat, height: CGFloat) -> some View {
Color.orange.opacity(1)
.frame(width: width, height: height)
}
}
struct SaveKeysView_Previews: PreviewProvider {

View File

@@ -55,7 +55,7 @@ struct SearchHomeView: View {
// Fetch new information by unsubscribing and resubscribing to the relay
model.unsubscribe()
model.subscribe()
}.padding(.horizontal)
}
}
var MainContent: some View {

View File

@@ -53,11 +53,8 @@ struct SearchResultsView: View {
let prof_model = ProfileModel(pubkey: h, damus: damus_state)
let f = FollowersModel(damus_state: damus_state, target: h)
let prof_view = ProfileView(damus_state: damus_state, profile: prof_model, followers: f)
let ev_view = BuildThreadV2View(
damus: damus_state,
event_id: h
)
let thread_model = ThreadModel(evid: h, damus_state: damus_state)
let ev_view = ThreadView(thread: thread_model, damus: damus_state, is_chatroom: false)
VStack(spacing: 50) {
NavigationLink(destination: prof_view) {
Text("Goto profile \(h)")
@@ -69,10 +66,8 @@ struct SearchResultsView: View {
case .note(let nid):
let decoded = try? bech32_decode(nid)
let hex = hex_encode(decoded!.data)
let ev_view = BuildThreadV2View(
damus: damus_state,
event_id: hex
)
let thread_model = ThreadModel(evid: hex, damus_state: damus_state)
let ev_view = ThreadView(thread: thread_model, damus: damus_state, is_chatroom: false)
NavigationLink(destination: ev_view) {
Text("Goto post \(nid)")
}

View File

@@ -1,78 +0,0 @@
//
// SelectWalletView.swift
// damus
//
// Created by Suhail Saqan on 12/22/22.
//
import SwiftUI
struct SelectWalletView: View {
@Binding var showingSelectWallet: Bool
let invoice: String
@Environment(\.openURL) private var openURL
@State var invoice_copied: Bool = false
@EnvironmentObject var user_settings: UserSettingsStore
@State var allWalletModels: [Wallet.Model] = Wallet.allModels
let generator = UIImpactFeedbackGenerator(style: .light)
var body: some View {
NavigationView {
Form {
Section("Copy invoice") {
HStack {
Text(invoice).font(.body)
.lineLimit(2)
.truncationMode(.tail)
Spacer()
Image(systemName: self.invoice_copied ? "checkmark.circle" : "doc.on.doc").foregroundColor(.blue)
}.clipShape(RoundedRectangle(cornerRadius: 5)).onTapGesture {
UIPasteboard.general.string = invoice
self.invoice_copied = true
generator.impactOccurred()
}
}
Section("Select a lightning wallet"){
List{
Button() {
let wallet_model = user_settings.default_wallet.model
open_with_wallet(wallet: wallet_model, invoice: invoice)
} label: {
HStack {
Text("Default Wallet").font(.body).foregroundColor(.blue)
}
}.buttonStyle(.plain)
List($allWalletModels) { $wallet in
if wallet.index >= 0 {
Button() {
open_with_wallet(wallet: wallet, invoice: invoice)
} label: {
HStack {
Image(wallet.image).resizable().frame(width: 32.0, height: 32.0,alignment: .center).cornerRadius(5)
Text(wallet.displayName).font(.body)
}
}.buttonStyle(.plain)
}
}
}.padding(.vertical, 2.5)
}
}.navigationBarTitle(Text("Pay the lightning invoice"), displayMode: .inline).navigationBarItems(trailing: Button(action: {
self.showingSelectWallet = false
}) {
Text("Done").bold()
})
}
}
}
struct SelectWalletView_Previews: PreviewProvider {
@State static var show: Bool = true
@State static var invoice: String = ""
static var previews: some View {
SelectWalletView(showingSelectWallet: $show, invoice: "")
}
}

View File

@@ -60,7 +60,7 @@ struct SetupView: View {
CarouselView()
DamusWhiteButton(NSLocalizedString("Create Account", comment: "Button to create an account.")) {
DamusWhiteButton("Create Account") {
self.state = .create_account
}

View File

@@ -1,325 +0,0 @@
//
// ThreadV2View.swift
// damus
//
// Created by Thomas Tastet on 25/12/2022.
//
import SwiftUI
struct ThreadV2 {
var parentEvents: [NostrEvent]
var current: NostrEvent
var childEvents: [NostrEvent]
mutating func clean() {
// remove duplicates
self.parentEvents = Array(Set(self.parentEvents))
self.childEvents = Array(Set(self.childEvents))
// remove empty contents
self.parentEvents = self.parentEvents.filter { event in
return !event.content.isEmpty
}
self.childEvents = self.childEvents.filter { event in
return !event.content.isEmpty
}
// sort events by publication date
self.parentEvents = self.parentEvents.sorted { event1, event2 in
return event1 < event2
}
self.childEvents = self.childEvents.sorted { event1, event2 in
return event1 < event2
}
}
}
struct BuildThreadV2View: View {
let damus: DamusState
@State var parents_ids: [String] = []
let event_id: String
@State var current_event: NostrEvent? = nil
@State var thread: ThreadV2? = nil
@State var current_events_uuid: String = ""
@State var childs_events_uuid: String = ""
@State var parents_events_uuids: [String] = []
@State var subscriptions_uuids: [String] = []
@Environment(\.dismiss) var dismiss
init(damus: DamusState, event_id: String) {
self.damus = damus
self.event_id = event_id
}
func unsubscribe_all() {
print("ThreadV2View: Unsubscribe all..")
for subscriptions in subscriptions_uuids {
unsubscribe(subscriptions)
}
}
func unsubscribe(_ sub_id: String) {
if subscriptions_uuids.contains(sub_id) {
damus.pool.unsubscribe(sub_id: sub_id)
subscriptions_uuids.remove(at: subscriptions_uuids.firstIndex(of: sub_id)!)
}
}
func subscribe(filters: [NostrFilter], sub_id: String = UUID().description) -> String {
damus.pool.register_handler(sub_id: sub_id, handler: handle_event)
damus.pool.send(.subscribe(.init(filters: filters, sub_id: sub_id)))
subscriptions_uuids.append(sub_id)
return sub_id
}
func handle_current_events(ev: NostrEvent) {
if current_event != nil {
return
}
current_event = ev
thread = ThreadV2(
parentEvents: [],
current: current_event!,
childEvents: []
)
// Get parents
parents_ids = current_event!.tags.enumerated().filter { (index, tag) in
return tag.count >= 2 && tag[0] == "e" && !current_event!.content.contains("#[\(index)]")
}.map { tag in
return tag.1[1]
}
print("ThreadV2View: Parents list: (\(parents_ids)")
if parents_ids.count > 0 {
// Ask for parents
let parents_events = NostrFilter(
ids: parents_ids,
limit: UInt32(parents_ids.count)
)
let uuid = subscribe(filters: [parents_events])
parents_events_uuids.append(uuid)
print("ThreadV2View: Ask for parents (\(uuid)) (\(parents_events))")
}
// Ask for children
let childs_events = NostrFilter(
kinds: [1],
referenced_ids: [self.event_id],
limit: 50
)
childs_events_uuid = subscribe(filters: [childs_events])
print("ThreadV2View: Ask for children (\(childs_events) (\(childs_events_uuid))")
}
func handle_parent_events(sub_id: String, nostr_event: NostrEvent) {
// We are filtering this later
thread!.parentEvents.append(nostr_event)
// Get parents of parents
let local_parents_ids = nostr_event.tags.enumerated().filter { (index, tag) in
return tag.count >= 2 && tag[0] == "e" && !nostr_event.content.contains("#[\(index)]")
}.map { tag in
return tag.1[1]
}.filter { tag_id in
return !parents_ids.contains(tag_id)
}
print("ThreadV2View: Sub Parents list: (\(local_parents_ids))")
// Expand new parents id
parents_ids.append(contentsOf: local_parents_ids)
if local_parents_ids.count > 0 {
// Ask for parents
let parents_events = NostrFilter(
ids: local_parents_ids,
limit: UInt32(local_parents_ids.count)
)
let uuid = subscribe(filters: [parents_events])
parents_events_uuids.append(uuid)
print("ThreadV2View: Ask for sub_parents (\(local_parents_ids)) \(uuid)")
}
thread!.clean()
unsubscribe(sub_id)
return
}
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
guard case .nostr_event(let nostr_response) = ev else {
return
}
guard case .event(let id, let nostr_event) = nostr_response else {
return
}
// Is current event
if id == current_events_uuid {
handle_current_events(ev: nostr_event)
return
}
if parents_events_uuids.contains(id) {
handle_parent_events(sub_id: id, nostr_event: nostr_event)
return
}
if id == childs_events_uuid {
// We are filtering this later
thread!.childEvents.append(nostr_event)
thread!.clean()
return
}
}
func reload() {
self.unsubscribe_all()
print("ThreadV2View: Reload!")
// Get the current event
current_events_uuid = subscribe(filters: [
NostrFilter(
ids: [self.event_id],
limit: 1
)
])
print("subscribing to threadV2 \(event_id) with sub_id \(current_events_uuid)")
}
var body: some View {
VStack {
if thread == nil {
ProgressView()
} else {
ThreadV2View(damus: damus, thread: thread!)
}
}
.onAppear {
if self.thread == nil {
self.reload()
}
}
.onDisappear {
self.unsubscribe_all()
}
.onReceive(handle_notify(.switched_timeline)) { n in
dismiss()
}
}
}
struct ThreadV2View: View {
let damus: DamusState
let thread: ThreadV2
var body: some View {
ScrollViewReader { reader in
ScrollView {
VStack {
// MARK: - Parents events view
VStack {
ForEach(thread.parentEvents, id: \.id) { event in
NavigationLink(destination: BuildThreadV2View(
damus: damus,
event_id: event.id
)){
EventView(
event: event,
highlight: .none,
has_action_bar: true,
damus: damus,
show_friend_icon: true, // TODO: change it
size: .small
)
}
.buttonStyle(.plain)
.onAppear {
// TODO: find another solution to prevent layout shifting and layout blocking on large responses
reader.scrollTo("main", anchor: .bottom)
}
}
}.background(GeometryReader { geometry in
// get the height and width of the EventView view
let eventHeight = geometry.frame(in: .global).height
// let eventWidth = geometry.frame(in: .global).width
// vertical gray line in the background
Rectangle()
.fill(Color.gray.opacity(0.25))
.frame(width: 2, height: eventHeight)
.offset(x: 25, y: 40)
})
// MARK: - Actual event view
EventView(
event: thread.current,
highlight: .none,
has_action_bar: true,
damus: damus,
show_friend_icon: true, // TODO: change it
size: .selected
).id("main")
// MARK: - Responses of the actual event view
ForEach(thread.childEvents, id: \.id) { event in
NavigationLink(destination: BuildThreadV2View(
damus: damus,
event_id: event.id
)){
EventView(
event: event,
highlight: .none,
has_action_bar: true,
damus: damus,
show_friend_icon: true, // TODO: change it
size: .small
)
}.buttonStyle(.plain)
}
}.padding()
}.navigationBarTitle("Thread")
}
}
}
struct ThreadV2View_Previews: PreviewProvider {
static var previews: some View {
BuildThreadV2View(damus: test_damus_state(), event_id: "ac9fd97b53b0c1d22b3aea2a3d62e11ae393960f5f91ee1791987d60151339a7")
ThreadV2View(
damus: test_damus_state(),
thread: ThreadV2(
parentEvents: [
NostrEvent(id: "1", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"),
NostrEvent(id: "2", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"),
NostrEvent(id: "3", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"),
],
current: NostrEvent(id: "4", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"),
childEvents: [
NostrEvent(id: "5", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"),
NostrEvent(id: "6", content: "hello there https://jb55.com/s/Oct12-150217.png https://jb55.com/red-me.jb55 cool 4", pubkey: "916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57"),
]
)
)
}
}

View File

@@ -24,14 +24,11 @@ struct InnerTimelineView: View {
EmptyTimelineView()
} else {
ForEach(events.filter(filter), id: \.id) { (ev: NostrEvent) in
//let tm = ThreadModel(event: inner_event_or_self(ev: ev), damus_state: damus)
//let is_chatroom = should_show_chatroom(ev)
//let tv = ThreadView(thread: tm, damus: damus, is_chatroom: is_chatroom)
let tm = ThreadModel(event: inner_event_or_self(ev: ev), damus_state: damus)
let is_chatroom = should_show_chatroom(ev)
let tv = ThreadView(thread: tm, damus: damus, is_chatroom: is_chatroom)
NavigationLink(destination: BuildThreadV2View(
damus: damus,
event_id: ev.id
)) {
NavigationLink(destination: tv) {
EventView(event: ev, highlight: .none, has_action_bar: true, damus: damus, show_friend_icon: show_friend_icon)
}
.isDetailLink(true)

View File

@@ -1,46 +0,0 @@
//
// UserRelaysView.swift
// damus
//
// Created by William Casarin on 2022-12-29.
//
import SwiftUI
struct UserRelaysView: View {
let state: DamusState
let pubkey: String
let relays: [String]
@State var relay_state: [(String, Bool)]
init (state: DamusState, pubkey: String, relays: [String]) {
self.state = state
self.pubkey = pubkey
self.relays = relays
let relay_state = UserRelaysView.make_relay_state(pool: state.pool, relays: relays)
self._relay_state = State(initialValue: relay_state)
}
static func make_relay_state(pool: RelayPool, relays: [String]) -> [(String, Bool)] {
return relays.map({ r in
return (r, pool.get_relay(r) == nil)
}).sorted { (a, b) in a.0 < b.0 }
}
var body: some View {
List(relay_state, id: \.0) { (r, add) in
RecommendedRelayView(damus: state, relay: r, add_button: add)
}
.onReceive(handle_notify(.relays_changed)) { _ in
self.relay_state = UserRelaysView.make_relay_state(pool: state.pool, relays: self.relays)
}
.navigationBarTitle("Relays")
}
}
struct UserRelaysView_Previews: PreviewProvider {
static var previews: some View {
UserRelaysView(state: test_damus_state(), pubkey: "", relays: [])
}
}

View File

@@ -1,4 +0,0 @@
/* Bundle display name */
"CFBundleDisplayName" = "Damus";
/* Bundle name */
"CFBundleName" = "damus";

Binary file not shown.

View File

@@ -1,42 +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>
</dict>
</plist>

View File

@@ -1,4 +0,0 @@
/* Bundle display name */
"CFBundleDisplayName" = "Damus";
/* Bundle name */
"CFBundleName" = "damus";

Binary file not shown.

View File

@@ -1,42 +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>
</dict>
</plist>

View File

@@ -1,4 +0,0 @@
/* Bundle display name */
"CFBundleDisplayName" = "Damus";
/* Bundle name */
"CFBundleName" = "damus";

Binary file not shown.

View File

@@ -1,42 +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>
</dict>
</plist>

View File

@@ -18,40 +18,6 @@ final class InvoiceTests: XCTestCase {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testParseAnyAmountInvoice() throws {
let invstr = "LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN4M4XU59XMJCXKR7YDV29DDP6LVQUT46ZW6CU3KE9GQDQ9V9H8JXQ8P3MYLZJCQPJRZJQF60PZDVNGGQWQDNERZSQN35L8CVQ3QG2Z5NSZYD0D3Q0JW2TL6VUZA7FYQQWKGQQYQQQQLGQQQQXJQQ9Q9QXPQYSGQ39EM4QJMQFKZGJXZVGL7QJMYNSWA8PGDTAGXXRG5Z92M7VLCGKQK2L2THDF8LM0AUKAURH7FVAWDLRNMVF38W4EYJDNVN9V4Z9CRS5CQCV465C"
let parsed = parse_mentions(content: invstr, tags: [])
XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 1)
XCTAssertNotNil(parsed[0].is_invoice)
guard let invoice = parsed[0].is_invoice else {
return
}
XCTAssertEqual(invoice.amount, .any)
//XCTAssertEqual(invoice.expiry, 604800)
//XCTAssertEqual(invoice.created_at, 1666139119)
XCTAssertEqual(invoice.string, invstr)
}
func testTextAfterInvoice() throws {
let invstr = """
LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN4M4XU59XMJCXKR7YDV29DDP6LVQUT46ZW6CU3KE9GQDQ9V9H8JXQ8P3MYLZJCQPJRZJQF60PZDVNGGQWQDNERZSQN35L8CVQ3QG2Z5NSZYD0D3Q0JW2TL6VUZA7FYQQWKGQQYQQQQLGQQQQXJQQ9Q9QXPQYSGQ39EM4QJMQFKZGJXZVGL7QJMYNSWA8PGDTAGXXRG5Z92M7VLCGKQK2L2THDF8LM0AUKAURH7FVAWDLRNMVF38W4EYJDNVN9V4Z9CRS5CQCV465C hi there
"""
let parsed = parse_mentions(content: invstr, tags: [])
XCTAssertNotNil(parsed)
XCTAssertEqual(parsed.count, 2)
XCTAssertNotNil(parsed[0].is_invoice)
XCTAssertEqual(parsed[1].is_text, " hi there")
guard let invoice = parsed[0].is_invoice else {
return
}
XCTAssertEqual(invoice.amount, .any)
//XCTAssertEqual(invoice.expiry, 604800)
//XCTAssertEqual(invoice.created_at, 1666139119)
}
func testParseInvoiceUpper() throws {
let invstr = "LNBC100N1P357SL0SP5T9N56WDZTUN39LGDQLR30XQWKSG3K69Q4Q2RKR52APLUJW0ESN0QPP5MRQGLJK62Z20Q4NVGR6LZCYN6FHYLZCCWDVU4K77APG3ZMRKUJJQDPZW35XJUEQD9EJQCFQV3JHXCMJD9C8G6T0DCXQYJW5QCQPJRZJQT56H4GVP5YX36U2UZQA6QWCSK3E2DUUNFXPPZJ9VHYPC3WFE2WSWZ607UQQ3XQQQSQQQQQQQQQQQLQQYG9QYYSGQAGX5H20AEULJ3GDWX3KXS8U9F4MCAKDKWUAKASAMM9562FFYR9EN8YG20LG0YGNR9ZPWP68524KMDA0T5XP2WYTEX35PU8HAPYJAJXQPSQL29R"
let parsed = parse_mentions(content: invstr, tags: [])
@@ -62,7 +28,7 @@ LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN
guard let invoice = parsed[0].is_invoice else {
return
}
XCTAssertEqual(invoice.amount, .specific(10000))
XCTAssertEqual(invoice.amount, 10000)
XCTAssertEqual(invoice.expiry, 604800)
XCTAssertEqual(invoice.created_at, 1666139119)
XCTAssertEqual(invoice.string, invstr)
@@ -96,7 +62,7 @@ LNBC1P3MR5UJSP5G7SA48YD4JWTTPCHWMY4QYN4UWZQCJQ8NMWKD6QE3HCRVYTDLH9SPP57YM9TSA9NN
guard let invoice = parsed[0].is_invoice else {
return
}
XCTAssertEqual(invoice.amount, .specific(10000))
XCTAssertEqual(invoice.amount, 10000)
XCTAssertEqual(invoice.expiry, 604800)
XCTAssertEqual(invoice.created_at, 1666139119)
XCTAssertEqual(invoice.string, invstr)

View File

@@ -1,43 +0,0 @@
//
// MarkdownTests.swift
// damusTests
//
// Created by Lionello Lunesu on 2022-12-28.
//
import XCTest
@testable import damus
class MarkdownTests: XCTestCase {
let md_opts: AttributedString.MarkdownParsingOptions =
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func test_convert_link() throws {
let helper = Markdown()
let md = helper.process("prologue https://nostr.build epilogue")
let expected = try AttributedString(markdown: "prologue [https://nostr.build](https://nostr.build) epilogue", options: md_opts)
XCTAssertEqual(md, expected)
}
func test_convert_link_no_scheme() throws {
let helper = Markdown()
let md = helper.process("prologue damus.io epilogue")
let expected = try AttributedString(markdown: "prologue [damus.io](https://damus.io) epilogue", options: md_opts)
XCTAssertEqual(md, expected)
}
func test_convert_links() throws {
let helper = Markdown()
let md = helper.process("prologue damus.io https://nostr.build epilogue")
let expected = try AttributedString(markdown: "prologue [damus.io](https://damus.io) [https://nostr.build](https://nostr.build) epilogue", options: md_opts)
XCTAssertEqual(md, expected)
}
}

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