Compare commits
26 Commits
tyiu/local
...
tyiu/photo
| Author | SHA1 | Date | |
|---|---|---|---|
|
a72f964111
|
|||
|
|
d658d1d987 | ||
|
|
e14cd99c85 | ||
|
|
0258ef792f | ||
|
|
1e6505abe3 | ||
|
|
98c24147e8 | ||
|
|
d1e7de5dcb | ||
|
|
11e0a87f06 | ||
|
|
1154cec719 | ||
|
|
1ecfb0487e | ||
|
|
8ffa8446b6 | ||
|
|
fb1f99e728 | ||
|
|
031408dec3 | ||
|
|
16b6d029fa | ||
|
|
3e093e8572 | ||
|
|
fa11af4b1d | ||
|
|
91159d70ca | ||
|
|
a57d654f32 | ||
|
|
0313480685 | ||
|
|
e07b31e0a1 | ||
|
538a0ae5ea
|
|||
|
0f82db2440
|
|||
|
|
92ae2c7754 | ||
|
|
aba758b143 | ||
|
|
b7c7b0b3bf | ||
|
|
2d10d4592b |
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,3 +1,37 @@
|
||||
## [1.0.0-7] - 2023-01-20
|
||||
|
||||
### Added
|
||||
|
||||
- Drastically improved image viewer (OlegAba)
|
||||
- Added pinch to zoom on images (Swift)
|
||||
- Add Latin American Spanish translations (William Casarin)
|
||||
- Added SVG profile picture support (OlegAba)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Makes both name and username clickable in sidebar to go to profile (Zach Hendel)
|
||||
- Clicking pfp in sidebar opens profile as well (radixrat)
|
||||
- Don't blur images if your friend boosted it (ericholguin)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix ... when too many likes/reposts (Joel Klabo)
|
||||
- Don't show report alert if logged in as a pubkey (Swift)
|
||||
- Fix padding issue at top of home timeline (Ben Weeks)
|
||||
- Fix absurdly large sidebar on Mac/iPad (John Bethancourt)
|
||||
- Fix tab views moving after selecting from search result (OlegAba)
|
||||
- Make follow/unfollow button a consistent width (OlegAba)
|
||||
- Don't add events to notifications from buggy relays (William Casarin)
|
||||
- Fixed some crashes with large images (OlegAba)
|
||||
- Fix DM sorting on incoming messages (William Casarin)
|
||||
- Fix text getting truncated next to link previews (William Casarin)
|
||||
|
||||
|
||||
[1.0.0-7]: https://github.com/damus-io/damus/releases/tag/v1.0.0-7
|
||||
|
||||
|
||||
## [1.0.0-6] - 2023-01-13
|
||||
|
||||
### Added
|
||||
@@ -384,4 +418,3 @@
|
||||
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
|
||||
<source>"Granting Damus access to your photo library allows you to save photos.</source>
|
||||
<target>"Granting Damus access to your photo library allows you to save photos.</target>
|
||||
<source>Granting Damus access to your photos allows you to save images.</source>
|
||||
<target>Granting Damus access to your photos allows you to save images.</target>
|
||||
<note>Privacy - Photo Library Additions Usage Description</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
@@ -285,6 +285,16 @@ Number of profiles a user is following.</note>
|
||||
<target>DM</target>
|
||||
<note>Navigation title for DM view, which is the English abbreviation for Direct Message.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="DM Type" xml:space="preserve">
|
||||
<source>DM Type</source>
|
||||
<target>DM Type</target>
|
||||
<note>DM selector for seeing either DMs or message requests, which are messages that have not been responded to yet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="DMs" xml:space="preserve">
|
||||
<source>DMs</source>
|
||||
<target>DMs</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Damus" xml:space="preserve">
|
||||
<source>Damus</source>
|
||||
<target>Damus</target>
|
||||
@@ -507,11 +517,6 @@ Part of a larger sentence to describe how many profiles a user is following.</no
|
||||
<trans-unit id="Private Key" xml:space="preserve">
|
||||
<source>Private Key</source>
|
||||
<target>Private Key</target>
|
||||
<note>Label to indicate that the text below is the user's private key used by only the user themself as a secret to login to access their account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PrivateKey" xml:space="preserve">
|
||||
<source>PrivateKey</source>
|
||||
<target>PrivateKey</target>
|
||||
<note>Title of the secure field that holds the user's private key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile" xml:space="preserve">
|
||||
@@ -590,6 +595,11 @@ Part of a larger sentence to describe how many profiles a user is following.</no
|
||||
<target>Reposted</target>
|
||||
<note>Text indicating that the post was reposted (i.e. re-shared).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Requests" xml:space="preserve">
|
||||
<source>Requests</source>
|
||||
<target>Requests</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reset" xml:space="preserve">
|
||||
<source>Reset</source>
|
||||
<target>Reset</target>
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
"NSPhotoLibraryAddUsageDescription" = "Granting Damus access to your photos allows you to save images.";
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,154 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>collapsed_event_view_other_notes</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>··· %#@NOTES@ ···</string>
|
||||
<key>NOTES</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>%d other note</string>
|
||||
<key>other</key>
|
||||
<string>%d other notes</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>followers_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@FOLLOWERS@</string>
|
||||
<key>FOLLOWERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Follower</string>
|
||||
<key>other</key>
|
||||
<string>Followers</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>reactions_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@REACTIONS@</string>
|
||||
<key>REACTIONS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Reaction</string>
|
||||
<key>other</key>
|
||||
<string>Reactions</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>relays_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@RELAYS@</string>
|
||||
<key>RELAYS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Relay</string>
|
||||
<key>other</key>
|
||||
<string>Relays</string>
|
||||
</dict>
|
||||
</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> & %d other</string>
|
||||
<key>other</key>
|
||||
<string> & %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> & %d other</string>
|
||||
<key>other</key>
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>reposts_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@REPOSTS@</string>
|
||||
<key>REPOSTS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Repost</string>
|
||||
<key>other</key>
|
||||
<string>Reposts</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>sats_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%1$#@SATS@</string>
|
||||
<key>SATS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>@</string>
|
||||
<key>one</key>
|
||||
<string>%2$@ sat</string>
|
||||
<key>other</key>
|
||||
<string>%2$@ sats</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>tips_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@TIPS@</string>
|
||||
<key>TIPS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Tip</string>
|
||||
<key>other</key>
|
||||
<string>Tips</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"developmentRegion" : "en-US",
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "es-419",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "14C18",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "14.2"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@
|
||||
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 */; };
|
||||
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; };
|
||||
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* 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 */; };
|
||||
@@ -145,6 +147,7 @@
|
||||
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 */; };
|
||||
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; };
|
||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
|
||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
||||
@@ -157,6 +160,7 @@
|
||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
||||
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -183,6 +187,8 @@
|
||||
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
|
||||
3A2B8B0A296A8982009CC16D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "en-US"; path = "en-US.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3ACB685B297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
3ACB685E297633BC00C46468 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -351,6 +357,7 @@
|
||||
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>"; };
|
||||
6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; };
|
||||
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
|
||||
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
7C45AE70297353390031D7BC /* KFImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFImageModel.swift; sourceTree = "<group>"; };
|
||||
@@ -360,6 +367,7 @@
|
||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
||||
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeToDismiss.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -510,8 +518,8 @@
|
||||
4C216F33286F5ACD00040376 /* DMView.swift */,
|
||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
||||
3169CAE4294E699400EE4006 /* Empty Views */,
|
||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||
4C75EFB82804A2740006080F /* EventView.swift */,
|
||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
||||
@@ -543,6 +551,7 @@
|
||||
647D9A8C2968520300A295DE /* SideMenuView.swift */,
|
||||
9609F057296E220800069BF3 /* BannerImageView.swift */,
|
||||
4CB8838E296F781C00DC99E7 /* ReactionsView.swift */,
|
||||
6439E013296790CF0020672B /* ProfileZoomView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -645,12 +654,15 @@
|
||||
4CE6DEE527F7A08100C66700 /* damus */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F7F0BA23297892AE009531F3 /* Modifiers */,
|
||||
4C4A3A5A288A1B2200453788 /* damus.entitlements */,
|
||||
4CE4F9DF285287A000C00DD9 /* Components */,
|
||||
4C7FF7D628233637009601DB /* Util */,
|
||||
4C0A3F8D280F63FF000448DE /* Models */,
|
||||
4C75EFAB28049CC80006080F /* Nostr */,
|
||||
4C75EFA72804823E0006080F /* Info.plist */,
|
||||
3ACB685D297633BC00C46468 /* Localizable.strings */,
|
||||
3ACB685A297633BC00C46468 /* InfoPlist.strings */,
|
||||
4C75EFA227FA576C0006080F /* Views */,
|
||||
4CE6DEE627F7A08100C66700 /* damusApp.swift */,
|
||||
4CE6DEE827F7A08100C66700 /* ContentView.swift */,
|
||||
@@ -701,6 +713,14 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F7F0BA23297892AE009531F3 /* Modifiers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F7F0BA24297892BD009531F3 /* SwipeToDismiss.swift */,
|
||||
);
|
||||
path = Modifiers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -822,7 +842,9 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */,
|
||||
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */,
|
||||
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */,
|
||||
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */,
|
||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */,
|
||||
);
|
||||
@@ -884,6 +906,7 @@
|
||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
|
||||
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */,
|
||||
F7F0BA25297892BD009531F3 /* SwipeToDismiss.swift in Sources */,
|
||||
4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */,
|
||||
4C649844285A952100EAE2B3 /* LocalUserConfig.swift in Sources */,
|
||||
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
||||
@@ -941,6 +964,7 @@
|
||||
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
|
||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */,
|
||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */,
|
||||
@@ -1032,6 +1056,22 @@
|
||||
name = Localizable.stringsdict;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3ACB685A297633BC00C46468 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3ACB685B297633BC00C46468 /* es-419 */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3ACB685D297633BC00C46468 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3ACB685E297633BC00C46468 /* es-419 */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -1163,7 +1203,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1171,7 +1211,7 @@
|
||||
INFOPLIST_FILE = damus/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Damus;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
@@ -1204,7 +1244,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
CURRENT_PROJECT_VERSION = 7;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1212,7 +1252,7 @@
|
||||
INFOPLIST_FILE = damus/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Damus;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "Granting Damus access to your photos allows you to save images.";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
|
||||
@@ -12,14 +12,14 @@ import Kingfisher
|
||||
struct ShareSheet: UIViewControllerRepresentable {
|
||||
typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
|
||||
|
||||
let activityItems: [URL]
|
||||
let activityItems: [URL?]
|
||||
let callback: Callback? = nil
|
||||
let applicationActivities: [UIActivity]? = nil
|
||||
let excludedActivityTypes: [UIActivity.ActivityType]? = nil
|
||||
|
||||
func makeUIViewController(context: Context) -> UIActivityViewController {
|
||||
let controller = UIActivityViewController(
|
||||
activityItems: activityItems,
|
||||
activityItems: activityItems as [Any],
|
||||
applicationActivities: applicationActivities)
|
||||
controller.excludedActivityTypes = excludedActivityTypes
|
||||
controller.completionWithItemsHandler = callback
|
||||
@@ -32,7 +32,7 @@ struct ShareSheet: UIViewControllerRepresentable {
|
||||
}
|
||||
|
||||
struct ImageContextMenuModifier: ViewModifier {
|
||||
let url: URL
|
||||
let url: URL?
|
||||
let image: UIImage?
|
||||
@Binding var showShareSheet: Bool
|
||||
|
||||
@@ -64,8 +64,21 @@ struct ImageContextMenuModifier: ViewModifier {
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageViewer: View {
|
||||
let urls: [URL]
|
||||
private struct ImageContainerView: View {
|
||||
|
||||
@ObservedObject var imageModel: KFImageModel
|
||||
|
||||
@State private var image: UIImage?
|
||||
@State private var showShareSheet = false
|
||||
|
||||
init(url: URL?) {
|
||||
self.imageModel = KFImageModel(
|
||||
url: url,
|
||||
fallbackUrl: nil,
|
||||
maxByteSize: 2000000, // 2 MB
|
||||
downsampleSize: CGSize(width: 400, height: 400)
|
||||
)
|
||||
}
|
||||
|
||||
private struct ImageHandler: ImageModifier {
|
||||
@Binding var handler: UIImage?
|
||||
@@ -75,45 +88,178 @@ struct ImageViewer: View {
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
@State private var image: UIImage?
|
||||
@State private var showShareSheet = false
|
||||
|
||||
func onShared(completed: Bool) -> Void {
|
||||
if (completed) {
|
||||
showShareSheet = false
|
||||
var body: some View {
|
||||
|
||||
KFAnimatedImage(imageModel.url)
|
||||
.callbackQueue(.dispatch(.global(qos: .background)))
|
||||
.processingQueue(.dispatch(.global(qos: .background)))
|
||||
.cacheOriginalImage()
|
||||
.configure { view in
|
||||
view.framePreloadCount = 1
|
||||
}
|
||||
.scaleFactor(UIScreen.main.scale)
|
||||
.loadDiskFileSynchronously()
|
||||
.fade(duration: 0.1)
|
||||
.imageModifier(ImageHandler(handler: $image))
|
||||
.onFailure { _ in
|
||||
imageModel.downloadFailed()
|
||||
}
|
||||
.id(imageModel.refreshID)
|
||||
.clipped()
|
||||
.modifier(ImageContextMenuModifier(url: imageModel.url, image: image, showShareSheet: $showShareSheet))
|
||||
.sheet(isPresented: $showShareSheet) {
|
||||
ShareSheet(activityItems: [imageModel.url])
|
||||
}
|
||||
|
||||
// TODO: Update ImageCarousel with serializer and processor
|
||||
// .serialize(by: imageModel.serializer)
|
||||
// .setProcessor(imageModel.processor)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct ZoomableScrollView<Content: View>: UIViewRepresentable {
|
||||
|
||||
private var content: Content
|
||||
|
||||
init(@ViewBuilder content: () -> Content) {
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
func makeUIView(context: Context) -> UIScrollView {
|
||||
let scrollView = UIScrollView()
|
||||
scrollView.delegate = context.coordinator
|
||||
scrollView.maximumZoomScale = 20
|
||||
scrollView.minimumZoomScale = 1
|
||||
scrollView.bouncesZoom = true
|
||||
scrollView.showsVerticalScrollIndicator = false
|
||||
scrollView.showsHorizontalScrollIndicator = false
|
||||
|
||||
let hostedView = context.coordinator.hostingController.view!
|
||||
hostedView.translatesAutoresizingMaskIntoConstraints = true
|
||||
hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
hostedView.frame = scrollView.bounds
|
||||
hostedView.backgroundColor = .clear
|
||||
scrollView.addSubview(hostedView)
|
||||
|
||||
return scrollView
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(hostingController: UIHostingController(rootView: self.content))
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIScrollView, context: Context) {
|
||||
context.coordinator.hostingController.rootView = self.content
|
||||
assert(context.coordinator.hostingController.view.superview == uiView)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, UIScrollViewDelegate {
|
||||
var hostingController: UIHostingController<Content>
|
||||
|
||||
init(hostingController: UIHostingController<Content>) {
|
||||
self.hostingController = hostingController
|
||||
}
|
||||
|
||||
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
|
||||
return hostingController.view
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageView: View {
|
||||
|
||||
let urls: [URL?]
|
||||
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
@State private var selectedIndex = 0
|
||||
@State var showMenu = true
|
||||
|
||||
var navBarView: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
Text(urls[selectedIndex]?.lastPathComponent ?? "")
|
||||
.bold()
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}, label: {
|
||||
Image(systemName: "xmark")
|
||||
})
|
||||
}
|
||||
.padding()
|
||||
|
||||
Divider()
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
.background(.regularMaterial)
|
||||
}
|
||||
|
||||
var tabViewIndicator: some View {
|
||||
HStack(spacing: 10) {
|
||||
ForEach(urls.indices, id: \.self) { index in
|
||||
Capsule()
|
||||
.fill(index == selectedIndex ? Color(UIColor.label) : Color.secondary)
|
||||
.frame(width: 7, height: 7)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(.regularMaterial)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
ForEach(urls, id: \.absoluteString) { url in
|
||||
VStack{
|
||||
Text(url.lastPathComponent)
|
||||
|
||||
KFAnimatedImage(url)
|
||||
.configure { view in
|
||||
view.framePreloadCount = 3
|
||||
}
|
||||
.cacheOriginalImage()
|
||||
.imageModifier(ImageHandler(handler: $image))
|
||||
.loadDiskFileSynchronously()
|
||||
.scaleFactor(UIScreen.main.scale)
|
||||
.fade(duration: 0.1)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.tabItem {
|
||||
Text(url.absoluteString)
|
||||
}
|
||||
.id(url.absoluteString)
|
||||
.modifier(ImageContextMenuModifier(url: url, image: image, showShareSheet: $showShareSheet))
|
||||
.sheet(isPresented: $showShareSheet) {
|
||||
ShareSheet(activityItems: [url])
|
||||
}
|
||||
|
||||
ZStack {
|
||||
Color(.systemBackground)
|
||||
.ignoresSafeArea()
|
||||
|
||||
TabView(selection: $selectedIndex) {
|
||||
ForEach(urls.indices, id: \.self) { index in
|
||||
ZoomableScrollView {
|
||||
ImageContainerView(url: urls[index])
|
||||
.aspectRatio(contentMode: .fit)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
.tag(index)
|
||||
.modifier(SwipeToDismissModifier(minDistance: 50, onDismiss: {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}))
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||
.onChange(of: selectedIndex, perform: { _ in
|
||||
showMenu = true
|
||||
})
|
||||
.onTapGesture {
|
||||
showMenu.toggle()
|
||||
}
|
||||
.overlay(
|
||||
VStack {
|
||||
if showMenu {
|
||||
navBarView
|
||||
Spacer()
|
||||
|
||||
if (urls.count > 1) {
|
||||
tabViewIndicator
|
||||
}
|
||||
}
|
||||
}
|
||||
.animation(.easeInOut, value: showMenu)
|
||||
.padding(
|
||||
.bottom,
|
||||
UIApplication
|
||||
.shared
|
||||
.connectedScenes
|
||||
.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
|
||||
.first { $0.isKeyWindow }?.safeAreaInsets.bottom
|
||||
)
|
||||
)
|
||||
}
|
||||
.tabViewStyle(PageTabViewStyle())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +276,15 @@ struct ImageCarousel: View {
|
||||
.foregroundColor(Color.clear)
|
||||
.overlay {
|
||||
KFAnimatedImage(url)
|
||||
.configure { view in
|
||||
view.framePreloadCount = 3
|
||||
}
|
||||
.callbackQueue(.dispatch(.global(qos: .background)))
|
||||
.processingQueue(.dispatch(.global(qos: .background)))
|
||||
.cacheOriginalImage()
|
||||
.loadDiskFileSynchronously()
|
||||
.scaleFactor(UIScreen.main.scale)
|
||||
.fade(duration: 0.1)
|
||||
.configure { view in
|
||||
view.framePreloadCount = 3
|
||||
}
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.tabItem {
|
||||
Text(url.absoluteString)
|
||||
@@ -151,8 +299,8 @@ struct ImageCarousel: View {
|
||||
}
|
||||
}
|
||||
.cornerRadius(10)
|
||||
.sheet(isPresented: $open_sheet) {
|
||||
ImageViewer(urls: urls)
|
||||
.fullScreenCover(isPresented: $open_sheet) {
|
||||
ImageView(urls: urls)
|
||||
}
|
||||
.frame(height: 200)
|
||||
.onTapGesture {
|
||||
@@ -164,6 +312,6 @@ struct ImageCarousel: View {
|
||||
|
||||
struct ImageCarousel_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ImageCarousel(urls: [URL(string: "https://jb55.com/red-me.jpg")!])
|
||||
ImageCarousel(urls: [URL(string: "https://jb55.com/red-me.jpg")!,URL(string: "https://jb55.com/red-me.jpg")!])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ struct ContentView: View {
|
||||
}
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
}
|
||||
.safeAreaInset(edge: .top) {
|
||||
.safeAreaInset(edge: .top, spacing: 0) {
|
||||
VStack(spacing: 0) {
|
||||
FiltersView
|
||||
//.frame(maxWidth: 275)
|
||||
@@ -113,7 +113,6 @@ struct ContentView: View {
|
||||
}
|
||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||
}
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
|
||||
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
||||
@@ -180,16 +179,19 @@ struct ContentView: View {
|
||||
.shadow(color: Color("DamusPurple"), radius: 2)
|
||||
case .dms:
|
||||
Text("DM", comment: "Toolbar label for DM view, which is the English abbreviation for Direct Message.")
|
||||
.bold()
|
||||
case .notifications:
|
||||
Text("Notifications", comment: "Toolbar label for Notifications view.")
|
||||
.bold()
|
||||
case .search:
|
||||
Text("Global", comment: "Toolbar label for Global view where posts from all connected relay servers appear.")
|
||||
.bold()
|
||||
case .none:
|
||||
Text("", comment: "Toolbar label for unknown views. This label would be displayed only if a new timeline view is added but a toolbar label was not explicitly assigned to it yet.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
|
||||
var MaybeSearchView: some View {
|
||||
|
||||
@@ -22,8 +22,13 @@ struct DamusState {
|
||||
var pubkey: String {
|
||||
return keypair.pubkey
|
||||
}
|
||||
|
||||
var is_privkey_user: Bool {
|
||||
keypair.privkey != nil
|
||||
}
|
||||
|
||||
|
||||
static var empty: DamusState {
|
||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(), previews: PreviewCache())
|
||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,34 @@
|
||||
import Foundation
|
||||
|
||||
class DirectMessageModel: ObservableObject {
|
||||
@Published var events: [NostrEvent]
|
||||
|
||||
init(events: [NostrEvent]) {
|
||||
self.events = events
|
||||
@Published var events: [NostrEvent] {
|
||||
didSet {
|
||||
is_request = determine_is_request()
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
var is_request: Bool
|
||||
var our_pubkey: String
|
||||
|
||||
func determine_is_request() -> Bool {
|
||||
for event in events {
|
||||
if event.pubkey == our_pubkey {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
init(events: [NostrEvent], our_pubkey: String) {
|
||||
self.events = events
|
||||
self.is_request = false
|
||||
self.our_pubkey = our_pubkey
|
||||
}
|
||||
|
||||
init(our_pubkey: String) {
|
||||
self.events = []
|
||||
self.is_request = false
|
||||
self.our_pubkey = our_pubkey
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,26 @@ import Foundation
|
||||
class DirectMessagesModel: ObservableObject {
|
||||
@Published var dms: [(String, DirectMessageModel)] = []
|
||||
@Published var loading: Bool = false
|
||||
let our_pubkey: String
|
||||
|
||||
init(our_pubkey: String) {
|
||||
self.our_pubkey = our_pubkey
|
||||
}
|
||||
|
||||
var message_requests: [(String, DirectMessageModel)] {
|
||||
return dms.filter { dm in dm.1.is_request }
|
||||
}
|
||||
|
||||
var friend_dms: [(String, DirectMessageModel)] {
|
||||
return dms.filter { dm in !dm.1.is_request }
|
||||
}
|
||||
|
||||
func lookup_or_create(_ pubkey: String) -> DirectMessageModel {
|
||||
if let dm = lookup(pubkey) {
|
||||
return dm
|
||||
}
|
||||
|
||||
let new = DirectMessageModel()
|
||||
let new = DirectMessageModel(our_pubkey: our_pubkey)
|
||||
dms.append((pubkey, new))
|
||||
return new
|
||||
}
|
||||
|
||||
@@ -48,17 +48,19 @@ class HomeModel: ObservableObject {
|
||||
|
||||
@Published var new_events: NewEventsBits = NewEventsBits()
|
||||
@Published var notifications: [NostrEvent] = []
|
||||
@Published var dms: DirectMessagesModel = DirectMessagesModel()
|
||||
@Published var dms: DirectMessagesModel
|
||||
@Published var events: [NostrEvent] = []
|
||||
@Published var loading: Bool = false
|
||||
@Published var signal: SignalModel = SignalModel()
|
||||
|
||||
init() {
|
||||
self.damus_state = DamusState.empty
|
||||
self.dms = DirectMessagesModel(our_pubkey: damus_state.pubkey)
|
||||
}
|
||||
|
||||
init(damus_state: DamusState) {
|
||||
self.damus_state = damus_state
|
||||
self.dms = DirectMessagesModel(our_pubkey: damus_state.pubkey)
|
||||
}
|
||||
|
||||
var pool: RelayPool {
|
||||
@@ -644,7 +646,7 @@ func handle_incoming_dm(prev_events: NewEventsBits, dms: DirectMessagesModel, ou
|
||||
|
||||
if !found {
|
||||
inserted = true
|
||||
let model = DirectMessageModel(events: [ev])
|
||||
let model = DirectMessageModel(events: [ev], our_pubkey: our_pubkey)
|
||||
dms.dms.append((the_pk, model))
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ enum Wallet: String, CaseIterable, Identifiable {
|
||||
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")
|
||||
appStoreLink: "https://testflight.apple.com/join/EXvGhRzS", 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")
|
||||
|
||||
35
damus/Modifiers/SwipeToDismiss.swift
Normal file
35
damus/Modifiers/SwipeToDismiss.swift
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// SwipeToDismiss.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Joel Klabo on 1/18/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SwipeToDismissModifier: ViewModifier {
|
||||
let minDistance: CGFloat?
|
||||
var onDismiss: () -> Void
|
||||
@State private var offset: CGSize = .zero
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.offset(y: offset.height)
|
||||
.animation(.interactiveSpring(), value: offset)
|
||||
.simultaneousGesture(
|
||||
DragGesture(minimumDistance: minDistance ?? 10)
|
||||
.onChanged { gesture in
|
||||
if gesture.translation.width < 50 {
|
||||
offset = gesture.translation
|
||||
}
|
||||
}
|
||||
.onEnded { _ in
|
||||
if abs(offset.height) > 100 {
|
||||
onDismiss()
|
||||
} else {
|
||||
offset = .zero
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -789,3 +789,29 @@ func inner_event_or_self(ev: NostrEvent) -> NostrEvent {
|
||||
|
||||
return inner_ev
|
||||
}
|
||||
|
||||
|
||||
func first_eref_mention(ev: NostrEvent, privkey: String?) -> Mention? {
|
||||
let blocks = ev.blocks(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" {
|
||||
return mention
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,62 +33,41 @@ struct EventActionBar: View {
|
||||
EventActionButton(img: "bubble.left", col: nil) {
|
||||
notify(.reply, event)
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
HStack(alignment: .bottom) {
|
||||
Spacer()
|
||||
ZStack {
|
||||
|
||||
EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
|
||||
if bar.boosted {
|
||||
notify(.delete, bar.our_boost)
|
||||
} else {
|
||||
} else if damus_state.is_privkey_user {
|
||||
self.confirm_boost = true
|
||||
}
|
||||
}.overlay {
|
||||
Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
|
||||
.offset(x: 22)
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(bar.boosted ? Color.green : Color.gray)
|
||||
}
|
||||
Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
|
||||
.offset(x: 18)
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(bar.boosted ? Color.green : Color.gray)
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
HStack(alignment: .bottom) {
|
||||
Spacer()
|
||||
ZStack {
|
||||
LikeButton(liked: bar.liked) {
|
||||
if bar.liked {
|
||||
notify(.delete, bar.our_like)
|
||||
} else {
|
||||
send_like()
|
||||
}
|
||||
}.overlay {
|
||||
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
|
||||
.offset(x: 22)
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(bar.liked ? Color.accentColor : Color.gray)
|
||||
}
|
||||
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
|
||||
.offset(x: 22)
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(bar.liked ? Color.accentColor : Color.gray)
|
||||
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Spacer()
|
||||
EventActionButton(img: "square.and.arrow.up", col: Color.gray) {
|
||||
show_share_sheet = true
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
/*
|
||||
HStack(alignment: .bottom) {
|
||||
Text("\(bar.tips > 0 ? "\(bar.tips)" : "")")
|
||||
.font(.footnote)
|
||||
.foregroundColor(bar.tipped ? Color.orange : Color.gray)
|
||||
|
||||
EventActionButton(img: bar.tipped ? "bitcoinsign.circle.fill" : "bitcoinsign.circle", col: bar.tipped ? Color.orange : nil) {
|
||||
if bar.tipped {
|
||||
//notify(.delete, bar.our_tip)
|
||||
} else {
|
||||
//notify(.boost, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
.sheet(isPresented: $show_share_sheet) {
|
||||
if let note_id = bech32_note_id(event.id) {
|
||||
@@ -177,8 +156,9 @@ struct EventActionBar_Previews: PreviewProvider {
|
||||
let ev = NostrEvent(content: "hi", pubkey: pk)
|
||||
|
||||
let bar = ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
|
||||
let likedbar = ActionBarModel(likes: 10, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
|
||||
let likedbar_ours = ActionBarModel(likes: 10, boosts: 0, tips: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: nil, our_tip: nil)
|
||||
let likedbar = ActionBarModel(likes: 10, boosts: 10, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
|
||||
let likedbar_ours = ActionBarModel(likes: 100, boosts: 100, tips: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: nil, our_tip: nil)
|
||||
let maxed_bar = ActionBarModel(likes: 999, boosts: 999, tips: 0, our_like: NostrEvent(id: "", content: "", pubkey: ""), our_boost: NostrEvent(id: "", content: "", pubkey: ""), our_tip: nil)
|
||||
|
||||
VStack(spacing: 50) {
|
||||
EventActionBar(damus_state: ds, event: ev, bar: bar)
|
||||
@@ -186,6 +166,8 @@ struct EventActionBar_Previews: PreviewProvider {
|
||||
EventActionBar(damus_state: ds, event: ev, bar: likedbar)
|
||||
|
||||
EventActionBar(damus_state: ds, event: ev, bar: likedbar_ours)
|
||||
|
||||
EventActionBar(damus_state: ds, event: ev, bar: maxed_bar)
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ struct ConfigView: View {
|
||||
Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) {
|
||||
HStack {
|
||||
if show_privkey == false {
|
||||
SecureField(NSLocalizedString("PrivateKey", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
|
||||
SecureField(NSLocalizedString("Private Key", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
|
||||
.disabled(true)
|
||||
} else {
|
||||
Text(sec)
|
||||
|
||||
@@ -158,7 +158,7 @@ struct DMChatView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let ev = NostrEvent(content: "hi", pubkey: "pubkey", kind: 1, tags: [])
|
||||
|
||||
let model = DirectMessageModel(events: [ev])
|
||||
let model = DirectMessageModel(events: [ev], our_pubkey: "pubkey")
|
||||
|
||||
DMChatView(damus_state: test_damus_state(), pubkey: "pubkey")
|
||||
.environmentObject(model)
|
||||
|
||||
@@ -7,15 +7,26 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum DMType: Hashable {
|
||||
case rando
|
||||
case friend
|
||||
}
|
||||
|
||||
struct DirectMessagesView: View {
|
||||
let damus_state: DamusState
|
||||
|
||||
@State var dm_type: DMType = .friend
|
||||
@State var open_dm: Bool = false
|
||||
@State var pubkey: String = ""
|
||||
@State var active_model: DirectMessageModel = DirectMessageModel()
|
||||
@EnvironmentObject var model: DirectMessagesModel
|
||||
@State var active_model: DirectMessageModel
|
||||
|
||||
var MainContent: some View {
|
||||
init(damus_state: DamusState) {
|
||||
self.damus_state = damus_state
|
||||
self._active_model = State(initialValue: DirectMessageModel(our_pubkey: damus_state.pubkey))
|
||||
}
|
||||
|
||||
func MainContent(requests: Bool) -> some View {
|
||||
ScrollView {
|
||||
let chat = DMChatView(damus_state: damus_state, pubkey: pubkey)
|
||||
.environmentObject(active_model)
|
||||
@@ -26,13 +37,12 @@ struct DirectMessagesView: View {
|
||||
if model.dms.isEmpty, !model.loading {
|
||||
EmptyTimelineView()
|
||||
} else {
|
||||
ForEach(model.dms, id: \.0) { tup in
|
||||
let dms = requests ? model.message_requests : model.friend_dms
|
||||
ForEach(dms, id: \.0) { tup in
|
||||
MaybeEvent(tup)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.top)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +62,29 @@ struct DirectMessagesView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
MainContent
|
||||
.navigationTitle(NSLocalizedString("Encrypted DMs", comment: "Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message."))
|
||||
VStack {
|
||||
Picker(NSLocalizedString("DM Type", comment: "DM selector for seeing either DMs or message requests, which are messages that have not been responded to yet."), selection: $dm_type) {
|
||||
Text("DMs")
|
||||
.tag(DMType.friend)
|
||||
|
||||
Text("Requests")
|
||||
.tag(DMType.rando)
|
||||
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
TabView(selection: $dm_type) {
|
||||
MainContent(requests: false)
|
||||
.tag(DMType.friend)
|
||||
|
||||
MainContent(requests: true)
|
||||
.tag(DMType.rando)
|
||||
}
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.top)
|
||||
.navigationTitle(NSLocalizedString("Encrypted DMs", comment: "Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message."))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +94,9 @@ struct DirectMessagesView_Previews: PreviewProvider {
|
||||
pubkey: "pubkey",
|
||||
kind: 4,
|
||||
tags: [])
|
||||
let model = DirectMessageModel(events: [ev])
|
||||
DirectMessagesView(damus_state: test_damus_state())
|
||||
let ds = test_damus_state()
|
||||
let model = DirectMessageModel(events: [ev], our_pubkey: ds.pubkey)
|
||||
DirectMessagesView(damus_state: ds)
|
||||
.environmentObject(model)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ struct BuilderEventView: View {
|
||||
if let event = event {
|
||||
let ev = event.inner_event ?? event
|
||||
NavigationLink(destination: BuildThreadV2View(damus: damus, event_id: ev.id)) {
|
||||
EventView(damus: damus, event: event, show_friend_icon: true, size: .small)
|
||||
EventView(damus: damus, event: event, show_friend_icon: true, size: .small, embedded: true)
|
||||
}.buttonStyle(.plain)
|
||||
} else {
|
||||
ProgressView().padding()
|
||||
@@ -129,10 +129,11 @@ struct EventView: View {
|
||||
let pubkey: String
|
||||
let show_friend_icon: Bool
|
||||
let size: EventViewKind
|
||||
let embedded: Bool
|
||||
|
||||
@EnvironmentObject var action_bar: ActionBarModel
|
||||
|
||||
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool, size: EventViewKind = .normal) {
|
||||
init(event: NostrEvent, highlight: Highlight, has_action_bar: Bool, damus: DamusState, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
|
||||
self.event = event
|
||||
self.highlight = highlight
|
||||
self.has_action_bar = has_action_bar
|
||||
@@ -140,9 +141,10 @@ struct EventView: View {
|
||||
self.pubkey = event.pubkey
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.size = size
|
||||
self.embedded = embedded
|
||||
}
|
||||
|
||||
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool, size: EventViewKind = .normal) {
|
||||
init(damus: DamusState, event: NostrEvent, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
|
||||
self.event = event
|
||||
self.highlight = .none
|
||||
self.has_action_bar = false
|
||||
@@ -150,6 +152,7 @@ struct EventView: View {
|
||||
self.pubkey = event.pubkey
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.size = size
|
||||
self.embedded = embedded
|
||||
}
|
||||
|
||||
init(damus: DamusState, event: NostrEvent, pubkey: String, show_friend_icon: Bool, size: EventViewKind = .normal, embedded: Bool = false) {
|
||||
@@ -160,6 +163,7 @@ struct EventView: View {
|
||||
self.pubkey = pubkey
|
||||
self.show_friend_icon = show_friend_icon
|
||||
self.size = size
|
||||
self.embedded = embedded
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -196,8 +200,10 @@ struct EventView: View {
|
||||
let pmodel = ProfileModel(pubkey: pubkey, damus: damus)
|
||||
let pv = ProfileView(damus_state: damus, profile: pmodel, followers: FollowersModel(damus_state: damus, target: pubkey))
|
||||
|
||||
NavigationLink(destination: pv) {
|
||||
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
|
||||
if !embedded {
|
||||
NavigationLink(destination: pv) {
|
||||
ProfilePicView(pubkey: pubkey, size: PFP_SIZE, highlight: highlight, profiles: damus.profiles)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -236,33 +242,33 @@ struct EventView: View {
|
||||
|
||||
NoteContentView(privkey: damus.keypair.privkey, event: event, profiles: damus.profiles, previews: damus.previews, show_images: should_show_img, artifacts: .just_content(content), size: self.size)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.allowsHitTesting(!embedded)
|
||||
|
||||
if has_action_bar {
|
||||
if size == .selected {
|
||||
Text("\(format_date(event.created_at))")
|
||||
.padding(.top, 10)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
if !embedded {
|
||||
if let mention = first_eref_mention(ev: event, privkey: damus.keypair.privkey) {
|
||||
BuilderEventView(damus: damus, event_id: mention.ref.id)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
let bar = make_actionbar_model(ev: event, damus: damus)
|
||||
|
||||
if size == .selected && !bar.is_empty {
|
||||
EventDetailBar(state: damus, target: event.id, bar: bar)
|
||||
Divider()
|
||||
.padding([.bottom], 4)
|
||||
}
|
||||
|
||||
EventActionBar(damus_state: damus, event: event, bar: bar)
|
||||
}
|
||||
|
||||
Divider()
|
||||
.padding([.top], 4)
|
||||
Divider()
|
||||
.padding([.top], 4)
|
||||
}
|
||||
}
|
||||
.padding([.leading], 2)
|
||||
}
|
||||
@@ -324,19 +330,19 @@ extension View {
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32_pubkey(pubkey) ?? pubkey
|
||||
} label: {
|
||||
Label(NSLocalizedString("Copy User ID", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "tag")
|
||||
Label(NSLocalizedString("Copy User ID", comment: "Context menu option for copying the ID of the user who created the note."), systemImage: "person")
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = bech32_note_id(event.id) ?? event.id
|
||||
} label: {
|
||||
Label(NSLocalizedString("Copy Note ID", comment: "Context menu option for copying the ID of the note."), systemImage: "tag")
|
||||
Label(NSLocalizedString("Copy Note ID", comment: "Context menu option for copying the ID of the note."), systemImage: "note.text")
|
||||
}
|
||||
|
||||
Button {
|
||||
UIPasteboard.general.string = event_to_json(ev: event)
|
||||
} label: {
|
||||
Label(NSLocalizedString("Copy Note JSON", comment: "Context menu option for copying the JSON text from the note."), systemImage: "note")
|
||||
Label(NSLocalizedString("Copy Note JSON", comment: "Context menu option for copying the JSON text from the note."), systemImage: "j.square.on.square")
|
||||
}
|
||||
|
||||
Button {
|
||||
|
||||
@@ -19,8 +19,7 @@ struct FollowButtonView: View {
|
||||
follow_state = perform_follow_btn_action(follow_state, target: target)
|
||||
} label: {
|
||||
Text(follow_btn_txt(follow_state))
|
||||
.frame(height: 30)
|
||||
.padding(.horizontal, 25)
|
||||
.frame(width: 105, height: 30)
|
||||
//.padding(.vertical, 10)
|
||||
.font(.caption.weight(.bold))
|
||||
.foregroundColor(follow_state == .unfollows ? filledTextColor() : borderColor())
|
||||
|
||||
20
damus/Views/ImageView.swift
Normal file
20
damus/Views/ImageView.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// ImageView.swift
|
||||
// damus
|
||||
//
|
||||
// Created by user232838 on 1/5/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ImageView: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ImageView()
|
||||
}
|
||||
}
|
||||
33
damus/Views/MagnificationGestureView.swift
Normal file
33
damus/Views/MagnificationGestureView.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// MagnificationGestureView.swift
|
||||
// damus
|
||||
//
|
||||
// Created by user232838 on 1/5/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MagnificationGestureView: View {
|
||||
|
||||
@GestureState var magnifyBy = 1.0
|
||||
|
||||
var magnification: some Gesture {
|
||||
MagnificationGesture()
|
||||
.updating($magnifyBy) { currentState, gestureState, transaction in
|
||||
gestureState = currentState
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Circle()
|
||||
.frame(width: 100, height: 100)
|
||||
.scaleEffect(magnifyBy)
|
||||
.gesture(magnification)
|
||||
}
|
||||
}
|
||||
|
||||
struct MagnificationGestureView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MagnificationGestureView()
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
||||
}
|
||||
|
||||
func is_image_url(_ url: URL) -> Bool {
|
||||
let str = url.lastPathComponent
|
||||
let str = url.lastPathComponent.lowercased()
|
||||
return str.hasSuffix("png") || str.hasSuffix("jpg") || str.hasSuffix("jpeg") || str.hasSuffix("gif")
|
||||
}
|
||||
|
||||
|
||||
@@ -211,9 +211,8 @@ struct ProfileView: View {
|
||||
.onTapGesture {
|
||||
is_zoomed.toggle()
|
||||
}
|
||||
.sheet(isPresented: $is_zoomed) {
|
||||
ProfilePicView(pubkey: profile.pubkey, size: zoom_size, highlight: .none, profiles: damus_state.profiles)
|
||||
}
|
||||
.fullScreenCover(isPresented: $is_zoomed) {
|
||||
ProfileZoomView(pubkey: profile.pubkey, profiles: damus_state.profiles) }
|
||||
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
|
||||
|
||||
Spacer()
|
||||
@@ -356,7 +355,7 @@ struct ProfileView_Previews: PreviewProvider {
|
||||
|
||||
func test_damus_state() -> DamusState {
|
||||
let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||
let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(our_pubkey: pubkey), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel(), previews: PreviewCache())
|
||||
let damus = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: pubkey, privkey: "privkey"), likes: EventCounter(our_pubkey: pubkey), boosts: EventCounter(our_pubkey: pubkey), contacts: Contacts(our_pubkey: pubkey), tips: TipCounter(our_pubkey: pubkey), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: pubkey), previews: PreviewCache())
|
||||
|
||||
let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io")
|
||||
let tsprof = TimestampedProfile(profile: prof, timestamp: 0)
|
||||
|
||||
98
damus/Views/ProfileZoomView.swift
Normal file
98
damus/Views/ProfileZoomView.swift
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// ProfileZoomView.swift
|
||||
// damus
|
||||
//
|
||||
// Created by scoder1747 on 12/27/22.
|
||||
//
|
||||
import SwiftUI
|
||||
|
||||
struct ProfileZoomView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
let pubkey: String
|
||||
let profiles: Profiles
|
||||
|
||||
@GestureState private var scaleState: CGFloat = 1
|
||||
@GestureState private var offsetState = CGSize.zero
|
||||
|
||||
@State private var offset = CGSize.zero
|
||||
@State private var scale: CGFloat = 1
|
||||
|
||||
func resetStatus(){
|
||||
self.offset = CGSize.zero
|
||||
self.scale = 1
|
||||
}
|
||||
|
||||
var zoomGesture: some Gesture {
|
||||
MagnificationGesture()
|
||||
.updating($scaleState) { currentState, gestureState, _ in
|
||||
gestureState = currentState
|
||||
}
|
||||
.onEnded { value in
|
||||
scale *= value
|
||||
}
|
||||
}
|
||||
|
||||
var dragGesture: some Gesture {
|
||||
DragGesture()
|
||||
.updating($offsetState) { currentState, gestureState, _ in
|
||||
gestureState = currentState.translation
|
||||
}.onEnded { value in
|
||||
offset.height += value.translation.height
|
||||
offset.width += value.translation.width
|
||||
}
|
||||
}
|
||||
|
||||
var doubleTapGesture : some Gesture {
|
||||
TapGesture(count: 2).onEnded { value in
|
||||
resetStatus()
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
Color("DamusDarkGrey") // Or Color("DamusBlack")
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
HStack() {
|
||||
Button {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
.foregroundColor(.white)
|
||||
.font(.largeTitle)
|
||||
.frame(width: 40, height: 40)
|
||||
.padding(20)
|
||||
}
|
||||
}
|
||||
.zIndex(1)
|
||||
|
||||
VStack(alignment: .center) {
|
||||
Spacer()
|
||||
.frame(height: 120)
|
||||
|
||||
ProfilePicView(pubkey: pubkey, size: 200.0, highlight: .none, profiles: profiles)
|
||||
.padding(100)
|
||||
.scaledToFit()
|
||||
.scaleEffect(self.scale * scaleState)
|
||||
.offset(x: offset.width + offsetState.width, y: offset.height + offsetState.height)
|
||||
.gesture(SimultaneousGesture(zoomGesture, dragGesture))
|
||||
.gesture(doubleTapGesture)
|
||||
.modifier(SwipeToDismissModifier(minDistance: nil, onDismiss: {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfileZoomView_Previews: PreviewProvider {
|
||||
static let pubkey = "ca48854ac6555fed8e439ebb4fa2d928410e0eef13fa41164ec45aaaa132d846"
|
||||
|
||||
static var previews: some View {
|
||||
ProfileZoomView(
|
||||
pubkey: pubkey,
|
||||
profiles: make_preview_profiles(pubkey))
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ struct SideMenuView: View {
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var sideBarWidth = UIScreen.main.bounds.size.width * 0.65
|
||||
var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0)
|
||||
|
||||
func fillColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusWhite") : Color("DamusBlack")
|
||||
@@ -53,23 +53,23 @@ struct SideMenuView: View {
|
||||
let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey)
|
||||
let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state)
|
||||
|
||||
if let picture = damus_state.profiles.lookup(id: damus_state.pubkey)?.picture {
|
||||
NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) {
|
||||
NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) {
|
||||
if let picture = damus_state.profiles.lookup(id: damus_state.pubkey)?.picture {
|
||||
ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, picture: picture)
|
||||
} else {
|
||||
Image(systemName: "person.fill")
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "person.fill")
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
if let display_name = profile?.display_name {
|
||||
Text(display_name)
|
||||
.foregroundColor(textColor())
|
||||
.font(.title)
|
||||
}
|
||||
if let name = profile?.name {
|
||||
Text("@" + name)
|
||||
.foregroundColor(Color("DamusMediumGrey"))
|
||||
.font(.body)
|
||||
VStack(alignment: .leading) {
|
||||
if let display_name = profile?.display_name {
|
||||
Text(display_name)
|
||||
.foregroundColor(textColor())
|
||||
.font(.title)
|
||||
}
|
||||
if let name = profile?.name {
|
||||
Text("@" + name)
|
||||
.foregroundColor(Color("DamusMediumGrey"))
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ struct InnerTimelineView: View {
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.top,10)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
"NSPhotoLibraryAddUsageDescription" = "Si le concedes acceso a Damus a tu fototeca, podrás guardar fotos.";
|
||||
|
||||
493
damus/es-419.lproj/Localizable.strings
Normal file
493
damus/es-419.lproj/Localizable.strings
Normal file
@@ -0,0 +1,493 @@
|
||||
/* Blank space to separate profile picture from profile editor form. */
|
||||
" " = "61b6edf1108e6f396680a33b02486a70_tr";
|
||||
|
||||
/* Description of how the nip05 identifier would be used for verification. */
|
||||
"'%@' at '%@' will be used for verification" = "'%@' en '%@' se usarán con fines de verificación";
|
||||
|
||||
/* Description of why the nip05 identifier is invalid. */
|
||||
"'%@' is an invalid nip05 identifier. It should look like an email." = "'%@' es un identificador nip05 no válido. Debería de tener la apariencia de un correo electrónico.";
|
||||
|
||||
/* Navigation bar title for view that shows who is following a user. */
|
||||
"(Profile.displayName(profile: profile, pubkey: whos))'s Followers" = "Seguidores de (Profile.displayName(profile: profile, pubkey: whos))";
|
||||
|
||||
/* Navigation bar title for view that shows who a user is following. */
|
||||
"(who) following" = "(who) sigue a";
|
||||
|
||||
/* Prefix character to username. */
|
||||
"@" = "@";
|
||||
|
||||
/* Amount of time that has passed since reply quote event occurred.
|
||||
Abbreviated version of a nostr public key. */
|
||||
"%@" = "%@";
|
||||
|
||||
/* Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.
|
||||
Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* Explanation of what is done to keep personally identifiable information private. There is a heading that precedes this explanation which is a variable to this string. */
|
||||
"%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." = "%@. No se requiere un número de teléfono, correo electrónico ni nombre para crear una cuenta. Comienza de inmediato sin fricciones.";
|
||||
|
||||
/* Explanation of what is done to keep private data encrypted. There is a heading that precedes this explanation which is a variable to this string. */
|
||||
"%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs" = "%@. Mensajes privados con cifrado de un extremo a otro. Mantén a los gigantes tecnológicos fuera de tus mensajes directos.";
|
||||
|
||||
/* Explanation of what can be done by users to earn money. There is a heading that precedes this explanation which is a variable to this string. */
|
||||
"%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet." = "%@. Deja propinas en las publicaciones de tus amigos y acumula sats con Bitcoin⚡️, la moneda nativa de internet.";
|
||||
|
||||
/* Number of reposts.
|
||||
Number of profiles a user is following. */
|
||||
"%lld" = "%lld";
|
||||
|
||||
/* Fraction of how many of the user's relay servers that are operational. */
|
||||
"%lld/%lld" = "%lld/%lld";
|
||||
|
||||
/* Placeholder for event mention. */
|
||||
"< e >" = "< e >";
|
||||
|
||||
/* Label to prompt for about text entry for user to describe about themself. */
|
||||
"About" = "Información";
|
||||
|
||||
/* Label for About Me section of user profile form. */
|
||||
"About Me" = "Acerca de mí";
|
||||
|
||||
/* Placeholder text for About Me description. */
|
||||
"Absolute Boss" = "Jefe supremo";
|
||||
|
||||
/* Label to indicate the public ID of the account. */
|
||||
"Account ID" = "Identificador de cuenta";
|
||||
|
||||
/* Button to add recommended relay server.
|
||||
Button to confirm adding user inputted relay. */
|
||||
"Add" = "Agregar";
|
||||
|
||||
/* Label for section for adding a relay server. */
|
||||
"Add Relay" = "Agregar relé";
|
||||
|
||||
/* Any amount of sats */
|
||||
"Any" = "Cualquiera";
|
||||
|
||||
/* Alert message to ask if user wants to repost a post. */
|
||||
"Are you sure you want to repost this?" = "¿Seguro quieres volver a publicar esto?";
|
||||
|
||||
/* Label for Banner Image section of user profile form. */
|
||||
"Banner Image" = "Imagen del banner";
|
||||
|
||||
/* Reminder to user that they should save their account information. */
|
||||
"Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus." = "Antes de empezar, tendrás que guardar la información de tu cuenta. De lo contrario, no podrás iniciar sesión en el futuro si llegas a desinstalar Damus.";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Bitcoin Beach. */
|
||||
"Bitcoin Beach" = "Bitcoin Beach";
|
||||
|
||||
/* Label for Bitcoin Lightning Tips section of user profile form. */
|
||||
"Bitcoin Lightning Tips" = "Propinas con Bitcoin Lightning";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Blixt Wallet */
|
||||
"Blixt Wallet" = "Blixt Wallet";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Blue Wallet. */
|
||||
"Blue Wallet" = "Blue Wallet";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Breez. */
|
||||
"Breez" = "Breez";
|
||||
|
||||
/* Context menu option for broadcasting the user's note to all of the user's connected relay servers. */
|
||||
"Broadcast" = "Transmitir";
|
||||
|
||||
/* Button to cancel out of posting a note.
|
||||
Button to cancel out of reposting a post.
|
||||
Button to cancel out of view adding user inputted relay.
|
||||
Cancel out of logging out the user. */
|
||||
"Cancel" = "Cancelar";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Cash App. */
|
||||
"Cash App" = "Cash App";
|
||||
|
||||
/* Navigation bar title for Chatroom view. */
|
||||
"Chat" = "Chat";
|
||||
|
||||
/* Button for clearing cached data. */
|
||||
"Clear" = "Borrar";
|
||||
|
||||
/* Section title for clearing cached data. */
|
||||
"Clear Cache" = "Borrar caché";
|
||||
|
||||
/* Label indicating that a user's key was copied. */
|
||||
"Copied" = "Copiada";
|
||||
|
||||
/* Button to copy a relay server address. */
|
||||
"Copy" = "Copiar";
|
||||
|
||||
/* Context menu option for copying the ID of the account that created the note. */
|
||||
"Copy Account ID" = "Copiar identificador de cuenta";
|
||||
|
||||
/* Context menu option to copy an image into clipboard.
|
||||
Context menu option to copy an image to clipboard. */
|
||||
"Copy Image" = "Copiar imagen";
|
||||
|
||||
/* Context menu option to copy the URL of an image into clipboard. */
|
||||
"Copy Image URL" = "Copiar URL de imagen";
|
||||
|
||||
/* Title of section for copying a Lightning invoice identifier. */
|
||||
"Copy invoice" = "Copiar factura";
|
||||
|
||||
/* Context menu option for copying a user's Lightning URL. */
|
||||
"Copy LNURL" = "Copiar LNURL";
|
||||
|
||||
/* Context menu option for copying the ID of the note. */
|
||||
"Copy Note ID" = "Copiar identificador de nota";
|
||||
|
||||
/* Context menu option for copying the JSON text from the note. */
|
||||
"Copy Note JSON" = "Copiar JSON de nota";
|
||||
|
||||
/* Context menu option for copying the text from an note. */
|
||||
"Copy Text" = "Copiar texto";
|
||||
|
||||
/* Context menu option for copying the ID of the user who created the note. */
|
||||
"Copy User ID" = "Copiar identificador de usuario";
|
||||
|
||||
/* Button to create account. */
|
||||
"Create" = "Crear";
|
||||
|
||||
/* Button to create an account. */
|
||||
"Create Account" = "Crear cuenta";
|
||||
|
||||
/* Example description about Bitcoin creator(s), Satoshi Nakamoto. */
|
||||
"Creator(s) of Bitcoin. Absolute legend." = "Creador(es) de Bitcoin. Toda una leyenda.";
|
||||
|
||||
/* Name of the app, shown on the first screen when user is not logged in. */
|
||||
"Damus" = "Damus";
|
||||
|
||||
/* Button to pay a Lightning invoice with the user's default Lightning wallet. */
|
||||
"Default Wallet" = "Billetera predeterminada";
|
||||
|
||||
/* Button to delete a relay server that the user connects to. */
|
||||
"Delete" = "Borrar";
|
||||
|
||||
/* Button to dismiss a text field alert. */
|
||||
"Dismiss" = "Descartar";
|
||||
|
||||
/* Label to prompt display name entry. */
|
||||
"Display Name" = "Mostrar nombre";
|
||||
|
||||
/* Navigation title for DM view, which is the English abbreviation for Direct Message. */
|
||||
"DM" = "MD";
|
||||
|
||||
/* Button to dismiss wallet selection view for paying Lightning invoice. */
|
||||
"Done" = "Listo";
|
||||
|
||||
/* Heading indicating that this application allows users to earn money. */
|
||||
"Earn Money" = "Gana dinero";
|
||||
|
||||
/* Button to edit user's profile. */
|
||||
"Edit" = "Editar";
|
||||
|
||||
/* Heading indicating that this application keeps private messaging end-to-end encrypted. */
|
||||
"Encrypted" = "Cifrada";
|
||||
|
||||
/* Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message. */
|
||||
"Encrypted DMs" = "MD cifrados";
|
||||
|
||||
/* Prompt for user to enter an account key to login. */
|
||||
"Enter your account key to login:" = "Ingresa la clave de tu cuenta para iniciar sesión:";
|
||||
|
||||
/* Error message indicating why saving keys failed. */
|
||||
"Error: %@" = "Error: %@";
|
||||
|
||||
/* Filter state for seeing either only posts, or posts & replies. */
|
||||
"Filter State" = "Estado del filtro";
|
||||
|
||||
/* Button to follow a user. */
|
||||
"Follow" = "Seguir";
|
||||
|
||||
/* Label describing followers of a user. */
|
||||
"Followers" = "Seguidores";
|
||||
|
||||
/* Text to indicate that the button next to it is in a state that indicates that it is in the process of following a profile.
|
||||
Part of a larger sentence to describe how many profiles a user is following. */
|
||||
"Following" = "Siguiendo";
|
||||
|
||||
/* Label to indicate that the user is in the process of following another user. */
|
||||
"Following..." = "Siguiendo...";
|
||||
|
||||
/* Text to indicate that button next to it is in a state that will follow a profile when tapped. */
|
||||
"Follows" = "Sigue";
|
||||
|
||||
/* Navigation bar title for Global view where posts from all connected relay servers appear. */
|
||||
"Global" = "Global";
|
||||
|
||||
/* Navigation link to go to post referenced by hex code. */
|
||||
"Goto post %@" = "Ir a la publicación %@";
|
||||
|
||||
/* Navigation link to go to profile. */
|
||||
"Goto profile %@" = "Ir al perfil %@";
|
||||
|
||||
/* Navigation bar title for Home view where posts and replies appear from those who the user is following. */
|
||||
"Home" = "Inicio";
|
||||
|
||||
/* Placeholder example text for profile picture URL. */
|
||||
"https://example.com/pic.jpg" = "https://ejemplo.com/foto.jpg";
|
||||
|
||||
/* Placeholder example text for website URL for user profile. */
|
||||
"https://jb55.com" = "https://jb55.com";
|
||||
|
||||
/* Error message indicating that an invalid account key was entered for login. */
|
||||
"Invalid key" = "Clave inválida";
|
||||
|
||||
/* Placeholder example text for identifier used for NIP-05 verification. */
|
||||
"jb55@jb55.com" = "jb55@jb55.com";
|
||||
|
||||
/* Moves the post button to the left side of the screen */
|
||||
"Left Handed" = "Zurdo";
|
||||
|
||||
/* Button to complete account creation and start using the app. */
|
||||
"Let's go!" = "¡Vamos!";
|
||||
|
||||
/* Placeholder text for entry of Lightning Address or LNURL. */
|
||||
"Lightning Address or LNURL" = "Dirección de Lightning o LNURL";
|
||||
|
||||
/* Indicates that the view is for paying a Lightning invoice. */
|
||||
"Lightning Invoice" = "Factura de Lightning";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, LNLink. */
|
||||
"LNLink" = "LNLink";
|
||||
|
||||
/* Dropdown option label for system default for Lightning wallet. */
|
||||
"Local default" = "Predeterminada del sistema";
|
||||
|
||||
/* Button to log into account.
|
||||
Button to log into an account. */
|
||||
"Login" = "Iniciar sesión";
|
||||
|
||||
/* Alert for logging out the user.
|
||||
Button for logging out the user.
|
||||
Button to logout the user. */
|
||||
"Logout" = "Cerrar sesión";
|
||||
|
||||
/* Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out. */
|
||||
"Make sure your nsec account key is saved before you logout or you will lose access to this account" = "Asegúrate de que tu clave de cuenta nsec esté guardada antes de cerrar sesión o perderás el acceso a esta cuenta";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Muun. */
|
||||
"Muun" = "Muun";
|
||||
|
||||
/* Label for NIP-05 Verification section of user profile form. */
|
||||
"NIP-05 Verification" = "Verificación NIP-05";
|
||||
|
||||
/* No search results. */
|
||||
"none" = "ninguno";
|
||||
|
||||
/* Indicates that there are no notes in the timeline to view. */
|
||||
"Nothing to see here. Check back later!" = "Nada para ver aquí. ¡Vuelve a consultar luego!";
|
||||
|
||||
/* Navigation title for notifications. */
|
||||
"Notifications" = "Notificaciones";
|
||||
|
||||
/* String indicating that a given timestamp just occurred */
|
||||
"now" = "ahora";
|
||||
|
||||
/* Prompt for user to enter in an account key to login. This text shows the characters the key could start with if it was a private key. */
|
||||
"nsec1..." = "nsec1...";
|
||||
|
||||
/* Label indicating that a form input is optional. */
|
||||
"optional" = "opcional";
|
||||
|
||||
/* Button to pay a Lightning invoice. */
|
||||
"Pay" = "Pagar";
|
||||
|
||||
/* Navigation bar title for view to pay Lightning invoice. */
|
||||
"Pay the Lightning invoice" = "Paga la factura de Lightning";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Phoenix. */
|
||||
"Phoenix" = "Phoenix";
|
||||
|
||||
/* Button to post a note. */
|
||||
"Post" = "Publicar";
|
||||
|
||||
/* Label for filter for seeing only posts (instead of posts and replies). */
|
||||
"Posts" = "Publicaciones";
|
||||
|
||||
/* Label for filter for seeing posts and replies (instead of only posts). */
|
||||
"Posts & Replies" = "Publicaciones y respuestas";
|
||||
|
||||
/* Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading. */
|
||||
"Private" = "Privada";
|
||||
|
||||
/* Label to indicate that the text below is the user's private key used by only the user themself as a secret to login to access their account. */
|
||||
"Private Key" = "Clave privada";
|
||||
|
||||
/* Title of the secure field that holds the user's private key. */
|
||||
"PrivateKey" = "ClavePrivada";
|
||||
|
||||
/* Sidebar menu label for Profile view. */
|
||||
"Profile" = "Perfil";
|
||||
|
||||
/* Label for Profile Picture section of user profile form. */
|
||||
"Profile Picture" = "Foto del perfil";
|
||||
|
||||
/* Section title for the user's public account ID. */
|
||||
"Public Account ID" = "Identificador de cuenta pública";
|
||||
|
||||
/* Label indicating that the text is a user's public account key. */
|
||||
"Public key" = "Clave pública";
|
||||
|
||||
/* Label indicating that the text is a user's public account key. */
|
||||
"Public Key" = "Clave pública";
|
||||
|
||||
/* Prompt to ask user if the key they entered is a public key. */
|
||||
"Public Key?" = "¿Clave pública?";
|
||||
|
||||
/* Navigation bar title for Reactions view. */
|
||||
"Reactions" = "Reacciones";
|
||||
|
||||
/* Section title for recommend relay servers that could be added as part of configuration */
|
||||
"Recommended Relays" = "Relés recomendados";
|
||||
|
||||
/* Text field for relay server. Used for testing purposes. */
|
||||
"Relay" = "Relé";
|
||||
|
||||
/* Sidebar menu label for Relay servers view */
|
||||
"Relays" = "Relés";
|
||||
|
||||
/* Label to indicate that the user is replying to themself. */
|
||||
"Reply to self" = "Respuesta a sí mismo";
|
||||
|
||||
/* Label to indicate that the user is replying to 2 users. */
|
||||
"Replying to %@ & %@" = "Respondiendo a %1$@ y %2$@";
|
||||
|
||||
/* Indicating that the user is replying to the following listed people. */
|
||||
"Replying to:" = "Respondiendo a:";
|
||||
|
||||
/* Button to confirm reposting a post.
|
||||
Title of alert for confirming to repost a post. */
|
||||
"Repost" = "Republicar";
|
||||
|
||||
/* Text indicating that the post was reposted (i.e. re-shared). */
|
||||
"Reposted" = "Republicada";
|
||||
|
||||
/* Section title for resetting the user */
|
||||
"Reset" = "Reiniciar";
|
||||
|
||||
/* Button to retry completing account creation after an error occurred. */
|
||||
"Retry" = "Reintentar";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, River */
|
||||
"River" = "River";
|
||||
|
||||
/* Example username of Bitcoin creator(s), Satoshi Nakamoto. */
|
||||
"satoshi" = "satoshi";
|
||||
|
||||
/* Name of Bitcoin creator(s). */
|
||||
"Satoshi Nakamoto" = "Satoshi Nakamoto";
|
||||
|
||||
/* Button for saving profile. */
|
||||
"Save" = "Guardar";
|
||||
|
||||
/* Context menu option to save an image. */
|
||||
"Save Image" = "Guardar imagen";
|
||||
|
||||
/* Navigation link to search hashtag. */
|
||||
"Search hashtag: #%@" = "Buscar hashtag: #%@";
|
||||
|
||||
/* Placeholder text to prompt entry of search query. */
|
||||
"Search..." = "Búsqueda...";
|
||||
|
||||
/* Section title for user's secret account login key. */
|
||||
"Secret Account Login Key" = "Clave de inicio de sesión de cuenta secreta";
|
||||
|
||||
/* Title of section for selecting a Lightning wallet to pay a Lightning invoice. */
|
||||
"Select a Lightning wallet" = "Seleccionar una billetera de Lightning";
|
||||
|
||||
/* Prompt selection of user's default wallet */
|
||||
"Select default wallet" = "Seleccionar billetera predeterminada";
|
||||
|
||||
/* Text prompt for user to send a message to the other user. */
|
||||
"Send a message to start the conversation..." = "Envía un mensaje para empezar la conversación...";
|
||||
|
||||
/* Navigation title for Settings view.
|
||||
Sidebar menu label for accessing the app settings */
|
||||
"Settings" = "Configuración";
|
||||
|
||||
/* Button to share an image. */
|
||||
"Share" = "Compartir";
|
||||
|
||||
/* Toggle to show or hide user's secret account login key. */
|
||||
"Show" = "Mostrar";
|
||||
|
||||
/* Toggle to show or hide selection of wallet. */
|
||||
"Show wallet selector" = "Mostrar selector de billetera";
|
||||
|
||||
/* Sidebar menu label to sign out of the account. */
|
||||
"Sign out" = "Cerrar sesión";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Strike. */
|
||||
"Strike" = "Strike";
|
||||
|
||||
/* Warning that the inputted account key is a public key and the result of what happens because of it. */
|
||||
"This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." = "Esta es una clave pública, por lo que no podrás hacer publicaciones ni interactuar de ningún modo. Se usa para ver cuentas desde su perspectiva.";
|
||||
|
||||
/* Warning that the inputted account key for login is an old-style and asking user to verify if it is a public key. */
|
||||
"This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key." = "Esta es una clave de nostr antigua. No sabemos con seguridad si es una clave pública o privada. Activa el siguiente botón si es una clave pública.";
|
||||
|
||||
/* Label to describe that a public key is the user's account ID and what they can do with it. */
|
||||
"This is your account ID, you can give this to your friends so that they can follow you. Click to copy." = "Este es tu identificador de cuenta, que puedes compartir con tus amigos para que te sigan. Haz clic para copiarlo.";
|
||||
|
||||
/* Label to describe that a private key is the user's secret account key and what they should do with it. */
|
||||
"This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!" = "Esta es tu clave de cuenta secreta, que necesitas para acceder a tu cuenta. ¡No la compartas con nadie! Guárdala en un administrador de contraseñas y protégela!";
|
||||
|
||||
/* Navigation bar title for note thread.
|
||||
Navigation bar title for threaded event detail view. */
|
||||
"Thread" = "Hilo";
|
||||
|
||||
/* Text box prompt to ask user to type their post. */
|
||||
"Type your post here..." = "Ingresa tu publicación aquí...";
|
||||
|
||||
/* Non-breaking space character to fill in blank space next to event action button icons. */
|
||||
"u{00A0}" = "u{00A0}";
|
||||
|
||||
/* Button to unfollow a user. */
|
||||
"Unfollow" = "Dejar de seguir";
|
||||
|
||||
/* Text to indicate that the button next to it is in a state that indicates that it is in the process of unfollowing a profile. */
|
||||
"Unfollowing" = "Dejando de seguir";
|
||||
|
||||
/* Label to indicate that the user is in the process of unfollowing another user. */
|
||||
"Unfollowing..." = "Dejando de seguir...";
|
||||
|
||||
/* Text to indicate that the button next to it is in a state that will unfollow a profile when tapped. */
|
||||
"Unfollows" = "Deja de seguir";
|
||||
|
||||
/* Label for Username section of user profile form.
|
||||
Label to prompt username entry. */
|
||||
"Username" = "Nombre de usuario";
|
||||
|
||||
/* Sidebar menu label for Wallet view. */
|
||||
"Wallet" = "Billetera";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Wallet Of Satoshi. */
|
||||
"Wallet Of Satoshi" = "Wallet Of Satoshi";
|
||||
|
||||
/* Section title for selection of wallet. */
|
||||
"Wallet Selector" = "Selección de billetera";
|
||||
|
||||
/* Label for Website section of user profile form. */
|
||||
"Website" = "Sitio web";
|
||||
|
||||
/* Welcoming message to the reader. The variable is 'you', the reader. */
|
||||
"Welcome to the social network %@ control." = "Te damos la bienvenida a la red social que %@ controlas.";
|
||||
|
||||
/* Text to welcome user. */
|
||||
"Welcome, %@!" = "¡Te damos la bienvenida, %@!";
|
||||
|
||||
/* Placeholder example for relay server address. */
|
||||
"wss://some.relay.com" = "wss://algún.relé.com";
|
||||
|
||||
/* You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself. */
|
||||
"you" = "tú";
|
||||
|
||||
/* Label for Your Name section of user profile form. */
|
||||
"Your Name" = "Tu nombre";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Zebedee. */
|
||||
"Zebedee" = "Zebedee";
|
||||
|
||||
/* Dropdown option label for Lightning wallet, Zeus LN. */
|
||||
"Zeus LN" = "Zeus LN";
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
<dict>
|
||||
<key>collapsed_event_view_other_notes</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>··· %#@NOTES@ ···</string>
|
||||
<key>NOTES</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@@ -13,15 +11,15 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>%d other note</string>
|
||||
<string>%d otra nota</string>
|
||||
<key>other</key>
|
||||
<string>%d other notes</string>
|
||||
<string>%d otras notas</string>
|
||||
</dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>··· %#@NOTES@ ···</string>
|
||||
</dict>
|
||||
<key>followers_count</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@FOLLOWERS@</string>
|
||||
<key>FOLLOWERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
@@ -29,10 +27,12 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Follower</string>
|
||||
<string>Seguidor</string>
|
||||
<key>other</key>
|
||||
<string>Followers</string>
|
||||
<string>Seguidores</string>
|
||||
</dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>%#@FOLLOWERS@</string>
|
||||
</dict>
|
||||
<key>reactions_count</key>
|
||||
<dict>
|
||||
@@ -45,9 +45,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Reaction</string>
|
||||
<string>Reacción</string>
|
||||
<key>other</key>
|
||||
<string>Reactions</string>
|
||||
<string>Reacciones</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>relays_count</key>
|
||||
@@ -61,45 +61,45 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Relay</string>
|
||||
<string>Relé</string>
|
||||
<key>other</key>
|
||||
<string>Relays</string>
|
||||
<string>Relés</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>replying_to_one_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Replying to %@%#@OTHERS@</string>
|
||||
<string>Respondiendo a %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string> y %d otro</string>
|
||||
<key>other</key>
|
||||
<string> y %d otros</string>
|
||||
<key>zero</key>
|
||||
<string></string>
|
||||
<key>one</key>
|
||||
<string> & %d other</string>
|
||||
<key>other</key>
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>replying_to_two_and_others</key>
|
||||
<dict>
|
||||
<key>NSStringLocalizedFormatKey</key>
|
||||
<string>Replying to %@, %@%#@OTHERS@</string>
|
||||
<string>Respondiendo a %@, %@%#@OTHERS@</string>
|
||||
<key>OTHERS</key>
|
||||
<dict>
|
||||
<key>NSStringFormatSpecTypeKey</key>
|
||||
<string>NSStringPluralRuleType</string>
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string> y %d otro</string>
|
||||
<key>other</key>
|
||||
<string> y %d otros</string>
|
||||
<key>zero</key>
|
||||
<string></string>
|
||||
<key>one</key>
|
||||
<string> & %d other</string>
|
||||
<key>other</key>
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>reposts_count</key>
|
||||
@@ -113,9 +113,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Repost</string>
|
||||
<string>Republicación</string>
|
||||
<key>other</key>
|
||||
<string>Reposts</string>
|
||||
<string>Republicaciones</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>sats_count</key>
|
||||
@@ -145,9 +145,9 @@
|
||||
<key>NSStringFormatValueTypeKey</key>
|
||||
<string>d</string>
|
||||
<key>one</key>
|
||||
<string>Tip</string>
|
||||
<string>Propina</string>
|
||||
<key>other</key>
|
||||
<string>Tips</string>
|
||||
<string>Propinas</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
Reference in New Issue
Block a user