Compare commits
128 Commits
tx_transla
...
tyiu/fix-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
6870978e49
|
|||
|
|
bcb40a6ec7 | ||
|
|
2f1063b49f | ||
|
|
73110952e5 | ||
| d01e7c0595 | |||
|
|
4f9cef541b | ||
|
|
2dfbc4b57e | ||
|
|
b9750dab77 | ||
|
|
d59331bc3c | ||
|
|
abd5856f21 | ||
|
|
42a475bd72 | ||
|
|
65c1325935 | ||
|
|
db64a73f87 | ||
|
|
9d44ed0bfe | ||
|
|
33383265c8 | ||
|
|
76d1ee34d8 | ||
|
|
3b0a84bd43 | ||
|
|
5a1daebeca | ||
| 08666ff90d | |||
|
|
b2b790a969 | ||
|
|
907f0d236f | ||
|
|
4b8f536a9b | ||
|
|
c76f92c6ed | ||
|
|
a165f4281c | ||
|
|
5dbf8029da | ||
|
|
b1885700ca | ||
|
|
9e5209b48d | ||
|
|
6bcea13d5a | ||
|
|
9944a7bf2a | ||
|
|
424fb55343 | ||
|
|
901931443f | ||
|
|
40d11ad680 | ||
|
|
4d8088d0d0 | ||
|
|
aee29f145a | ||
|
|
c9463804a8 | ||
|
|
dccaf35410 | ||
|
|
45e64dc42c | ||
|
|
5a32a384c4 | ||
|
|
35ae69740a | ||
|
|
43cc3b6b6b | ||
|
b367382aae
|
|||
|
bf1866056c
|
|||
|
016dfa54f3
|
|||
|
887eb902bf
|
|||
|
|
966f330317 | ||
|
|
cb54ac0494 | ||
|
|
fac1911524 | ||
|
dcf328e7ac
|
|||
|
|
38211a74f5 | ||
|
|
9b236f9583 | ||
|
|
4378397c4c | ||
|
|
392281ca40 | ||
|
|
3bf39dc04b | ||
|
|
8161d55d05 | ||
|
|
30151e14fa | ||
|
|
c6ca0a058e | ||
|
|
c10fcc52e3 | ||
|
|
08dd2d5414 | ||
|
|
d9b60c0052 | ||
|
|
fba29d4454 | ||
|
|
421245a2f8 | ||
|
|
ec4790a8fe | ||
|
|
5f22a7691f | ||
|
|
d878ff6fdb | ||
|
|
7081c4ac69 | ||
|
|
b03bef2f8b | ||
|
|
5733f782d9 | ||
|
|
a153515366 | ||
|
|
d5857325b1 | ||
|
|
04e4bc7985 | ||
|
|
e7d32d9ea7 | ||
|
|
442a50f9ae | ||
|
|
9f8379cb1e | ||
|
|
d0eb632ce6 | ||
|
|
cd62418dda | ||
|
|
ba32d15a49 | ||
|
|
b688fa98a5 | ||
|
|
61a451184b | ||
|
|
96741af97b | ||
|
|
553dcb785c | ||
|
|
22008aeabc | ||
|
|
64bb28e017 | ||
|
|
c58c349053 | ||
|
|
4d358415bd | ||
|
|
5ba5a68628 | ||
|
|
89c857d3e9 | ||
|
|
4d3a3184b4 | ||
|
|
71b1a9d14f | ||
|
|
8785f31834 | ||
|
|
104205394f | ||
|
|
6593c9456d | ||
|
|
186954195d | ||
|
|
dba31a9d33 | ||
|
|
a69fb5306c | ||
|
|
fb1bcdd31f | ||
|
|
2ccc7e9a30 | ||
|
|
e9380c3821 | ||
|
|
39fa973a80 | ||
|
|
b70ce53b88 | ||
|
|
16e3c4e1cf | ||
|
|
b3b8a708f3 | ||
|
|
d89e3d32f8 | ||
|
|
b99e9b9acc | ||
|
|
61974ca696 | ||
|
|
42f484bc64 | ||
|
|
43c6084620 | ||
|
|
501412e75c | ||
|
|
5789cc0097 | ||
|
|
d4995aa4bf | ||
|
|
57dbb6a487 | ||
|
|
7f71ddce1d | ||
|
|
abf736ec2a | ||
|
|
2cfcc82b2d | ||
|
|
aaa21bf1bf | ||
|
|
ba03be5b91 | ||
|
|
3f3b78f9bc | ||
|
|
2348f64dff | ||
| 8428f0af43 | |||
|
|
8c91ce3e10 | ||
|
|
5fd5593595 | ||
|
|
2e99e5acaa | ||
|
|
0b7a600c67 | ||
|
|
bd5390fbc0 | ||
|
|
068099c5a7 | ||
|
|
7372c4847d | ||
|
|
b42f0ec5eb | ||
|
|
0d2ab6aff3 | ||
|
|
e3732b3adc |
17
.github/workflows/run-tests.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
jobs:
|
||||
test:
|
||||
name: Run Tests
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v1
|
||||
- name: Running Tests
|
||||
run: xcodebuild test -scheme damus -project damus.xcodeproj -destination 'platform=iOS Simulator,name=iPhone 14,OS=16.0' | xcpretty && exit ${PIPESTATUS[0]}
|
||||
119
CHANGELOG.md
@@ -1,3 +1,120 @@
|
||||
## [1.0.0-6] - 2023-01-13
|
||||
|
||||
### Added
|
||||
|
||||
- Profile banner images (Jason Jōb)
|
||||
- Added Reactions View (William Casarin)
|
||||
- Left hand option for post button (Jonathan Milligan)
|
||||
- Damus icon at the top (Ben Weeks)
|
||||
- Make purple badges on profile page tappable (Joel Klabo)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Make Shaka button purple when liked (Joel Klabo)
|
||||
- Move counts to right side like Birdsite (Joel Klabo)
|
||||
- Use custom icon for shaka button (Joel Klabo)
|
||||
- Renamed boost to repost (William Casarin)
|
||||
- Removed nip05 domain from boosts/reposts (William Casarin)
|
||||
- Make DMs only take up 80% of screen width (Jonathan Milligan)
|
||||
- Hide Recommended Relays Section if Empty (Joel Klabo)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed shaka moving when you press it (Joel Klabo)
|
||||
- Fixed issue with relays not keeping in sync when adding (Fredrik Olofsson)
|
||||
|
||||
|
||||
|
||||
[1.0.0-6]: https://github.com/damus-io/damus/releases/tag/v1.0.0-6
|
||||
|
||||
|
||||
## [1.0.0-5] - 2023-01-06
|
||||
|
||||
### Added
|
||||
|
||||
- Added share button to profile (William Casarin)
|
||||
- Added universal link sharing of notes (William Casarin)
|
||||
- Added clear cache button to wipe pfp/image cache (OlegAba)
|
||||
- Allow Adding Relay Without wss:// Prefix (Joel Klabo)
|
||||
- Allow Saving Images to Library (Joel Klabo)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Added damus gradient to post button (Ben Weeks)
|
||||
- Center the Post Button (Thomas)
|
||||
- Switch yellow nip05 check to gray (William Casarin)
|
||||
- Switch from bluecheck to purplecheck (William Casarin)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add system background color to profile pics (OlegAba)
|
||||
- High res color pubkey on profile page (William Casarin)
|
||||
- Don't spin forever if we're temporarily disconnected (William Casarin)
|
||||
- Fixed a few issues with avatars not animating (OlegAba)
|
||||
- Scroll to bottom when new DM received (Aidan O'Loan)
|
||||
- Make reply view scrollable (Joel Klabo)
|
||||
- Hide profile edit button when logged in with pubkey (Swift)
|
||||
|
||||
|
||||
[1.0.0-5]: https://github.com/damus-io/damus/releases/tag/v1.0.0-5
|
||||
|
||||
## [1.0.0-4] - 2023-01-04
|
||||
|
||||
### Added
|
||||
|
||||
- Added NIP05 Verification (William Casarin)
|
||||
- Downscale images if they are unreasonably large (OlegAba)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Revert to old style ln/dm buttons (William Casarin)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix ascii shrug guy (Lionello Lunesu)
|
||||
- Fix navigation popping in threads (William Casarin)
|
||||
|
||||
|
||||
[1.0.0-4]: https://github.com/damus-io/damus/releases/tag/v1.0.0-4
|
||||
|
||||
## [1.0.0-2] - 2023-01-03
|
||||
|
||||
### Added
|
||||
|
||||
- Cache link previews (William Casarin)
|
||||
- Added brb.io to recommended relay list (William Casarin)
|
||||
- Add Blixt Wallet to Wallet Selector (Benjamin Hakes)
|
||||
- Add River Wallet to Wallet Selector (Benjamin Hakes)
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Added muted shaka images instead of thumbs up (CutClout)
|
||||
- Updated profile page look and feel (Ben Weeks)
|
||||
- Filter replies from global feed (Nitesh Balusu)
|
||||
- Show non-image links inline (William Casarin)
|
||||
- Add swipe gesture to switch between tabs (Thomas Rademaker)
|
||||
- Parse links in profiles (Lionello Lunesu) (Lio李歐)
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix detection of email addresses in profiles (Lionello Lunesu)
|
||||
- Fix padding on search results view (OlegAba)
|
||||
- Fix home view moving after selecting from search result (OlegAba)
|
||||
- Fix bug where boost event is loaded in the thread instead of the boosted event (William Casarin)
|
||||
- Hide edit button on profile page when no private key (Swift)
|
||||
- Fixed follows and relays getting out of sync on profile pages (William Casarin)
|
||||
|
||||
|
||||
|
||||
[1.0.0-2]: https://github.com/damus-io/damus/releases/tag/v1.0.0-2
|
||||
## [1.0.0] - 2023-01-01
|
||||
|
||||
### Added
|
||||
@@ -266,3 +383,5 @@
|
||||
|
||||
[0.1.2]: https://github.com/damus-io/damus/releases/tag/v0.1.2
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
||||
## Getting Started on Damus
|
||||
|
||||
### Damus iOS
|
||||
1) Get the Damus app on Testflight: https://testflight.apple.com/join/CLwjLxWl
|
||||
1) Get the Damus app on TestFlight: https://testflight.apple.com/join/CLwjLxWl
|
||||
|
||||
#### ⚙️ Settings (gear icon, top right)
|
||||
- Relays: You can add more relays to send your notes to by tapping the "+".
|
||||
@@ -56,8 +56,8 @@ damus implements the following [Nostr Implementation Possibilities][nips]
|
||||
- Formatting Notes (may not format as intended in other web clients)
|
||||
- Italics: 1 asterisk `*italic*`
|
||||
- Bold: 2 asterisk `**bold**`
|
||||
- Strikethrough: 2 tildes `~~strikethrough~~`
|
||||
- Code: 1 back-tick ``code``
|
||||
- Strikethrough: 1 tildes `~strikethrough~`
|
||||
- Code: 1 back-tick `` `code` ``
|
||||
|
||||
#### 💬 Encrypted DMs (chat app, bottom navigation)
|
||||
- Tap the chat icon and you'll notice there's nothing to see at first. Go to a user profile and tap the 💬 chat icon next to the follow button to begin a DM
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
@@ -0,0 +1,154 @@
|
||||
<?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,7 +1,7 @@
|
||||
{
|
||||
"developmentRegion" : "en",
|
||||
"developmentRegion" : "en-US",
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "en",
|
||||
"targetLocale" : "en-US",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "14C18",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
@@ -1,4 +0,0 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
@@ -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> & 1 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> & 1 other</string>
|
||||
<key>other</key>
|
||||
<string> & %d others</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
869
damus Localizations/es-419.xcloc/Localized Contents/es-419.xliff
Normal file
@@ -0,0 +1,869 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||
<file original="damus/en-US.lproj/InfoPlist.strings" datatype="plaintext" source-language="en-US" target-language="es-419">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||
<source>Damus</source>
|
||||
<note>Bundle display name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||
<source>damus</source>
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
|
||||
<source>"Granting Damus access to your photo library allows you to save photos.</source>
|
||||
<note>Privacy - Photo Library Additions Usage Description</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="es-419" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<source> </source>
|
||||
<note>Blank space to separate profile picture from profile editor form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@" xml:space="preserve">
|
||||
<source>%@</source>
|
||||
<note>Amount of time that has passed since reply quote event occurred.
|
||||
Abbreviated version of a nostr public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ %@" xml:space="preserve">
|
||||
<source>%@ %@</source>
|
||||
<note>Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.
|
||||
Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction." xml:space="preserve">
|
||||
<source>%@. Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.</source>
|
||||
<note>Explanation of what is done to keep personally identifiable information private. There is a heading that precedes this explanation which is a variable to this string.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs" xml:space="preserve">
|
||||
<source>%@. End-to-End encrypted private messaging. Keep Big Tech out of your DMs</source>
|
||||
<note>Explanation of what is done to keep private data encrypted. There is a heading that precedes this explanation which is a variable to this string.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet." xml:space="preserve">
|
||||
<source>%@. Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet.</source>
|
||||
<note>Explanation of what can be done by users to earn money. There is a heading that precedes this explanation which is a variable to this string.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld" xml:space="preserve">
|
||||
<source>%lld</source>
|
||||
<note>Number of reposts.
|
||||
Number of profiles a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld/%lld" xml:space="preserve">
|
||||
<source>%lld/%lld</source>
|
||||
<note>Fraction of how many of the user's relay servers that are operational.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="'%@' at '%@' will be used for verification" xml:space="preserve">
|
||||
<source>'%@' at '%@' will be used for verification</source>
|
||||
<note>Description of how the nip05 identifier would be used for verification.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="'%@' is an invalid nip05 identifier. It should look like an email." xml:space="preserve">
|
||||
<source>'%@' is an invalid nip05 identifier. It should look like an email.</source>
|
||||
<note>Description of why the nip05 identifier is invalid.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(Profile.displayName(profile: profile, pubkey: whos))'s Followers" xml:space="preserve">
|
||||
<source>(Profile.displayName(profile: profile, pubkey: whos))'s Followers</source>
|
||||
<note>Navigation bar title for view that shows who is following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(who) following" xml:space="preserve">
|
||||
<source>(who) following</source>
|
||||
<note>Navigation bar title for view that shows who a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="< e >" xml:space="preserve">
|
||||
<source>< e ></source>
|
||||
<note>Placeholder for event mention.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="@" xml:space="preserve">
|
||||
<source>@</source>
|
||||
<note>Prefix character to username.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About" xml:space="preserve">
|
||||
<source>About</source>
|
||||
<note>Label to prompt for about text entry for user to describe about themself.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About Me" xml:space="preserve">
|
||||
<source>About Me</source>
|
||||
<note>Label for About Me section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Absolute Boss" xml:space="preserve">
|
||||
<source>Absolute Boss</source>
|
||||
<note>Placeholder text for About Me description.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Account ID" xml:space="preserve">
|
||||
<source>Account ID</source>
|
||||
<note>Label to indicate the public ID of the account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add" xml:space="preserve">
|
||||
<source>Add</source>
|
||||
<note>Button to add recommended relay server.
|
||||
Button to confirm adding user inputted relay.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add Relay" xml:space="preserve">
|
||||
<source>Add Relay</source>
|
||||
<note>Label for section for adding a relay server.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Any" xml:space="preserve">
|
||||
<source>Any</source>
|
||||
<note>Any amount of sats</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Are you sure you want to repost this?" xml:space="preserve">
|
||||
<source>Are you sure you want to repost this?</source>
|
||||
<note>Alert message to ask if user wants to repost a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Banner Image" xml:space="preserve">
|
||||
<source>Banner Image</source>
|
||||
<note>Label for Banner Image section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus." xml:space="preserve">
|
||||
<source>Before we get started, you'll need to save your account info, otherwise you won't be able to login in the future if you ever uninstall Damus.</source>
|
||||
<note>Reminder to user that they should save their account information.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bitcoin Beach" xml:space="preserve">
|
||||
<source>Bitcoin Beach</source>
|
||||
<note>Dropdown option label for Lightning wallet, Bitcoin Beach.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bitcoin Lightning Tips" xml:space="preserve">
|
||||
<source>Bitcoin Lightning Tips</source>
|
||||
<note>Label for Bitcoin Lightning Tips section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Blixt Wallet" xml:space="preserve">
|
||||
<source>Blixt Wallet</source>
|
||||
<note>Dropdown option label for Lightning wallet, Blixt Wallet</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Blue Wallet" xml:space="preserve">
|
||||
<source>Blue Wallet</source>
|
||||
<note>Dropdown option label for Lightning wallet, Blue Wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Breez" xml:space="preserve">
|
||||
<source>Breez</source>
|
||||
<note>Dropdown option label for Lightning wallet, Breez.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Broadcast" xml:space="preserve">
|
||||
<source>Broadcast</source>
|
||||
<note>Context menu option for broadcasting the user's note to all of the user's connected relay servers.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<note>Button to cancel out of posting a note.
|
||||
Button to cancel out of reposting a post.
|
||||
Button to cancel out of view adding user inputted relay.
|
||||
Cancel out of logging out the user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cash App" xml:space="preserve">
|
||||
<source>Cash App</source>
|
||||
<note>Dropdown option label for Lightning wallet, Cash App.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat" xml:space="preserve">
|
||||
<source>Chat</source>
|
||||
<note>Navigation bar title for Chatroom view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Clear" xml:space="preserve">
|
||||
<source>Clear</source>
|
||||
<note>Button for clearing cached data.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Clear Cache" xml:space="preserve">
|
||||
<source>Clear Cache</source>
|
||||
<note>Section title for clearing cached data.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copied" xml:space="preserve">
|
||||
<source>Copied</source>
|
||||
<note>Label indicating that a user's key was copied.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<note>Button to copy a relay server address.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Account ID" xml:space="preserve">
|
||||
<source>Copy Account ID</source>
|
||||
<note>Context menu option for copying the ID of the account that created the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Image" xml:space="preserve">
|
||||
<source>Copy Image</source>
|
||||
<note>Context menu option to copy an image into clipboard.
|
||||
Context menu option to copy an image to clipboard.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Image URL" xml:space="preserve">
|
||||
<source>Copy Image URL</source>
|
||||
<note>Context menu option to copy the URL of an image into clipboard.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy LNURL" xml:space="preserve">
|
||||
<source>Copy LNURL</source>
|
||||
<note>Context menu option for copying a user's Lightning URL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Note ID" xml:space="preserve">
|
||||
<source>Copy Note ID</source>
|
||||
<note>Context menu option for copying the ID of the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Note JSON" xml:space="preserve">
|
||||
<source>Copy Note JSON</source>
|
||||
<note>Context menu option for copying the JSON text from the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy Text" xml:space="preserve">
|
||||
<source>Copy Text</source>
|
||||
<note>Context menu option for copying the text from an note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy User ID" xml:space="preserve">
|
||||
<source>Copy User ID</source>
|
||||
<note>Context menu option for copying the ID of the user who created the note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy invoice" xml:space="preserve">
|
||||
<source>Copy invoice</source>
|
||||
<note>Title of section for copying a Lightning invoice identifier.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create" xml:space="preserve">
|
||||
<source>Create</source>
|
||||
<note>Button to create account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create Account" xml:space="preserve">
|
||||
<source>Create Account</source>
|
||||
<note>Button to create an account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creator(s) of Bitcoin. Absolute legend." xml:space="preserve">
|
||||
<source>Creator(s) of Bitcoin. Absolute legend.</source>
|
||||
<note>Example description about Bitcoin creator(s), Satoshi Nakamoto.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="DM" xml:space="preserve">
|
||||
<source>DM</source>
|
||||
<note>Navigation title for DM view, which is the English abbreviation for Direct Message.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Damus" xml:space="preserve">
|
||||
<source>Damus</source>
|
||||
<note>Name of the app, shown on the first screen when user is not logged in.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Default Wallet" xml:space="preserve">
|
||||
<source>Default Wallet</source>
|
||||
<note>Button to pay a Lightning invoice with the user's default Lightning wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete" xml:space="preserve">
|
||||
<source>Delete</source>
|
||||
<note>Button to delete a relay server that the user connects to.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Dismiss" xml:space="preserve">
|
||||
<source>Dismiss</source>
|
||||
<note>Button to dismiss a text field alert.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Display Name" xml:space="preserve">
|
||||
<source>Display Name</source>
|
||||
<note>Label to prompt display name entry.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Done" xml:space="preserve">
|
||||
<source>Done</source>
|
||||
<note>Button to dismiss wallet selection view for paying Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Earn Money" xml:space="preserve">
|
||||
<source>Earn Money</source>
|
||||
<note>Heading indicating that this application allows users to earn money.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit" xml:space="preserve">
|
||||
<source>Edit</source>
|
||||
<note>Button to edit user's profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted" xml:space="preserve">
|
||||
<source>Encrypted</source>
|
||||
<note>Heading indicating that this application keeps private messaging end-to-end encrypted.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted DMs" xml:space="preserve">
|
||||
<source>Encrypted DMs</source>
|
||||
<note>Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter your account key to login:" xml:space="preserve">
|
||||
<source>Enter your account key to login:</source>
|
||||
<note>Prompt for user to enter an account key to login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: %@" xml:space="preserve">
|
||||
<source>Error: %@</source>
|
||||
<note>Error message indicating why saving keys failed.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Filter State" xml:space="preserve">
|
||||
<source>Filter State</source>
|
||||
<note>Filter state for seeing either only posts, or posts & replies.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follow" xml:space="preserve">
|
||||
<source>Follow</source>
|
||||
<note>Button to follow a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Followers" xml:space="preserve">
|
||||
<source>Followers</source>
|
||||
<note>Label describing followers of a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Following" xml:space="preserve">
|
||||
<source>Following</source>
|
||||
<note>Text to indicate that the button next to it is in a state that indicates that it is in the process of following a profile.
|
||||
Part of a larger sentence to describe how many profiles a user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Following..." xml:space="preserve">
|
||||
<source>Following...</source>
|
||||
<note>Label to indicate that the user is in the process of following another user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Follows" xml:space="preserve">
|
||||
<source>Follows</source>
|
||||
<note>Text to indicate that button next to it is in a state that will follow a profile when tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Global" xml:space="preserve">
|
||||
<source>Global</source>
|
||||
<note>Navigation bar title for Global view where posts from all connected relay servers appear.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Goto post %@" xml:space="preserve">
|
||||
<source>Goto post %@</source>
|
||||
<note>Navigation link to go to post referenced by hex code.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Goto profile %@" xml:space="preserve">
|
||||
<source>Goto profile %@</source>
|
||||
<note>Navigation link to go to profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Home" xml:space="preserve">
|
||||
<source>Home</source>
|
||||
<note>Navigation bar title for Home view where posts and replies appear from those who the user is following.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid key" xml:space="preserve">
|
||||
<source>Invalid key</source>
|
||||
<note>Error message indicating that an invalid account key was entered for login.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="LNLink" xml:space="preserve">
|
||||
<source>LNLink</source>
|
||||
<note>Dropdown option label for Lightning wallet, LNLink.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Left Handed" xml:space="preserve">
|
||||
<source>Left Handed</source>
|
||||
<note>Moves the post button to the left side of the screen</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Let's go!" xml:space="preserve">
|
||||
<source>Let's go!</source>
|
||||
<note>Button to complete account creation and start using the app.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lightning Address or LNURL" xml:space="preserve">
|
||||
<source>Lightning Address or LNURL</source>
|
||||
<note>Placeholder text for entry of Lightning Address or LNURL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lightning Invoice" xml:space="preserve">
|
||||
<source>Lightning Invoice</source>
|
||||
<note>Indicates that the view is for paying a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local default" xml:space="preserve">
|
||||
<source>Local default</source>
|
||||
<note>Dropdown option label for system default for Lightning wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Login" xml:space="preserve">
|
||||
<source>Login</source>
|
||||
<note>Button to log into account.
|
||||
Button to log into an account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Logout" xml:space="preserve">
|
||||
<source>Logout</source>
|
||||
<note>Alert for logging out the user.
|
||||
Button for logging out the user.
|
||||
Button to logout the user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
|
||||
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
|
||||
<note>Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Muun" xml:space="preserve">
|
||||
<source>Muun</source>
|
||||
<note>Dropdown option label for Lightning wallet, Muun.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NIP-05 Verification" xml:space="preserve">
|
||||
<source>NIP-05 Verification</source>
|
||||
<note>Label for NIP-05 Verification section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Nothing to see here. Check back later!" xml:space="preserve">
|
||||
<source>Nothing to see here. Check back later!</source>
|
||||
<note>Indicates that there are no notes in the timeline to view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications" xml:space="preserve">
|
||||
<source>Notifications</source>
|
||||
<note>Navigation title for notifications.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Pay" xml:space="preserve">
|
||||
<source>Pay</source>
|
||||
<note>Button to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Pay the Lightning invoice" xml:space="preserve">
|
||||
<source>Pay the Lightning invoice</source>
|
||||
<note>Navigation bar title for view to pay Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Phoenix" xml:space="preserve">
|
||||
<source>Phoenix</source>
|
||||
<note>Dropdown option label for Lightning wallet, Phoenix.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Post" xml:space="preserve">
|
||||
<source>Post</source>
|
||||
<note>Button to post a note.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Posts" xml:space="preserve">
|
||||
<source>Posts</source>
|
||||
<note>Label for filter for seeing only posts (instead of posts and replies).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Posts & Replies" xml:space="preserve">
|
||||
<source>Posts & Replies</source>
|
||||
<note>Label for filter for seeing posts and replies (instead of only posts).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private" xml:space="preserve">
|
||||
<source>Private</source>
|
||||
<note>Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Private Key" xml:space="preserve">
|
||||
<source>Private Key</source>
|
||||
<note>Label to indicate that the text below is the user's private key used by only the user themself as a secret to login to access their account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PrivateKey" xml:space="preserve">
|
||||
<source>PrivateKey</source>
|
||||
<note>Title of the secure field that holds the user's private key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile" xml:space="preserve">
|
||||
<source>Profile</source>
|
||||
<note>Sidebar menu label for Profile view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile Picture" xml:space="preserve">
|
||||
<source>Profile Picture</source>
|
||||
<note>Label for Profile Picture section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Account ID" xml:space="preserve">
|
||||
<source>Public Account ID</source>
|
||||
<note>Section title for the user's public account ID.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Key" xml:space="preserve">
|
||||
<source>Public Key</source>
|
||||
<note>Label indicating that the text is a user's public account key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public Key?" xml:space="preserve">
|
||||
<source>Public Key?</source>
|
||||
<note>Prompt to ask user if the key they entered is a public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Public key" xml:space="preserve">
|
||||
<source>Public key</source>
|
||||
<note>Label indicating that the text is a user's public account key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reactions" xml:space="preserve">
|
||||
<source>Reactions</source>
|
||||
<note>Navigation bar title for Reactions view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recommended Relays" xml:space="preserve">
|
||||
<source>Recommended Relays</source>
|
||||
<note>Section title for recommend relay servers that could be added as part of configuration</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Relay" xml:space="preserve">
|
||||
<source>Relay</source>
|
||||
<note>Text field for relay server. Used for testing purposes.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Relays" xml:space="preserve">
|
||||
<source>Relays</source>
|
||||
<note>Sidebar menu label for Relay servers view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply to self" xml:space="preserve">
|
||||
<source>Reply to self</source>
|
||||
<note>Label to indicate that the user is replying to themself.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Replying to %@ & %@" xml:space="preserve">
|
||||
<source>Replying to %1$@ & %2$@</source>
|
||||
<note>Label to indicate that the user is replying to 2 users.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Replying to:" xml:space="preserve">
|
||||
<source>Replying to:</source>
|
||||
<note>Indicating that the user is replying to the following listed people.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Repost" xml:space="preserve">
|
||||
<source>Repost</source>
|
||||
<note>Button to confirm reposting a post.
|
||||
Title of alert for confirming to repost a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reposted" xml:space="preserve">
|
||||
<source>Reposted</source>
|
||||
<note>Text indicating that the post was reposted (i.e. re-shared).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reset" xml:space="preserve">
|
||||
<source>Reset</source>
|
||||
<note>Section title for resetting the user</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>Button to retry completing account creation after an error occurred.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="River" xml:space="preserve">
|
||||
<source>River</source>
|
||||
<note>Dropdown option label for Lightning wallet, River</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Satoshi Nakamoto" xml:space="preserve">
|
||||
<source>Satoshi Nakamoto</source>
|
||||
<note>Name of Bitcoin creator(s).</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save" xml:space="preserve">
|
||||
<source>Save</source>
|
||||
<note>Button for saving profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save Image" xml:space="preserve">
|
||||
<source>Save Image</source>
|
||||
<note>Context menu option to save an image.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search hashtag: #%@" xml:space="preserve">
|
||||
<source>Search hashtag: #%@</source>
|
||||
<note>Navigation link to search hashtag.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search..." xml:space="preserve">
|
||||
<source>Search...</source>
|
||||
<note>Placeholder text to prompt entry of search query.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secret Account Login Key" xml:space="preserve">
|
||||
<source>Secret Account Login Key</source>
|
||||
<note>Section title for user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select a Lightning wallet" xml:space="preserve">
|
||||
<source>Select a Lightning wallet</source>
|
||||
<note>Title of section for selecting a Lightning wallet to pay a Lightning invoice.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Select default wallet" xml:space="preserve">
|
||||
<source>Select default wallet</source>
|
||||
<note>Prompt selection of user's default wallet</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send a message to start the conversation..." xml:space="preserve">
|
||||
<source>Send a message to start the conversation...</source>
|
||||
<note>Text prompt for user to send a message to the other user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Settings" xml:space="preserve">
|
||||
<source>Settings</source>
|
||||
<note>Navigation title for Settings view.
|
||||
Sidebar menu label for accessing the app settings</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share" xml:space="preserve">
|
||||
<source>Share</source>
|
||||
<note>Button to share an image.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show" xml:space="preserve">
|
||||
<source>Show</source>
|
||||
<note>Toggle to show or hide user's secret account login key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show wallet selector" xml:space="preserve">
|
||||
<source>Show wallet selector</source>
|
||||
<note>Toggle to show or hide selection of wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Sign out" xml:space="preserve">
|
||||
<source>Sign out</source>
|
||||
<note>Sidebar menu label to sign out of the account.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Strike" xml:space="preserve">
|
||||
<source>Strike</source>
|
||||
<note>Dropdown option label for Lightning wallet, Strike.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective." xml:space="preserve">
|
||||
<source>This is a public key, you will not be able to make posts or interact in any way. This is used for viewing accounts from their perspective.</source>
|
||||
<note>Warning that the inputted account key is a public key and the result of what happens because of it.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key." xml:space="preserve">
|
||||
<source>This is an old-style nostr key. We're not sure if it's a pubkey or private key. Please toggle the button below if this a public key.</source>
|
||||
<note>Warning that the inputted account key for login is an old-style and asking user to verify if it is a public key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is your account ID, you can give this to your friends so that they can follow you. Click to copy." xml:space="preserve">
|
||||
<source>This is your account ID, you can give this to your friends so that they can follow you. Click to copy.</source>
|
||||
<note>Label to describe that a public key is the user's account ID and what they can do with it.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!" xml:space="preserve">
|
||||
<source>This is your secret account key. You need this to access your account. Don't share this with anyone! Save it in a password manager and keep it safe!</source>
|
||||
<note>Label to describe that a private key is the user's secret account key and what they should do with it.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Thread" xml:space="preserve">
|
||||
<source>Thread</source>
|
||||
<note>Navigation bar title for note thread.
|
||||
Navigation bar title for threaded event detail view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Type your post here..." xml:space="preserve">
|
||||
<source>Type your post here...</source>
|
||||
<note>Text box prompt to ask user to type their post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollow" xml:space="preserve">
|
||||
<source>Unfollow</source>
|
||||
<note>Button to unfollow a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollowing" xml:space="preserve">
|
||||
<source>Unfollowing</source>
|
||||
<note>Text to indicate that the button next to it is in a state that indicates that it is in the process of unfollowing a profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollowing..." xml:space="preserve">
|
||||
<source>Unfollowing...</source>
|
||||
<note>Label to indicate that the user is in the process of unfollowing another user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfollows" xml:space="preserve">
|
||||
<source>Unfollows</source>
|
||||
<note>Text to indicate that the button next to it is in a state that will unfollow a profile when tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Username" xml:space="preserve">
|
||||
<source>Username</source>
|
||||
<note>Label for Username section of user profile form.
|
||||
Label to prompt username entry.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wallet" xml:space="preserve">
|
||||
<source>Wallet</source>
|
||||
<note>Sidebar menu label for Wallet view.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wallet Of Satoshi" xml:space="preserve">
|
||||
<source>Wallet Of Satoshi</source>
|
||||
<note>Dropdown option label for Lightning wallet, Wallet Of Satoshi.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wallet Selector" xml:space="preserve">
|
||||
<source>Wallet Selector</source>
|
||||
<note>Section title for selection of wallet.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Website" xml:space="preserve">
|
||||
<source>Website</source>
|
||||
<note>Label for Website section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome to the social network %@ control." xml:space="preserve">
|
||||
<source>Welcome to the social network %@ control.</source>
|
||||
<note>Welcoming message to the reader. The variable is 'you', the reader.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome, %@!" xml:space="preserve">
|
||||
<source>Welcome, %@!</source>
|
||||
<note>Text to welcome user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your Name" xml:space="preserve">
|
||||
<source>Your Name</source>
|
||||
<note>Label for Your Name section of user profile form.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Zebedee" xml:space="preserve">
|
||||
<source>Zebedee</source>
|
||||
<note>Dropdown option label for Lightning wallet, Zebedee.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Zeus LN" xml:space="preserve">
|
||||
<source>Zeus LN</source>
|
||||
<note>Dropdown option label for Lightning wallet, Zeus LN.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="collapsed_event_view_other_notes" translate="no" xml:space="preserve">
|
||||
<source>collapsed_event_view_other_notes</source>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="followers_count" translate="no" xml:space="preserve">
|
||||
<source>followers_count</source>
|
||||
<note>Part of a larger sentence to describe how many people are following a user. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="https://example.com/pic.jpg" xml:space="preserve">
|
||||
<source>https://example.com/pic.jpg</source>
|
||||
<note>Placeholder example text for profile picture URL.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="https://jb55.com" xml:space="preserve">
|
||||
<source>https://jb55.com</source>
|
||||
<note>Placeholder example text for website URL for user profile.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="jb55@jb55.com" xml:space="preserve">
|
||||
<source>jb55@jb55.com</source>
|
||||
<note>Placeholder example text for identifier used for NIP-05 verification.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="none" xml:space="preserve">
|
||||
<source>none</source>
|
||||
<note>No search results.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="now" xml:space="preserve">
|
||||
<source>now</source>
|
||||
<note>String indicating that a given timestamp just occurred</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="nsec1..." xml:space="preserve">
|
||||
<source>nsec1...</source>
|
||||
<note>Prompt for user to enter in an account key to login. This text shows the characters the key could start with if it was a private key.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="optional" xml:space="preserve">
|
||||
<source>optional</source>
|
||||
<note>Label indicating that a form input is optional.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="reactions_count" translate="no" xml:space="preserve">
|
||||
<source>reactions_count</source>
|
||||
<note>Part of a larger sentence to describe how many reactions there are on a post. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="relays_count" translate="no" xml:space="preserve">
|
||||
<source>relays_count</source>
|
||||
<note>Part of a larger sentence to describe how many relay servers a user is connected. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="replying_to_one_and_others" translate="no" xml:space="preserve">
|
||||
<source>replying_to_one_and_others</source>
|
||||
<note>Label to indicate that the user is replying to 1 user and others. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="replying_to_two_and_others" translate="no" xml:space="preserve">
|
||||
<source>replying_to_two_and_others</source>
|
||||
<note>Label to indicate that the user is replying to 2 users and others. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="reposts_count" translate="no" xml:space="preserve">
|
||||
<source>reposts_count</source>
|
||||
<note>Part of a larger sentence to describe how many reposts there are. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="satoshi" xml:space="preserve">
|
||||
<source>satoshi</source>
|
||||
<note>Example username of Bitcoin creator(s), Satoshi Nakamoto.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sats_count" translate="no" xml:space="preserve">
|
||||
<source>sats_count</source>
|
||||
<note>Amount of sats. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="tips_count" translate="no" xml:space="preserve">
|
||||
<source>tips_count</source>
|
||||
<note>Part of a larger sentence to describe how many tip payments there are on a post. (Key in .stringsdict)</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="u{00A0}" xml:space="preserve">
|
||||
<source>u{00A0}</source>
|
||||
<note>Non-breaking space character to fill in blank space next to event action button icons.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="wss://some.relay.com" xml:space="preserve">
|
||||
<source>wss://some.relay.com</source>
|
||||
<note>Placeholder example for relay server address.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="es-419" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>%d other note</source>
|
||||
<target>%d other note</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NOTES:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>%d other notes</source>
|
||||
<target>%d other notes</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/collapsed_event_view_other_notes:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>··· %#@NOTES@ ···</source>
|
||||
<target>··· %#@NOTES@ ···</target>
|
||||
<note>Text to indicate that the thread was collapsed and that there are other notes to view if tapped.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/followers_count:dict/FOLLOWERS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>Follower</source>
|
||||
<target>Follower</target>
|
||||
<note>Part of a larger sentence to describe how many people are following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/followers_count:dict/FOLLOWERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>Followers</source>
|
||||
<target>Followers</target>
|
||||
<note>Part of a larger sentence to describe how many people are following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/followers_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>%#@FOLLOWERS@</source>
|
||||
<target>%#@FOLLOWERS@</target>
|
||||
<note>Part of a larger sentence to describe how many people are following a user.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/reactions_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>%#@REACTIONS@</source>
|
||||
<target>%#@REACTIONS@</target>
|
||||
<note>Part of a larger sentence to describe how many reactions there are on a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/reactions_count:dict/REACTIONS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>Reaction</source>
|
||||
<target>Reaction</target>
|
||||
<note>Part of a larger sentence to describe how many reactions there are on a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/reactions_count:dict/REACTIONS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>Reactions</source>
|
||||
<target>Reactions</target>
|
||||
<note>Part of a larger sentence to describe how many reactions there are on a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/relays_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>%#@RELAYS@</source>
|
||||
<target>%#@RELAYS@</target>
|
||||
<note>Part of a larger sentence to describe how many relay servers a user is connected.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/relays_count:dict/RELAYS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>Relay</source>
|
||||
<target>Relay</target>
|
||||
<note>Part of a larger sentence to describe how many relay servers a user is connected.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/relays_count:dict/RELAYS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>Relays</source>
|
||||
<target>Relays</target>
|
||||
<note>Part of a larger sentence to describe how many relay servers a user is connected.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>Replying to %@%#@OTHERS@</source>
|
||||
<target>Replying to %@%#@OTHERS@</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source> & %d other</source>
|
||||
<target> & %d other</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source> & %d others</source>
|
||||
<target> & %d others</target>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_one_and_others:dict/OTHERS:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source/>
|
||||
<target/>
|
||||
<note>Label to indicate that the user is replying to 1 user and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>Replying to %@, %@%#@OTHERS@</source>
|
||||
<target>Replying to %@, %@%#@OTHERS@</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source> & %d other</source>
|
||||
<target> & %d other</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source> & %d others</source>
|
||||
<target> & %d others</target>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/replying_to_two_and_others:dict/OTHERS:dict/zero:dict/:string" xml:space="preserve">
|
||||
<source/>
|
||||
<target/>
|
||||
<note>Label to indicate that the user is replying to 2 users and others.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/reposts_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>%#@REPOSTS@</source>
|
||||
<target>%#@REPOSTS@</target>
|
||||
<note>Part of a larger sentence to describe how many reposts there are.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/reposts_count:dict/REPOSTS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>Repost</source>
|
||||
<target>Repost</target>
|
||||
<note>Part of a larger sentence to describe how many reposts there are.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/reposts_count:dict/REPOSTS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>Reposts</source>
|
||||
<target>Reposts</target>
|
||||
<note>Part of a larger sentence to describe how many reposts there are.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/sats_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>%1$#@SATS@</source>
|
||||
<target>%1$#@SATS@</target>
|
||||
<note>Amount of sats.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/sats_count:dict/SATS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>%2$@ sat</source>
|
||||
<target>%2$@ sat</target>
|
||||
<note>Amount of sats.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/sats_count:dict/SATS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>%2$@ sats</source>
|
||||
<target>%2$@ sats</target>
|
||||
<note>Amount of sats.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/tips_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||
<source>%#@TIPS@</source>
|
||||
<target>%#@TIPS@</target>
|
||||
<note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/tips_count:dict/TIPS:dict/one:dict/:string" xml:space="preserve">
|
||||
<source>Tip</source>
|
||||
<target>Tip</target>
|
||||
<note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="/tips_count:dict/TIPS:dict/other:dict/:string" xml:space="preserve">
|
||||
<source>Tips</source>
|
||||
<target>Tips</target>
|
||||
<note>Part of a larger sentence to describe how many tip payments there are on a post.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "Damus";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "damus";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "\"Granting Damus access to your photo library allows you to save photos.";
|
||||
@@ -0,0 +1,154 @@
|
||||
<?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>
|
||||
12
damus Localizations/es-419.xcloc/contents.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"developmentRegion" : "en-US",
|
||||
"project" : "damus.xcodeproj",
|
||||
"targetLocale" : "es-419",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "14C18",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "14.2"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -12,8 +12,6 @@
|
||||
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
|
||||
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
||||
3AB18056296375CA00FD1BD8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AB18052296375CA00FD1BD8 /* InfoPlist.strings */; };
|
||||
3AB18057296375CA00FD1BD8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3AB18054296375CA00FD1BD8 /* Localizable.strings */; };
|
||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
||||
@@ -53,6 +51,7 @@
|
||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA328296DEE006E126D /* SearchModel.swift */; };
|
||||
4C363AA828297703006E126D /* InsertSort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA728297703006E126D /* InsertSort.swift */; };
|
||||
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D322960DB0500558C0F /* Markdown.swift */; };
|
||||
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D3629637E0500558C0F /* PreviewCache.swift */; };
|
||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79A28306D7B00E1F516 /* Contacts.swift */; };
|
||||
4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79C2833036D00E1F516 /* FollowingView.swift */; };
|
||||
4C3AC79F2833115300E1F516 /* FollowButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79E2833115300E1F516 /* FollowButtonView.swift */; };
|
||||
@@ -116,6 +115,13 @@
|
||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9DB280C38C000D9BBE8 /* Profiles.swift */; };
|
||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */; };
|
||||
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */; };
|
||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838529656C8B00DC99E7 /* NIP05.swift */; };
|
||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */; };
|
||||
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838A296F6E1E00DC99E7 /* NIP05Badge.swift */; };
|
||||
4CB8838D296F710400DC99E7 /* Reposted.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838C296F710400DC99E7 /* Reposted.swift */; };
|
||||
4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838E296F781C00DC99E7 /* ReactionsView.swift */; };
|
||||
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88392296F798300DC99E7 /* ReactionsModel.swift */; };
|
||||
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB88395296F7F8B00DC99E7 /* ReactionView.swift */; };
|
||||
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
||||
4CE4F8CD281352B30009DFBB /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F8CC281352B30009DFBB /* Notifications.swift */; };
|
||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */; };
|
||||
@@ -138,9 +144,13 @@
|
||||
4CEE2AF9280B2EAC00AB5EEF /* PowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */; };
|
||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */; };
|
||||
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE60CDC295E1C5E00105A1F /* Wallet.swift */; };
|
||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
|
||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
|
||||
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
|
||||
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9609F057296E220800069BF3 /* BannerImageView.swift */; };
|
||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; };
|
||||
E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; };
|
||||
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
@@ -167,13 +177,8 @@
|
||||
3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTimelineView.swift; sourceTree = "<group>"; };
|
||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Constants.swift; path = damus/Util/Constants.swift; sourceTree = SOURCE_ROOT; };
|
||||
31D2E846295218AF006D67F8 /* Shimmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shimmer.swift; sourceTree = "<group>"; };
|
||||
3A4325A92961E11400BFCD9D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
3AB1803D29636FB100FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
3AB18058296377E500FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3AB18059296377E700FD1BD8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3AB1805A2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3AB1805B2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
3AB1805C2963EF7E00FD1BD8 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = fr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
3A2B8B0A296A8982009CC16D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "en-US"; path = "en-US.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||
3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeAgoTests.swift; sourceTree = "<group>"; };
|
||||
4C06670028FC7C5900038D2A /* RelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayView.swift; sourceTree = "<group>"; };
|
||||
4C06670528FCB08600038D2A /* ImageCarousel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCarousel.swift; sourceTree = "<group>"; };
|
||||
@@ -215,6 +220,7 @@
|
||||
4C363AA328296DEE006E126D /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; };
|
||||
4C363AA728297703006E126D /* InsertSort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertSort.swift; sourceTree = "<group>"; };
|
||||
4C3A1D322960DB0500558C0F /* Markdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Markdown.swift; sourceTree = "<group>"; };
|
||||
4C3A1D3629637E0500558C0F /* PreviewCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCache.swift; sourceTree = "<group>"; };
|
||||
4C3AC79A28306D7B00E1F516 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = "<group>"; };
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowButtonView.swift; sourceTree = "<group>"; };
|
||||
@@ -308,6 +314,13 @@
|
||||
4CACA9DB280C38C000D9BBE8 /* Profiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profiles.swift; sourceTree = "<group>"; };
|
||||
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendedRelayView.swift; sourceTree = "<group>"; };
|
||||
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRelaysView.swift; sourceTree = "<group>"; };
|
||||
4CB8838529656C8B00DC99E7 /* NIP05.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP05.swift; sourceTree = "<group>"; };
|
||||
4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailBar.swift; sourceTree = "<group>"; };
|
||||
4CB8838A296F6E1E00DC99E7 /* NIP05Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP05Badge.swift; sourceTree = "<group>"; };
|
||||
4CB8838C296F710400DC99E7 /* Reposted.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reposted.swift; sourceTree = "<group>"; };
|
||||
4CB8838E296F781C00DC99E7 /* ReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsView.swift; sourceTree = "<group>"; };
|
||||
4CB88392296F798300DC99E7 /* ReactionsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsModel.swift; sourceTree = "<group>"; };
|
||||
4CB88395296F7F8B00DC99E7 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = "<group>"; };
|
||||
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
|
||||
4CE4F8CC281352B30009DFBB /* Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
|
||||
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
|
||||
@@ -333,8 +346,12 @@
|
||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowView.swift; sourceTree = "<group>"; };
|
||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventActionBar.swift; sourceTree = "<group>"; };
|
||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
|
||||
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
|
||||
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
9609F057296E220800069BF3 /* BannerImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannerImageView.swift; sourceTree = "<group>"; };
|
||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = "<group>"; };
|
||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = "<group>"; };
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadV2View.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -462,6 +479,7 @@
|
||||
4C99737A28C92A9200E53835 /* ChatroomMetadata.swift */,
|
||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */,
|
||||
4FE60CDC295E1C5E00105A1F /* Wallet.swift */,
|
||||
4CB88392296F798300DC99E7 /* ReactionsModel.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@@ -469,49 +487,53 @@
|
||||
4C75EFA227FA576C0006080F /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3169CAE4294E699400EE4006 /* Empty Views */,
|
||||
4C75EFA327FA577B0006080F /* PostView.swift */,
|
||||
4C75EFAC28049CFB0006080F /* PostButton.swift */,
|
||||
4C75EFB82804A2740006080F /* EventView.swift */,
|
||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
|
||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
|
||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
|
||||
4CB88394296F7F8100DC99E7 /* Reactions */,
|
||||
4CB88387296AF97C00DC99E7 /* ActionBar */,
|
||||
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
|
||||
4C363A8728236948006E126D /* BlocksView.swift */,
|
||||
4C285C8128385570008A31F1 /* CarouselView.swift */,
|
||||
4C0A3F8B280F5FCA000448DE /* ChatroomView.swift */,
|
||||
4C0A3F90280F6528000448DE /* ChatView.swift */,
|
||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||
4C363A8728236948006E126D /* BlocksView.swift */,
|
||||
4C363A8928236B57006E126D /* MentionView.swift */,
|
||||
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
||||
4C363A8D28236FE4006E126D /* NoteContentView.swift */,
|
||||
4C363AA128296A7E006E126D /* SearchView.swift */,
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
||||
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
|
||||
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
||||
4C3AC7A628369BA200E1F516 /* SearchHomeView.swift */,
|
||||
4C285C8128385570008A31F1 /* CarouselView.swift */,
|
||||
4C285C8328385690008A31F1 /* CreateAccountView.swift */,
|
||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
|
||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
||||
4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */,
|
||||
4CE4F9DD2852768D00C00DD9 /* ConfigView.swift */,
|
||||
4CE4F9E228528C5200C00DD9 /* AddRelayView.swift */,
|
||||
4C285C8328385690008A31F1 /* CreateAccountView.swift */,
|
||||
4C64987B286D03E000EAE2B3 /* DirectMessagesView.swift */,
|
||||
4C216F31286E388800040376 /* DMChatView.swift */,
|
||||
4C216F33286F5ACD00040376 /* DMView.swift */,
|
||||
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
||||
E990020E2955F837003BBC5A /* EditMetadataView.swift */,
|
||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
||||
3169CAE4294E699400EE4006 /* Empty Views */,
|
||||
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */,
|
||||
4C75EFB82804A2740006080F /* EventView.swift */,
|
||||
4C3AC79E2833115300E1F516 /* FollowButtonView.swift */,
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */,
|
||||
4C90BD17283A9EE5008EE7EF /* LoginView.swift */,
|
||||
4C363A8928236B57006E126D /* MentionView.swift */,
|
||||
4C363A8D28236FE4006E126D /* NoteContentView.swift */,
|
||||
4C75EFAC28049CFB0006080F /* PostButton.swift */,
|
||||
4C75EFA327FA577B0006080F /* PostView.swift */,
|
||||
4CEE2AF8280B2EAC00AB5EEF /* PowView.swift */,
|
||||
4CEE2AF6280B2DEA00AB5EEF /* ProfileName.swift */,
|
||||
4C285C892838B985008A31F1 /* ProfilePictureSelector.swift */,
|
||||
4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */,
|
||||
4C8682862814DE470026224F /* ProfileView.swift */,
|
||||
4C3AC7A42836987600E1F516 /* MainTabView.swift */,
|
||||
4C363A8B28236B92006E126D /* PubkeyView.swift */,
|
||||
4CB55EF2295E5D59007FD187 /* RecommendedRelayView.swift */,
|
||||
4C06670028FC7C5900038D2A /* RelayView.swift */,
|
||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */,
|
||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */,
|
||||
4C285C8D28399BFD008A31F1 /* SaveKeysView.swift */,
|
||||
4C3AC7A628369BA200E1F516 /* SearchHomeView.swift */,
|
||||
4C5C7E69284EDE2E00A22DF5 /* SearchResultsView.swift */,
|
||||
4C363AA128296A7E006E126D /* SearchView.swift */,
|
||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
|
||||
4C3AC7A02835A81400E1F516 /* SetupView.swift */,
|
||||
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
|
||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
|
||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
|
||||
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
|
||||
647D9A8C2968520300A295DE /* SideMenuView.swift */,
|
||||
9609F057296E220800069BF3 /* BannerImageView.swift */,
|
||||
4CB8838E296F781C00DC99E7 /* ReactionsView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@@ -550,10 +572,30 @@
|
||||
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */,
|
||||
3169CAEC294FCCFC00EE4006 /* Constants.swift */,
|
||||
3165648A295B70D500C64604 /* LinkView.swift */,
|
||||
4C3A1D3629637E0500558C0F /* PreviewCache.swift */,
|
||||
64FBD06E296255C400D9D3B2 /* Theme.swift */,
|
||||
4CB8838529656C8B00DC99E7 /* NIP05.swift */,
|
||||
);
|
||||
path = Util;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4CB88387296AF97C00DC99E7 /* ActionBar */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4CEE2B01280B39E800AB5EEF /* EventActionBar.swift */,
|
||||
4CB88388296AF99A00DC99E7 /* EventDetailBar.swift */,
|
||||
);
|
||||
path = ActionBar;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4CB88394296F7F8100DC99E7 /* Reactions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4CB88395296F7F8B00DC99E7 /* ReactionView.swift */,
|
||||
);
|
||||
path = Reactions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4CE4F9DF285287A000C00DD9 /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -563,6 +605,8 @@
|
||||
4C06670528FCB08600038D2A /* ImageCarousel.swift */,
|
||||
4C3EA67C28FFBBA200C48A62 /* InvoicesView.swift */,
|
||||
4C3EA67E28FFC01D00C48A62 /* InvoiceView.swift */,
|
||||
4CB8838A296F6E1E00DC99E7 /* NIP05Badge.swift */,
|
||||
4CB8838C296F710400DC99E7 /* Reposted.swift */,
|
||||
);
|
||||
path = Components;
|
||||
sourceTree = "<group>";
|
||||
@@ -603,8 +647,6 @@
|
||||
4CE6DEE827F7A08100C66700 /* ContentView.swift */,
|
||||
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
|
||||
4CE6DEEC27F7A08200C66700 /* Preview Content */,
|
||||
3AB18052296375CA00FD1BD8 /* InfoPlist.strings */,
|
||||
3AB18054296375CA00FD1BD8 /* Localizable.strings */,
|
||||
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
|
||||
);
|
||||
path = damus;
|
||||
@@ -621,6 +663,7 @@
|
||||
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */,
|
||||
4C90BD1B283AC38E008EE7EF /* Bech32Tests.swift */,
|
||||
4C363A9F2828A8DD006E126D /* LikeTests.swift */,
|
||||
4C363A9D2828A822006E126D /* ReplyTests.swift */,
|
||||
@@ -736,20 +779,18 @@
|
||||
};
|
||||
buildConfigurationList = 4CE6DEDE27F7A08100C66700 /* Build configuration list for PBXProject "damus" */;
|
||||
compatibilityVersion = "Xcode 13.0";
|
||||
developmentRegion = en;
|
||||
developmentRegion = "en-US";
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
es,
|
||||
fr,
|
||||
"es-419",
|
||||
"en-US",
|
||||
);
|
||||
mainGroup = 4CE6DEDA27F7A08100C66700;
|
||||
packageReferences = (
|
||||
4CE6DF1027F7A2B300C66700 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||
4C64987F286E0EE300EAE2B3 /* XCRemoteSwiftPackageReference "secp256k1" */,
|
||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||
3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */,
|
||||
6C7DE41D2955169800E66263 /* XCRemoteSwiftPackageReference "Vault" */,
|
||||
);
|
||||
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
||||
@@ -768,9 +809,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3AB18057296375CA00FD1BD8 /* Localizable.strings in Resources */,
|
||||
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */,
|
||||
3AB18056296375CA00FD1BD8 /* InfoPlist.strings in Resources */,
|
||||
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */,
|
||||
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */,
|
||||
);
|
||||
@@ -810,6 +849,7 @@
|
||||
4C363A8C28236B92006E126D /* PubkeyView.swift in Sources */,
|
||||
4C5C7E68284ED36500A22DF5 /* SearchHomeModel.swift in Sources */,
|
||||
4C75EFB728049D990006080F /* RelayPool.swift in Sources */,
|
||||
4CB8838D296F710400DC99E7 /* Reposted.swift in Sources */,
|
||||
4C3EA67728FF7A9800C48A62 /* talstr.c in Sources */,
|
||||
4CE6DEE927F7A08100C66700 /* ContentView.swift in Sources */,
|
||||
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
||||
@@ -824,12 +864,14 @@
|
||||
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,
|
||||
4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */,
|
||||
4C477C9E282C3A4800033AA3 /* TipCounter.swift in Sources */,
|
||||
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */,
|
||||
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
|
||||
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
|
||||
4C216F382871EDE300040376 /* DirectMessageModel.swift in Sources */,
|
||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
||||
4CE4F9DE2852768D00C00DD9 /* ConfigView.swift in Sources */,
|
||||
4C285C8E28399BFE008A31F1 /* SaveKeysView.swift in Sources */,
|
||||
4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */,
|
||||
4C649844285A952100EAE2B3 /* LocalUserConfig.swift in Sources */,
|
||||
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
|
||||
@@ -838,6 +880,7 @@
|
||||
4C363A9A28283854006E126D /* Reply.swift in Sources */,
|
||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */,
|
||||
4C90BD18283A9EE5008EE7EF /* LoginView.swift in Sources */,
|
||||
4CB8838B296F6E1E00DC99E7 /* NIP05Badge.swift in Sources */,
|
||||
4C3EA66828FF5F9900C48A62 /* hex.c in Sources */,
|
||||
E9E4ED0B295867B900DD7078 /* ThreadV2View.swift in Sources */,
|
||||
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
|
||||
@@ -854,14 +897,17 @@
|
||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */,
|
||||
4C3EA64928FF597700C48A62 /* bech32.c in Sources */,
|
||||
4C90BD162839DB54008EE7EF /* NostrMetadata.swift in Sources */,
|
||||
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */,
|
||||
4C3EA67528FF7A5A00C48A62 /* take.c in Sources */,
|
||||
4C3AC7A12835A81400E1F516 /* SetupView.swift in Sources */,
|
||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */,
|
||||
4C285C8C28398BC7008A31F1 /* Keys.swift in Sources */,
|
||||
4CACA9DC280C38C000D9BBE8 /* Profiles.swift in Sources */,
|
||||
4C633352283D419F00B1C9C3 /* SignalModel.swift in Sources */,
|
||||
9609F058296E220800069BF3 /* BannerImageView.swift in Sources */,
|
||||
4C363A94282704FA006E126D /* Post.swift in Sources */,
|
||||
4C216F32286E388800040376 /* DMChatView.swift in Sources */,
|
||||
4C3EA67928FF7ABF00C48A62 /* list.c in Sources */,
|
||||
@@ -875,8 +921,11 @@
|
||||
4C3EA66528FF5F6800C48A62 /* mem.c in Sources */,
|
||||
4C3EA64128FF553900C48A62 /* hash_u5.c in Sources */,
|
||||
4C3EA64F28FF59F200C48A62 /* tal.c in Sources */,
|
||||
4CB88393296F798300DC99E7 /* ReactionsModel.swift in Sources */,
|
||||
4CB88396296F7F8B00DC99E7 /* ReactionView.swift in Sources */,
|
||||
4C8682872814DE470026224F /* ProfileView.swift in Sources */,
|
||||
4C5F9114283D694D0052CD1C /* FollowTarget.swift in Sources */,
|
||||
4CB8838629656C8B00DC99E7 /* NIP05.swift in Sources */,
|
||||
4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */,
|
||||
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */,
|
||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */,
|
||||
@@ -896,6 +945,7 @@
|
||||
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
||||
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
|
||||
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
||||
4C3EA63D28FF52D600C48A62 /* bolt11.c in Sources */,
|
||||
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
|
||||
@@ -924,6 +974,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
|
||||
DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */,
|
||||
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
|
||||
4C363A9E2828A822006E126D /* ReplyTests.swift in Sources */,
|
||||
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
||||
@@ -960,31 +1011,12 @@
|
||||
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3A4325A92961E11400BFCD9D /* en */,
|
||||
3AB1803D29636FB100FD1BD8 /* es */,
|
||||
3AB1805C2963EF7E00FD1BD8 /* fr */,
|
||||
3A5C4575296A879E0032D398 /* es-419 */,
|
||||
3A2B8B0A296A8982009CC16D /* en-US */,
|
||||
);
|
||||
name = Localizable.stringsdict;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3AB18052296375CA00FD1BD8 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3AB18058296377E500FD1BD8 /* es */,
|
||||
3AB1805A2963EF7E00FD1BD8 /* fr */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3AB18054296375CA00FD1BD8 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
3AB18059296377E700FD1BD8 /* es */,
|
||||
3AB1805B2963EF7E00FD1BD8 /* fr */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -1046,6 +1078,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1101,6 +1134,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -1114,7 +1148,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1154,7 +1188,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = XK7H4JAB3D;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1303,14 +1337,6 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
3169CAE9294FCABA00EE4006 /* XCRemoteSwiftPackageReference "Shimmer" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/joshuajhomann/Shimmer";
|
||||
requirement = {
|
||||
branch = master;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/onevcat/Kingfisher";
|
||||
|
||||
@@ -17,15 +17,6 @@
|
||||
"revision" : "40b4b38b3b1c83f7088c76189a742870e0ca06a9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "shimmer",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/joshuajhomann/Shimmer",
|
||||
"state" : {
|
||||
"branch" : "master",
|
||||
"revision" : "2fde687b3f1d9c5409c53da095d3686361e41343"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "starscream",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
||||
6
damus/Assets.xcassets/Colors/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0x00",
|
||||
"red" : "0x00"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0x4D",
|
||||
"red" : "0x4B"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x1C",
|
||||
"red" : "0x1C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x1E",
|
||||
"green" : "0x1C",
|
||||
"red" : "0x1C"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4F",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x66"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x4F",
|
||||
"green" : "0xC3",
|
||||
"red" : "0x66"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xF4",
|
||||
"green" : "0xEE",
|
||||
"red" : "0xEE"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5F",
|
||||
"green" : "0x5F",
|
||||
"red" : "0x5F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x5F",
|
||||
"green" : "0x5F",
|
||||
"red" : "0x5F"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xC5",
|
||||
"green" : "0x43",
|
||||
"red" : "0xCC"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xFF",
|
||||
"green" : "0xFF",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
damus/Assets.xcassets/Profile/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
21
damus/Assets.xcassets/Profile/ic-copy.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-copy.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-copy.imageset/ic-copy.png
vendored
Normal file
|
After Width: | Height: | Size: 354 B |
21
damus/Assets.xcassets/Profile/ic-key.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-key.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-key.imageset/ic-key.png
vendored
Normal file
|
After Width: | Height: | Size: 400 B |
52
damus/Assets.xcassets/Profile/ic-message.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-message-black.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "ic-message-white 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-message.imageset/ic-message-black.png
vendored
Normal file
|
After Width: | Height: | Size: 321 B |
BIN
damus/Assets.xcassets/Profile/ic-message.imageset/ic-message-white 1.png
vendored
Normal file
|
After Width: | Height: | Size: 341 B |
21
damus/Assets.xcassets/Profile/ic-nipverified.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-nipverified.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-nipverified.imageset/ic-nipverified.png
vendored
Normal file
|
After Width: | Height: | Size: 950 B |
21
damus/Assets.xcassets/Profile/ic-qr.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-qr.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/ic-qr.imageset/ic-qr.png
vendored
Normal file
|
After Width: | Height: | Size: 252 B |
21
damus/Assets.xcassets/Profile/profile-banner.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "profile-banner.jpeg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/Profile/profile-banner.imageset/profile-banner.jpeg
vendored
Normal file
|
After Width: | Height: | Size: 290 KiB |
23
damus/Assets.xcassets/damus-home.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "damus-home@1x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "damus-home@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "damus-home@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/damus-home.imageset/damus-home@1x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
damus/Assets.xcassets/damus-home.imageset/damus-home@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
damus/Assets.xcassets/damus-home.imageset/damus-home@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
21
damus/Assets.xcassets/ic-lightning.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-lightning.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/ic-lightning.imageset/ic-lightning.png
vendored
Normal file
|
After Width: | Height: | Size: 458 B |
21
damus/Assets.xcassets/ic-tick.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic-tick.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/ic-tick.imageset/ic-tick.png
vendored
Normal file
|
After Width: | Height: | Size: 671 B |
15
damus/Assets.xcassets/shaka-full.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "shaka-full.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
88
damus/Assets.xcassets/shaka-full.imageset/shaka-full.pdf
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.073975 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
1.295334 8.661732 m
|
||||
3.613694 8.367855 l
|
||||
4.475733 8.733568 5.268113 9.771931 5.474915 10.327032 c
|
||||
6.083156 11.959681 5.507567 14.604573 5.474915 15.061715 c
|
||||
5.448792 15.427428 6.008246 15.693006 6.291239 15.780080 c
|
||||
7.571236 15.858447 8.508359 14.876789 8.642253 13.984165 c
|
||||
8.740212 13.331103 8.576948 11.752880 8.381030 10.849482 c
|
||||
8.979668 10.936556 10.980525 10.901726 11.868687 10.849482 c
|
||||
12.756847 10.797236 13.474895 10.196423 14.193260 9.412750 c
|
||||
14.767952 8.237244 13.953805 7.725680 13.474895 7.616838 c
|
||||
13.834077 7.257654 l
|
||||
14.781013 5.918882 13.649043 5.178749 13.115711 5.004600 c
|
||||
13.474895 4.743376 l
|
||||
14.487136 3.763786 13.246323 2.751544 13.017752 2.882155 c
|
||||
11.058574 3.176033 l
|
||||
15.499378 1.673996 l
|
||||
16.054478 0.400530 15.074889 0.073999 14.781013 0.073999 c
|
||||
8.576947 1.673996 l
|
||||
6.291239 1.673996 5.311650 1.869914 4.299407 2.163791 c
|
||||
4.157911 2.131138 3.659409 1.987464 2.797370 1.673996 c
|
||||
1.935332 1.360527 1.219143 2.087601 0.968804 2.490320 c
|
||||
-0.285071 4.083785 -0.467927 7.257655 1.295334 8.661732 c
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
1149
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 15.666626 15.710510 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000001239 00000 n
|
||||
0000001262 00000 n
|
||||
0000001435 00000 n
|
||||
0000001509 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
1568
|
||||
%%EOF
|
||||
15
damus/Assets.xcassets/shaka-line.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "shaka-line.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
||||
323
damus/Assets.xcassets/shaka-line.imageset/shaka-line.pdf
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 0.474731 -0.563965 cm
|
||||
1.000000 1.000000 1.000000 scn
|
||||
3.613694 9.332577 m
|
||||
3.553993 8.861599 l
|
||||
3.637261 8.851044 3.721838 8.862753 3.799107 8.895533 c
|
||||
3.613694 9.332577 l
|
||||
h
|
||||
1.295334 9.626453 m
|
||||
1.355035 10.097433 l
|
||||
1.227973 10.113539 1.099794 10.077623 0.999601 9.997839 c
|
||||
1.295334 9.626453 l
|
||||
h
|
||||
0.968804 3.455042 m
|
||||
1.372000 3.705677 l
|
||||
1.362764 3.720535 1.352713 3.734872 1.341894 3.748621 c
|
||||
0.968804 3.455042 l
|
||||
h
|
||||
4.299407 3.128512 m
|
||||
4.431771 3.584435 l
|
||||
4.353942 3.607030 4.271623 3.609325 4.192656 3.591103 c
|
||||
4.299407 3.128512 l
|
||||
h
|
||||
8.576947 2.638718 m
|
||||
8.695503 3.098424 l
|
||||
8.656776 3.108411 8.616942 3.113465 8.576947 3.113465 c
|
||||
8.576947 2.638718 l
|
||||
h
|
||||
14.781013 1.038721 m
|
||||
14.662457 0.579016 l
|
||||
14.701184 0.569027 14.741018 0.563974 14.781013 0.563974 c
|
||||
14.781013 1.038721 l
|
||||
h
|
||||
15.499378 2.638718 m
|
||||
15.934578 2.828420 l
|
||||
15.881091 2.951125 15.778289 3.045548 15.651489 3.088437 c
|
||||
15.499378 2.638718 l
|
||||
h
|
||||
11.058574 4.140755 m
|
||||
11.128998 4.610250 l
|
||||
10.885809 4.646729 10.655017 4.491467 10.597156 4.252461 c
|
||||
10.539293 4.013455 10.673516 3.769826 10.906463 3.691035 c
|
||||
11.058574 4.140755 l
|
||||
h
|
||||
13.017752 3.846877 m
|
||||
13.253292 4.259073 l
|
||||
13.202273 4.288227 13.146286 4.307655 13.088176 4.316372 c
|
||||
13.017752 3.846877 l
|
||||
h
|
||||
13.474895 5.708097 m
|
||||
13.805044 6.049252 l
|
||||
13.789093 6.064689 13.772079 6.078987 13.754128 6.092043 c
|
||||
13.474895 5.708097 l
|
||||
h
|
||||
13.115711 5.969321 m
|
||||
12.968349 6.420619 l
|
||||
12.798800 6.365256 12.674588 6.219535 12.646772 6.043359 c
|
||||
12.618958 5.867183 12.692234 5.690281 12.836478 5.585376 c
|
||||
13.115711 5.969321 l
|
||||
h
|
||||
13.834077 8.222376 m
|
||||
14.221668 8.496526 l
|
||||
14.206144 8.518474 14.188784 8.539063 14.169774 8.558073 c
|
||||
13.834077 8.222376 l
|
||||
h
|
||||
13.474895 8.581559 m
|
||||
13.369680 9.044500 l
|
||||
13.201114 9.006190 13.066693 8.879284 13.018762 8.713197 c
|
||||
12.970830 8.547110 13.016963 8.368095 13.139197 8.245862 c
|
||||
13.474895 8.581559 l
|
||||
h
|
||||
14.193260 10.377472 m
|
||||
14.619765 10.585986 l
|
||||
14.599768 10.626891 14.573989 10.664707 14.543221 10.698271 c
|
||||
14.193260 10.377472 l
|
||||
h
|
||||
8.381030 11.814203 m
|
||||
7.917068 11.914822 l
|
||||
7.884080 11.762714 7.927746 11.604099 8.033934 11.490305 c
|
||||
8.140121 11.376513 8.295343 11.321997 8.449365 11.344399 c
|
||||
8.381030 11.814203 l
|
||||
h
|
||||
8.642253 14.948887 m
|
||||
9.111748 15.019311 l
|
||||
8.642253 14.948887 l
|
||||
h
|
||||
6.291239 16.744801 m
|
||||
6.262227 17.218662 l
|
||||
6.224693 17.216364 6.187564 17.209614 6.151623 17.198555 c
|
||||
6.291239 16.744801 l
|
||||
h
|
||||
5.474915 16.026436 m
|
||||
5.948456 16.060261 l
|
||||
5.474915 16.026436 l
|
||||
h
|
||||
5.474915 11.291754 m
|
||||
5.030037 11.457493 l
|
||||
5.474915 11.291754 l
|
||||
h
|
||||
3.673396 9.803555 m
|
||||
1.355035 10.097433 l
|
||||
1.235632 9.155476 l
|
||||
3.553993 8.861599 l
|
||||
3.673396 9.803555 l
|
||||
h
|
||||
0.999601 9.997839 m
|
||||
-0.029049 9.178730 -0.454726 7.875908 -0.474048 6.619066 c
|
||||
-0.493367 5.362488 -0.110331 4.058727 0.595713 3.161463 c
|
||||
1.341894 3.748621 l
|
||||
0.794064 4.444821 0.458734 5.524729 0.475334 6.604470 c
|
||||
0.491930 7.683949 0.856455 8.670100 1.591066 9.255068 c
|
||||
0.999601 9.997839 l
|
||||
h
|
||||
0.565608 3.204407 m
|
||||
0.721970 2.952868 1.013515 2.611341 1.407507 2.372385 c
|
||||
1.811404 2.127421 2.357187 1.973489 2.959612 2.192553 c
|
||||
2.635129 3.084882 l
|
||||
2.375515 2.990478 2.132184 3.043347 1.899893 3.184233 c
|
||||
1.657696 3.331126 1.465977 3.554496 1.372000 3.705677 c
|
||||
0.565608 3.204407 l
|
||||
h
|
||||
2.959612 2.192553 m
|
||||
3.816493 2.504146 4.293336 2.639887 4.406158 2.665923 c
|
||||
4.192656 3.591103 l
|
||||
4.022485 3.551832 3.502325 3.400227 2.635129 3.084882 c
|
||||
2.959612 2.192553 l
|
||||
h
|
||||
4.167043 2.672591 m
|
||||
5.229115 2.364247 6.254152 2.163970 8.576947 2.163970 c
|
||||
8.576947 3.113465 l
|
||||
6.328326 3.113465 5.394184 3.305025 4.431771 3.584435 c
|
||||
4.167043 2.672591 l
|
||||
h
|
||||
8.458392 2.179011 m
|
||||
14.662457 0.579016 l
|
||||
14.899569 1.498427 l
|
||||
8.695503 3.098424 l
|
||||
8.458392 2.179011 l
|
||||
h
|
||||
14.781013 0.563974 m
|
||||
15.036198 0.563974 15.495326 0.684875 15.814721 1.047266 c
|
||||
16.180891 1.462728 16.264221 2.072176 15.934578 2.828420 c
|
||||
15.064179 2.449016 l
|
||||
15.289635 1.931793 15.160722 1.741243 15.102402 1.675073 c
|
||||
15.055794 1.622190 14.990156 1.579316 14.916806 1.549556 c
|
||||
14.881134 1.535082 14.847747 1.525430 14.820526 1.519657 c
|
||||
14.791491 1.513498 14.777695 1.513469 14.781013 1.513469 c
|
||||
14.781013 0.563974 l
|
||||
h
|
||||
15.651489 3.088437 m
|
||||
11.210685 4.590474 l
|
||||
10.906463 3.691035 l
|
||||
15.347267 2.188998 l
|
||||
15.651489 3.088437 l
|
||||
h
|
||||
10.988150 3.671260 m
|
||||
12.947328 3.377382 l
|
||||
13.088176 4.316372 l
|
||||
11.128998 4.610250 l
|
||||
10.988150 3.671260 l
|
||||
h
|
||||
12.782211 3.434681 m
|
||||
12.991495 3.315090 13.204453 3.370091 13.288217 3.396689 c
|
||||
13.400116 3.432221 13.506123 3.490767 13.598186 3.554502 c
|
||||
13.783985 3.683133 13.977411 3.877748 14.120350 4.119644 c
|
||||
14.264680 4.363894 14.369576 4.678114 14.335162 5.031647 c
|
||||
14.300108 5.391746 14.125634 5.739002 13.805044 6.049252 c
|
||||
13.144745 5.366943 l
|
||||
13.330275 5.187398 13.380290 5.040778 13.390134 4.939653 c
|
||||
13.400617 4.831963 13.370820 4.717613 13.302905 4.602680 c
|
||||
13.233600 4.485394 13.137231 4.390213 13.057724 4.335170 c
|
||||
13.017135 4.307070 12.996612 4.300308 13.000857 4.301657 c
|
||||
13.003194 4.302399 13.024761 4.309311 13.061064 4.310122 c
|
||||
13.095938 4.310902 13.170414 4.306433 13.253292 4.259073 c
|
||||
12.782211 3.434681 l
|
||||
h
|
||||
13.754128 6.092043 m
|
||||
13.394944 6.353267 l
|
||||
12.836478 5.585376 l
|
||||
13.195662 5.324152 l
|
||||
13.754128 6.092043 l
|
||||
h
|
||||
13.263074 5.518023 m
|
||||
13.593105 5.625790 14.123367 5.907292 14.433812 6.409482 c
|
||||
14.595931 6.671733 14.696482 6.993351 14.669847 7.364054 c
|
||||
14.643518 7.730516 14.495621 8.109214 14.221668 8.496526 c
|
||||
13.446486 7.948226 l
|
||||
13.646002 7.666152 13.711709 7.450294 13.722795 7.296009 c
|
||||
13.733575 7.145966 13.695351 7.020646 13.626177 6.908748 c
|
||||
13.474038 6.662641 13.171650 6.487002 12.968349 6.420619 c
|
||||
13.263074 5.518023 l
|
||||
h
|
||||
14.169774 8.558073 m
|
||||
13.810592 8.917255 l
|
||||
13.139197 8.245862 l
|
||||
13.498380 7.886679 l
|
||||
14.169774 8.558073 l
|
||||
h
|
||||
13.580109 8.118617 m
|
||||
13.896242 8.190466 14.344993 8.395787 14.624650 8.816864 c
|
||||
14.929440 9.275781 14.963785 9.882310 14.619765 10.585986 c
|
||||
13.766754 10.168959 l
|
||||
13.997427 9.697128 13.912044 9.460121 13.833706 9.342171 c
|
||||
13.730235 9.186377 13.532457 9.081495 13.369680 9.044500 c
|
||||
13.580109 8.118617 l
|
||||
h
|
||||
14.543221 10.698271 m
|
||||
13.820906 11.486253 12.989320 12.223852 11.896564 12.288132 c
|
||||
11.840808 11.340275 l
|
||||
12.524374 11.300065 13.128883 10.836036 13.843298 10.056674 c
|
||||
14.543221 10.698271 l
|
||||
h
|
||||
11.896564 12.288132 m
|
||||
11.441970 12.314873 10.711069 12.336796 10.019300 12.341186 c
|
||||
9.341933 12.345484 8.654247 12.333687 8.312695 12.284006 c
|
||||
8.449365 11.344399 l
|
||||
8.706450 11.381794 9.318512 11.396118 10.013274 11.391710 c
|
||||
10.693633 11.387392 11.407242 11.365778 11.840808 11.340275 c
|
||||
11.896564 12.288132 l
|
||||
h
|
||||
8.844993 11.713585 m
|
||||
8.948084 12.188952 9.040332 12.829445 9.094679 13.432834 c
|
||||
9.147870 14.023395 9.169946 14.631327 9.111748 15.019311 c
|
||||
8.172758 14.878462 l
|
||||
8.212520 14.613384 8.201942 14.105675 8.149012 13.518009 c
|
||||
8.097237 12.943172 8.009893 12.342852 7.917068 11.914822 c
|
||||
8.844993 11.713585 l
|
||||
h
|
||||
9.111748 15.019311 m
|
||||
8.944062 16.137217 7.805658 17.313158 6.262227 17.218662 c
|
||||
6.320251 16.270941 l
|
||||
7.336813 16.333179 8.072657 15.545805 8.172758 14.878462 c
|
||||
9.111748 15.019311 l
|
||||
h
|
||||
6.151623 17.198555 m
|
||||
5.976391 17.144638 5.715709 17.036982 5.490986 16.876261 c
|
||||
5.292936 16.734617 4.969444 16.439627 5.001374 15.992612 c
|
||||
5.948456 16.060261 l
|
||||
5.951383 16.019283 5.934667 15.999795 5.943361 16.012491 c
|
||||
5.954769 16.029152 5.984430 16.061831 6.043331 16.103956 c
|
||||
6.162553 16.189222 6.323094 16.257891 6.430855 16.291048 c
|
||||
6.151623 17.198555 l
|
||||
h
|
||||
5.001374 15.992612 m
|
||||
5.011176 15.855374 5.059216 15.566318 5.104405 15.255149 c
|
||||
5.152757 14.922197 5.207128 14.509316 5.241940 14.062993 c
|
||||
5.312967 13.152368 5.295928 12.171200 5.030037 11.457493 c
|
||||
5.919792 11.126015 l
|
||||
6.262142 12.044956 6.261431 13.202559 6.188560 14.136827 c
|
||||
6.151423 14.612950 6.093790 15.049047 6.044043 15.391605 c
|
||||
5.991133 15.755945 5.954979 15.968927 5.948456 16.060261 c
|
||||
5.001374 15.992612 l
|
||||
h
|
||||
5.030037 11.457493 m
|
||||
4.953650 11.252455 4.742510 10.903708 4.434547 10.555828 c
|
||||
4.127778 10.209298 3.769400 9.914337 3.428282 9.769621 c
|
||||
3.799107 8.895533 l
|
||||
4.320028 9.116529 4.788858 9.523607 5.145489 9.926461 c
|
||||
5.500926 10.327968 5.789377 10.775953 5.919792 11.126015 c
|
||||
5.030037 11.457493 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
7995
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 16.615845 16.660034 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000008085 00000 n
|
||||
0000008108 00000 n
|
||||
0000008281 00000 n
|
||||
0000008355 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
8414
|
||||
%%EOF
|
||||
@@ -41,19 +41,24 @@ struct ImageContextMenuModifier: ViewModifier {
|
||||
Button {
|
||||
UIPasteboard.general.url = url
|
||||
} label: {
|
||||
Label("Copy Image URL", systemImage: "doc.on.doc")
|
||||
Label(NSLocalizedString("Copy Image URL", comment: "Context menu option to copy the URL of an image into clipboard."), systemImage: "doc.on.doc")
|
||||
}
|
||||
if let someImage = image {
|
||||
Button {
|
||||
UIPasteboard.general.image = someImage
|
||||
} label: {
|
||||
Label("Copy Image", systemImage: "photo.on.rectangle")
|
||||
Label(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image into clipboard."), systemImage: "photo.on.rectangle")
|
||||
}
|
||||
Button {
|
||||
UIImageWriteToSavedPhotosAlbum(someImage, nil, nil, nil)
|
||||
} label: {
|
||||
Label(NSLocalizedString("Save Image", comment: "Context menu option to save an image."), systemImage: "square.and.arrow.down")
|
||||
}
|
||||
}
|
||||
Button {
|
||||
showShareSheet = true
|
||||
} label: {
|
||||
Label("Share", systemImage: "square.and.arrow.up")
|
||||
Label(NSLocalizedString("Share", comment: "Button to share an image."), systemImage: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +143,7 @@ struct ImageCarousel: View {
|
||||
}
|
||||
.id(url.absoluteString)
|
||||
.contextMenu {
|
||||
Button("Copy Image") {
|
||||
Button(NSLocalizedString("Copy Image", comment: "Context menu option to copy an image to clipboard.")) {
|
||||
UIPasteboard.general.string = url.absoluteString
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ struct InvoiceView: View {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.foregroundColor(colorScheme == .light ? .black : .white)
|
||||
.overlay {
|
||||
Text("Pay")
|
||||
Text("Pay", comment: "Button to pay a Lightning invoice.")
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(colorScheme == .light ? .white : .black)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ struct InvoiceView: View {
|
||||
HStack {
|
||||
Label("", systemImage: "bolt.fill")
|
||||
.foregroundColor(.orange)
|
||||
Text("Lightning Invoice")
|
||||
Text("Lightning Invoice", comment: "Indicates that the view is for paying a Lightning invoice.")
|
||||
}
|
||||
Divider()
|
||||
Text(invoice.description)
|
||||
|
||||
65
damus/Components/NIP05Badge.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// NIP05Badge.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-11.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct NIP05Badge: View {
|
||||
let nip05: NIP05
|
||||
let pubkey: String
|
||||
let contacts: Contacts
|
||||
let show_domain: Bool
|
||||
let clickable: Bool
|
||||
|
||||
@Environment(\.openURL) var openURL
|
||||
|
||||
init (nip05: NIP05, pubkey: String, contacts: Contacts, show_domain: Bool, clickable: Bool) {
|
||||
self.nip05 = nip05
|
||||
self.pubkey = pubkey
|
||||
self.contacts = contacts
|
||||
self.show_domain = show_domain
|
||||
self.clickable = clickable
|
||||
}
|
||||
|
||||
var nip05_color: Color {
|
||||
return get_nip05_color(pubkey: pubkey, contacts: contacts)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 2) {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.font(.footnote)
|
||||
.foregroundColor(nip05_color)
|
||||
if show_domain {
|
||||
if clickable {
|
||||
Text(nip05.host)
|
||||
.foregroundColor(nip05_color)
|
||||
.onTapGesture {
|
||||
if let nip5url = nip05.siteUrl {
|
||||
openURL(nip5url)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text(nip05.host)
|
||||
.foregroundColor(nip05_color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func get_nip05_color(pubkey: String, contacts: Contacts) -> Color {
|
||||
return contacts.is_friend_or_self(pubkey) ? .accentColor : .gray
|
||||
}
|
||||
|
||||
struct NIP05Badge_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let test_state = test_damus_state()
|
||||
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, clickable: false)
|
||||
}
|
||||
}
|
||||
|
||||
34
damus/Components/Reposted.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Reposted.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-11.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Reposted: View {
|
||||
let damus: DamusState
|
||||
let pubkey: String
|
||||
let profile: Profile?
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: "arrow.2.squarepath")
|
||||
.font(.footnote)
|
||||
.foregroundColor(Color.gray)
|
||||
ProfileName(pubkey: pubkey, profile: profile, damus: damus, show_friend_confirmed: true, show_nip5_domain: false)
|
||||
.foregroundColor(Color.gray)
|
||||
Text("Reposted", comment: "Text indicating that the post was reposted (i.e. re-shared).")
|
||||
.font(.footnote)
|
||||
.foregroundColor(Color.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Reposted_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let test_state = test_damus_state()
|
||||
Reposted(damus: test_state, pubkey: test_state.pubkey, profile: make_test_profile())
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ struct TextFieldAlert<Presenting>: View where Presenting: View {
|
||||
.disabled(isShowing)
|
||||
VStack {
|
||||
Text(self.title)
|
||||
TextField("Relay", text: self.$text)
|
||||
TextField(NSLocalizedString("Relay", comment: "Text field for relay server. Used for testing purposes."), text: self.$text)
|
||||
Divider()
|
||||
HStack {
|
||||
Button(action: {
|
||||
@@ -28,7 +28,7 @@ struct TextFieldAlert<Presenting>: View where Presenting: View {
|
||||
self.isShowing.toggle()
|
||||
}
|
||||
}) {
|
||||
Text("Dismiss")
|
||||
Text("Dismiss", comment: "Button to dismiss a text field alert.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,15 @@ enum ThreadState {
|
||||
enum FilterState : Int {
|
||||
case posts_and_replies = 1
|
||||
case posts = 0
|
||||
|
||||
func filter(ev: NostrEvent) -> Bool {
|
||||
switch self {
|
||||
case .posts:
|
||||
return !ev.is_reply(nil)
|
||||
case .posts_and_replies:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@@ -71,6 +80,7 @@ struct ContentView: View {
|
||||
@State var thread_open: Bool = false
|
||||
@State var search_open: Bool = false
|
||||
@State var filter_state : FilterState = .posts_and_replies
|
||||
@State private var isSideBarOpened = false
|
||||
@StateObject var home: HomeModel = HomeModel()
|
||||
@StateObject var user_settings = UserSettingsStore()
|
||||
|
||||
@@ -84,10 +94,12 @@ struct ContentView: View {
|
||||
var PostingTimelineView: some View {
|
||||
VStack {
|
||||
TabView(selection: $filter_state) {
|
||||
ContentTimelineView
|
||||
contentTimelineView(filter: FilterState.posts.filter)
|
||||
.tag(FilterState.posts)
|
||||
ContentTimelineView
|
||||
.id(FilterState.posts)
|
||||
contentTimelineView(filter: FilterState.posts_and_replies.filter)
|
||||
.tag(FilterState.posts_and_replies)
|
||||
.id(FilterState.posts_and_replies)
|
||||
}
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
}
|
||||
@@ -101,15 +113,16 @@ struct ContentView: View {
|
||||
}
|
||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||
}
|
||||
.ignoresSafeArea(.keyboard)
|
||||
}
|
||||
|
||||
var ContentTimelineView: some View {
|
||||
func contentTimelineView(filter: (@escaping (NostrEvent) -> Bool)) -> some View {
|
||||
ZStack {
|
||||
if let damus = self.damus_state {
|
||||
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
|
||||
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter)
|
||||
}
|
||||
if privkey != nil {
|
||||
PostButtonContainer {
|
||||
PostButtonContainer(userSettings: user_settings) {
|
||||
self.active_sheet = .post
|
||||
}
|
||||
}
|
||||
@@ -118,22 +131,14 @@ struct ContentView: View {
|
||||
|
||||
var FiltersView: some View {
|
||||
VStack{
|
||||
Picker("Filter State", selection: $filter_state) {
|
||||
Text("Posts").tag(FilterState.posts)
|
||||
Text("Posts & Replies").tag(FilterState.posts_and_replies)
|
||||
Picker(NSLocalizedString("Filter State", comment: "Filter state for seeing either only posts, or posts & replies."), selection: $filter_state) {
|
||||
Text("Posts", comment: "Label for filter for seeing only posts (instead of posts and replies).").tag(FilterState.posts)
|
||||
Text("Posts & Replies", comment: "Label for filter for seeing posts and replies (instead of only posts).").tag(FilterState.posts_and_replies)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
}
|
||||
|
||||
func filter_event(_ ev: NostrEvent) -> Bool {
|
||||
if self.filter_state == .posts {
|
||||
return !ev.is_reply(nil)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func MainContent(damus: DamusState) -> some View {
|
||||
VStack {
|
||||
NavigationLink(destination: MaybeProfileView, isActive: $profile_open) {
|
||||
@@ -154,7 +159,7 @@ struct ContentView: View {
|
||||
|
||||
case .notifications:
|
||||
TimelineView(events: $home.notifications, loading: $home.loading, damus: damus, show_friend_icon: true, filter: { _ in true })
|
||||
.navigationTitle("Notifications")
|
||||
.navigationTitle(NSLocalizedString("Notifications", comment: "Navigation title for notifications."))
|
||||
|
||||
case .dms:
|
||||
DirectMessagesView(damus_state: damus_state!)
|
||||
@@ -164,7 +169,27 @@ struct ContentView: View {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.navigationBarTitle(selected_timeline == .home ? "Home" : "Global", displayMode: .inline)
|
||||
.navigationBarTitle(selected_timeline == .home ? NSLocalizedString("Home", comment: "Navigation bar title for Home view where posts and replies appear from those who the user is following.") : NSLocalizedString("Global", comment: "Navigation bar title for Global view where posts from all connected relay servers appear."), displayMode: .inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
switch selected_timeline {
|
||||
case .home:
|
||||
Image("damus-home")
|
||||
.resizable()
|
||||
.frame(width:30,height:30)
|
||||
.shadow(color: Color("DamusPurple"), radius: 2)
|
||||
case .dms:
|
||||
Text("DM", comment: "Toolbar label for DM view, which is the English abbreviation for Direct Message.")
|
||||
case .notifications:
|
||||
Text("Notifications", comment: "Toolbar label for Notifications view.")
|
||||
case .search:
|
||||
Text("Global", comment: "Toolbar label for Global view where posts from all connected relay servers appear.")
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var MaybeSearchView: some View {
|
||||
@@ -198,51 +223,49 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if let damus = self.damus_state {
|
||||
NavigationView {
|
||||
MainContent(damus: damus)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
let profile_model = ProfileModel(pubkey: damus_state!.pubkey, damus: damus_state!)
|
||||
let followers_model = FollowersModel(damus_state: damus_state!, target: damus_state!.pubkey)
|
||||
let prof_dest = ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers_model)
|
||||
ZStack {
|
||||
VStack {
|
||||
MainContent(damus: damus)
|
||||
.toolbar() {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
isSideBarOpened.toggle()
|
||||
} label: {
|
||||
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles)
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack(alignment: .center) {
|
||||
if home.signal.signal != home.signal.max_signal {
|
||||
Text("\(home.signal.signal)/\(home.signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
NavigationLink(destination: prof_dest) {
|
||||
/// Verify that the user has a profile picture, if not display a generic SF Symbol
|
||||
/// (Resolves an in-app error where ``Robohash`` pictures are not generated so the button dissapears
|
||||
if let picture = damus_state?.profiles.lookup(id: pubkey)?.picture {
|
||||
ProfilePicView(pubkey: damus_state!.pubkey, size: 32, highlight: .none, profiles: damus_state!.profiles, picture: picture)
|
||||
} else {
|
||||
Image(systemName: "person.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack(alignment: .center) {
|
||||
if home.signal.signal != home.signal.max_signal {
|
||||
Text("\(home.signal.signal)/\(home.signal.max_signal)")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
NavigationLink(destination: ConfigView(state: damus_state!).environmentObject(user_settings)) {
|
||||
Label("", systemImage: "gear")
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Color.clear
|
||||
.overlay(
|
||||
SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened)
|
||||
)
|
||||
}
|
||||
.navigationBarHidden(isSideBarOpened ? true: false) // Would prefer a different way of doing this.
|
||||
}
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
TabBar(new_events: $home.new_events, selected: $selected_timeline, isSidebarVisible: $isSideBarOpened, action: switch_timeline)
|
||||
.padding([.bottom], 8)
|
||||
}
|
||||
|
||||
TabBar(new_events: $home.new_events, selected: $selected_timeline, action: switch_timeline)
|
||||
.padding([.bottom], 8)
|
||||
}
|
||||
.onAppear() {
|
||||
self.connect()
|
||||
@@ -283,7 +306,6 @@ struct ContentView: View {
|
||||
guard let privkey = self.privkey else {
|
||||
return
|
||||
}
|
||||
|
||||
let ev = notif.object as! NostrEvent
|
||||
let boost = make_boost_event(pubkey: pubkey, privkey: privkey, boosted: ev)
|
||||
self.damus_state?.pool.send(.event(boost))
|
||||
@@ -316,10 +338,10 @@ struct ContentView: View {
|
||||
let pk = target.pubkey
|
||||
|
||||
if let ev = unfollow_user(pool: damus.pool,
|
||||
our_contacts: damus.contacts.event,
|
||||
pubkey: damus.pubkey,
|
||||
privkey: privkey,
|
||||
unfollow: pk) {
|
||||
our_contacts: damus.contacts.event,
|
||||
pubkey: damus.pubkey,
|
||||
privkey: privkey,
|
||||
unfollow: pk) {
|
||||
notify(.unfollowed, pk)
|
||||
|
||||
damus.contacts.event = ev
|
||||
@@ -338,10 +360,10 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
if let ev = follow_user(pool: damus.pool,
|
||||
our_contacts: damus.contacts.event,
|
||||
pubkey: damus.pubkey,
|
||||
privkey: privkey,
|
||||
follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) {
|
||||
our_contacts: damus.contacts.event,
|
||||
pubkey: damus.pubkey,
|
||||
privkey: privkey,
|
||||
follow: ReferencedId(ref_id: fnotify.pubkey, relay_id: nil, key: "p")) {
|
||||
notify(.followed, fnotify.pubkey)
|
||||
|
||||
damus_state?.contacts.event = ev
|
||||
@@ -411,10 +433,11 @@ struct ContentView: View {
|
||||
self.damus_state = DamusState(pool: pool, keypair: keypair,
|
||||
likes: EventCounter(our_pubkey: pubkey),
|
||||
boosts: EventCounter(our_pubkey: pubkey),
|
||||
contacts: Contacts(),
|
||||
contacts: Contacts(our_pubkey: pubkey),
|
||||
tips: TipCounter(our_pubkey: pubkey),
|
||||
profiles: Profiles(),
|
||||
dms: home.dms
|
||||
dms: home.dms,
|
||||
previews: PreviewCache()
|
||||
)
|
||||
home.damus_state = self.damus_state!
|
||||
|
||||
@@ -430,7 +453,6 @@ struct ContentView_Previews: PreviewProvider {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func get_since_time(last_event: NostrEvent?) -> Int64? {
|
||||
if let last_event = last_event {
|
||||
return last_event.created_at - 60 * 10
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>"Granting Damus access to your photo library allows you to save photos.</string>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>river</string>
|
||||
|
||||
@@ -16,6 +16,10 @@ class ActionBarModel: ObservableObject {
|
||||
@Published var boosts: Int
|
||||
@Published var tips: Int64
|
||||
|
||||
static func empty() -> ActionBarModel {
|
||||
return ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
|
||||
}
|
||||
|
||||
init(likes: Int, boosts: Int, tips: Int64, our_like: NostrEvent?, our_boost: NostrEvent?, our_tip: NostrEvent?) {
|
||||
self.likes = likes
|
||||
self.boosts = boosts
|
||||
@@ -25,6 +29,10 @@ class ActionBarModel: ObservableObject {
|
||||
self.our_tip = our_tip
|
||||
}
|
||||
|
||||
var is_empty: Bool {
|
||||
return likes == 0 && boosts == 0 && tips == 0
|
||||
}
|
||||
|
||||
var tipped: Bool {
|
||||
return our_tip != nil
|
||||
}
|
||||
|
||||
@@ -11,8 +11,13 @@ import Foundation
|
||||
class Contacts {
|
||||
private var friends: Set<String> = Set()
|
||||
private var friend_of_friends: Set<String> = Set()
|
||||
let our_pubkey: String
|
||||
var event: NostrEvent?
|
||||
|
||||
init(our_pubkey: String) {
|
||||
self.our_pubkey = our_pubkey
|
||||
}
|
||||
|
||||
func get_friendosphere() -> [String] {
|
||||
var fs = get_friend_list()
|
||||
fs.append(contentsOf: get_friend_of_friend_list())
|
||||
@@ -56,6 +61,10 @@ class Contacts {
|
||||
return friends.contains(pubkey)
|
||||
}
|
||||
|
||||
func is_friend_or_self(_ pubkey: String) -> Bool {
|
||||
return pubkey == our_pubkey || is_friend(pubkey)
|
||||
}
|
||||
|
||||
func follow_state(_ pubkey: String) -> FollowState {
|
||||
return is_friend(pubkey) ? .follows : .unfollows
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LinkPresentation
|
||||
|
||||
struct DamusState {
|
||||
let pool: RelayPool
|
||||
@@ -16,12 +17,13 @@ struct DamusState {
|
||||
let tips: TipCounter
|
||||
let profiles: Profiles
|
||||
let dms: DirectMessagesModel
|
||||
let previews: PreviewCache
|
||||
|
||||
var pubkey: String {
|
||||
return keypair.pubkey
|
||||
}
|
||||
|
||||
static var empty: DamusState {
|
||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel())
|
||||
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(), previews: PreviewCache())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ class FollowersModel: ObservableObject {
|
||||
let sub_id: String = UUID().description
|
||||
let profiles_id: String = UUID().description
|
||||
|
||||
var count_display: String {
|
||||
var count: Int? {
|
||||
guard let contacts = self.contacts else {
|
||||
return "?"
|
||||
return nil
|
||||
}
|
||||
return "\(contacts.count)";
|
||||
return contacts.count
|
||||
}
|
||||
|
||||
init(damus_state: DamusState, target: String) {
|
||||
@@ -73,31 +73,30 @@ class FollowersModel: ObservableObject {
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||
switch ev {
|
||||
case .ws_event:
|
||||
break
|
||||
case .nostr_event(let nev):
|
||||
switch nev {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.sub_id || sub_id == self.profiles_id else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .contacts {
|
||||
handle_contact_event(ev)
|
||||
} else if ev.known_kind == .metadata {
|
||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
case .notice(let msg):
|
||||
print("followingmodel notice: \(msg)")
|
||||
|
||||
case .eose(let sub_id):
|
||||
if sub_id == self.sub_id {
|
||||
load_profiles(relay_id: relay_id)
|
||||
} else if sub_id == self.profiles_id {
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_id, to: [relay_id])
|
||||
}
|
||||
guard case .nostr_event(let nev) = ev else {
|
||||
return
|
||||
}
|
||||
|
||||
switch nev {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.sub_id || sub_id == self.profiles_id else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .contacts {
|
||||
handle_contact_event(ev)
|
||||
} else if ev.known_kind == .metadata {
|
||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
case .notice(let msg):
|
||||
print("followingmodel notice: \(msg)")
|
||||
|
||||
case .eose(let sub_id):
|
||||
if sub_id == self.sub_id {
|
||||
load_profiles(relay_id: relay_id)
|
||||
} else if sub_id == self.profiles_id {
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_id, to: [relay_id])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,7 +544,9 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
return
|
||||
}
|
||||
|
||||
var old_nip05: String? = nil
|
||||
if let mprof = profiles.lookup_with_timestamp(id: ev.pubkey) {
|
||||
old_nip05 = mprof.profile.nip05
|
||||
if mprof.timestamp > ev.created_at {
|
||||
// skip if we already have an newer profile
|
||||
return
|
||||
@@ -554,6 +556,20 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
let tprof = TimestampedProfile(profile: profile, timestamp: ev.created_at)
|
||||
profiles.add(id: ev.pubkey, profile: tprof)
|
||||
|
||||
if let nip05 = profile.nip05, old_nip05 != profile.nip05 {
|
||||
Task.detached(priority: .background) {
|
||||
let validated = await validate_nip05(pubkey: ev.pubkey, nip05_str: nip05)
|
||||
if validated != nil {
|
||||
print("validated nip05 for '\(nip05)'")
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
profiles.validated[ev.pubkey] = validated
|
||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load pfps asap
|
||||
let picture = tprof.profile.picture ?? robohash(ev.pubkey)
|
||||
if let _ = URL(string: picture) {
|
||||
@@ -562,6 +578,13 @@ func process_metadata_event(profiles: Profiles, ev: NostrEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
let banner = tprof.profile.banner ?? ""
|
||||
if let _ = URL(string: banner) {
|
||||
DispatchQueue.main.async {
|
||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||
}
|
||||
}
|
||||
|
||||
notify(.profile_updated, ProfileUpdate(pubkey: ev.pubkey, profile: profile))
|
||||
}
|
||||
|
||||
|
||||
@@ -187,12 +187,18 @@ enum Amount: Equatable {
|
||||
func amount_sats_str() -> String {
|
||||
switch self {
|
||||
case .any:
|
||||
return "Any"
|
||||
return NSLocalizedString("Any", comment: "Any amount of sats")
|
||||
case .specific(let amt):
|
||||
if amt < 1000 {
|
||||
return "\(Double(amt) / 1000.0) sats"
|
||||
}
|
||||
return "\(amt / 1000) sats"
|
||||
let numberFormatter = NumberFormatter()
|
||||
numberFormatter.numberStyle = .decimal
|
||||
numberFormatter.minimumFractionDigits = 0
|
||||
numberFormatter.maximumFractionDigits = 3
|
||||
numberFormatter.roundingMode = .down
|
||||
|
||||
let sats = NSNumber(value: (Double(amt) / 1000.0))
|
||||
let formattedSats = numberFormatter.string(from: sats) ?? sats.stringValue
|
||||
|
||||
return String(format: NSLocalizedString("sats_count", comment: "Amount of sats."), sats.decimalValue as NSDecimalNumber, formattedSats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
78
damus/Models/ReactionsModel.swift
Normal file
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// LikesModel.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-11.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
class ReactionsModel: ObservableObject {
|
||||
let state: DamusState
|
||||
let target: String
|
||||
let sub_id: String
|
||||
let profiles_id: String
|
||||
|
||||
@Published var reactions: [NostrEvent]
|
||||
|
||||
init (state: DamusState, target: String) {
|
||||
self.state = state
|
||||
self.target = target
|
||||
self.sub_id = UUID().description
|
||||
self.profiles_id = UUID().description
|
||||
self.reactions = []
|
||||
}
|
||||
|
||||
func get_filter() -> NostrFilter {
|
||||
var filter = NostrFilter.filter_kinds([7])
|
||||
filter.referenced_ids = [target]
|
||||
filter.limit = 500
|
||||
return filter
|
||||
}
|
||||
|
||||
func subscribe() {
|
||||
let filter = get_filter()
|
||||
let filters = [filter]
|
||||
self.state.pool.subscribe(sub_id: sub_id, filters: filters, handler: handle_nostr_event)
|
||||
}
|
||||
|
||||
func unsubscribe() {
|
||||
self.state.pool.unsubscribe(sub_id: sub_id)
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, ev: NostrEvent) {
|
||||
guard ev.kind == 7 else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let reacted_to = last_etag(tags: ev.tags) else {
|
||||
return
|
||||
}
|
||||
|
||||
guard reacted_to == self.target else {
|
||||
return
|
||||
}
|
||||
|
||||
if insert_uniq_sorted_event(events: &self.reactions, new_ev: ev, cmp: { a, b in a.created_at < b.created_at } ) {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
func handle_nostr_event(relay_id: String, ev: NostrConnectionEvent) {
|
||||
guard case .nostr_event(let nev) = ev else {
|
||||
return
|
||||
}
|
||||
|
||||
switch nev {
|
||||
case .event(_, let ev):
|
||||
handle_event(relay_id: relay_id, ev: ev)
|
||||
|
||||
case .notice(_):
|
||||
break
|
||||
case .eose(_):
|
||||
load_profiles(profiles_subid: profiles_id, relay_id: relay_id, events: reactions, damus_state: state)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,40 +41,40 @@ class SearchHomeModel: ObservableObject {
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, conn_ev: NostrConnectionEvent) {
|
||||
switch conn_ev {
|
||||
case .ws_event:
|
||||
break
|
||||
case .nostr_event(let event):
|
||||
switch event {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.base_subid || sub_id == self.profiles_subid else {
|
||||
guard case .nostr_event(let event) = conn_ev else {
|
||||
return
|
||||
}
|
||||
|
||||
switch event {
|
||||
case .event(let sub_id, let ev):
|
||||
guard sub_id == self.base_subid || sub_id == self.profiles_subid else {
|
||||
return
|
||||
}
|
||||
if ev.is_textlike && ev.should_show_event && !ev.is_reply(nil) {
|
||||
if seen_pubkey.contains(ev.pubkey) {
|
||||
return
|
||||
}
|
||||
if ev.is_textlike && ev.should_show_event {
|
||||
if seen_pubkey.contains(ev.pubkey) {
|
||||
return
|
||||
}
|
||||
seen_pubkey.insert(ev.pubkey)
|
||||
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
|
||||
$0.created_at > $1.created_at
|
||||
}
|
||||
seen_pubkey.insert(ev.pubkey)
|
||||
|
||||
let _ = insert_uniq_sorted_event(events: &events, new_ev: ev) {
|
||||
$0.created_at > $1.created_at
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("search home notice: \(msg)")
|
||||
case .eose(let sub_id):
|
||||
loading = false
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
// Make sure we unsubscribe after we've fetched the global events
|
||||
// global events are not realtime
|
||||
unsubscribe(to: relay_id)
|
||||
|
||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
||||
}
|
||||
|
||||
|
||||
break
|
||||
}
|
||||
case .notice(let msg):
|
||||
print("search home notice: \(msg)")
|
||||
case .eose(let sub_id):
|
||||
loading = false
|
||||
|
||||
if sub_id == self.base_subid {
|
||||
// Make sure we unsubscribe after we've fetched the global events
|
||||
// global events are not realtime
|
||||
unsubscribe(to: relay_id)
|
||||
|
||||
load_profiles(profiles_subid: profiles_subid, relay_id: relay_id, events: events, damus_state: damus_state)
|
||||
}
|
||||
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,27 +112,30 @@ func load_profiles(profiles_subid: String, relay_id: String, events: [NostrEvent
|
||||
let authors = find_profiles_to_fetch(profiles: damus_state.profiles, events: events)
|
||||
filter.authors = authors
|
||||
|
||||
if !authors.isEmpty {
|
||||
print("loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in
|
||||
let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in
|
||||
guard sub_id == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guard done && sid == profiles_subid else {
|
||||
guard !authors.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
print("loading \(authors.count) profiles from \(relay_id)")
|
||||
|
||||
damus_state.pool.subscribe_to(sub_id: profiles_subid, filters: [filter], to: [relay_id]) { sub_id, conn_ev in
|
||||
let (sid, done) = handle_subid_event(pool: damus_state.pool, relay_id: relay_id, ev: conn_ev) { sub_id, ev in
|
||||
guard sub_id == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
print("done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
|
||||
if ev.known_kind == .metadata {
|
||||
process_metadata_event(profiles: damus_state.profiles, ev: ev)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
guard done && sid == profiles_subid else {
|
||||
return
|
||||
}
|
||||
|
||||
print("done loading \(authors.count) profiles from \(relay_id)")
|
||||
damus_state.pool.unsubscribe(sub_id: profiles_subid, to: [relay_id])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class ThreadModel: ObservableObject {
|
||||
ref_events.referenced_ids = ev.referenced_ids.map { $0.ref_id }
|
||||
ref_events.referenced_ids?.append(ev.id)
|
||||
ref_events.limit = 50
|
||||
events_filter.ids = ref_events.referenced_ids!
|
||||
events_filter.ids = ref_events.referenced_ids ?? []
|
||||
events_filter.limit = 100
|
||||
events_filter.ids?.append(ev.id)
|
||||
case .event_id(let evid):
|
||||
|
||||
@@ -20,13 +20,22 @@ class UserSettingsStore: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
@Published var left_handed: Bool {
|
||||
didSet {
|
||||
UserDefaults.standard.set(left_handed, forKey: "left_handed")
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
if let defaultWalletName = UserDefaults.standard.string(forKey: "default_wallet"),
|
||||
let default_wallet = Wallet(rawValue: defaultWalletName) {
|
||||
let default_wallet = Wallet(rawValue: defaultWalletName)
|
||||
{
|
||||
self.default_wallet = default_wallet
|
||||
} else {
|
||||
self.default_wallet = .system_default_wallet
|
||||
default_wallet = .system_default_wallet
|
||||
}
|
||||
self.show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
|
||||
show_wallet_selector = UserDefaults.standard.object(forKey: "show_wallet_selector") as? Bool ?? true
|
||||
|
||||
left_handed = UserDefaults.standard.object(forKey: "left_handed") as? Bool ?? false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ import Foundation
|
||||
struct Profile: Codable {
|
||||
var value: [String: String]
|
||||
|
||||
init (name: String?, display_name: String?, about: String?, picture: String?, website: String?, lud06: String?, lud16: String?, nip05: String?) {
|
||||
init (name: String?, display_name: String?, about: String?, picture: String?, banner: String?, website: String?, lud06: String?, lud16: String?, nip05: String?) {
|
||||
self.value = [:]
|
||||
self.name = name
|
||||
self.display_name = display_name
|
||||
self.about = about
|
||||
self.picture = picture
|
||||
self.banner = banner
|
||||
self.website = website
|
||||
self.lud06 = lud06
|
||||
self.lud16 = lud16
|
||||
@@ -42,6 +43,11 @@ struct Profile: Codable {
|
||||
set(s) { value["picture"] = s }
|
||||
}
|
||||
|
||||
var banner: String? {
|
||||
get { return value["banner"]; }
|
||||
set(s) { value["banner"] = s }
|
||||
}
|
||||
|
||||
var website: String? {
|
||||
get { return value["website"]; }
|
||||
set(s) { value["website"] = s }
|
||||
@@ -94,26 +100,9 @@ struct Profile: Codable {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
struct Profile: Decodable {
|
||||
let name: String?
|
||||
let display_name: String?
|
||||
let about: String?
|
||||
let picture: String?
|
||||
let website: String?
|
||||
let nip05: String?
|
||||
let lud06: String?
|
||||
let lud16: String?
|
||||
|
||||
var lightning_uri: URL? {
|
||||
return make_ln_url(self.lud06) ?? make_ln_url(self.lud16)
|
||||
}
|
||||
|
||||
static func displayName(profile: Profile?, pubkey: String) -> String {
|
||||
return profile?.name ?? abbrev_pubkey(pubkey)
|
||||
}
|
||||
func make_test_profile() -> Profile {
|
||||
return Profile(name: "jb55", display_name: "Will", about: "Its a me", picture: "https://cdn.jb55.com/img/red-me.jpg", banner: "https://pbs.twimg.com/profile_banners/9918032/1531711830/600x200", website: "jb55.com", lud06: "jb55@jb55.com", lud16: nil, nip05: "jb55@jb55.com")
|
||||
}
|
||||
*/
|
||||
|
||||
func make_ln_url(_ str: String?) -> URL? {
|
||||
return str.flatMap { URL(string: "lightning:" + $0) }
|
||||
|
||||
@@ -446,11 +446,13 @@ func hex_encode(_ data: Data) -> String {
|
||||
|
||||
|
||||
func random_bytes(count: Int) -> Data {
|
||||
var data = Data(count: count)
|
||||
_ = data.withUnsafeMutableBytes { mutableBytes in
|
||||
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes.baseAddress!)
|
||||
var bytes = [Int8](repeating: 0, count: count)
|
||||
guard
|
||||
SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) == errSecSuccess
|
||||
else {
|
||||
fatalError("can't copy secure random data")
|
||||
}
|
||||
return data
|
||||
return Data(bytes: bytes, count: count)
|
||||
}
|
||||
|
||||
func refid_to_tag(_ ref: ReferencedId) -> [String] {
|
||||
@@ -770,6 +772,15 @@ func validate_event(ev: NostrEvent) -> ValidationResult {
|
||||
return ok ? .ok : .bad_sig
|
||||
}
|
||||
|
||||
func last_etag(tags: [[String]]) -> String? {
|
||||
var e: String? = nil
|
||||
for tag in tags {
|
||||
if tag.count >= 2 && tag[0] == "e" {
|
||||
e = tag[1]
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func inner_event_or_self(ev: NostrEvent) -> NostrEvent {
|
||||
guard let inner_ev = ev.inner_event else {
|
||||
|
||||
@@ -80,7 +80,32 @@ func parse_nostr_ref_uri(_ p: Parser) -> ReferencedId? {
|
||||
return ReferencedId(ref_id: pk, relay_id: nil, key: typ)
|
||||
}
|
||||
|
||||
func decode_universal_link(_ s: String) -> NostrLink? {
|
||||
var uri = s.replacingOccurrences(of: "https://damus.io/r/", with: "")
|
||||
uri = uri.replacingOccurrences(of: "https://damus.io/", with: "")
|
||||
uri = uri.replacingOccurrences(of: "/", with: "")
|
||||
|
||||
guard let decoded = try? bech32_decode(uri) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let h = hex_encode(decoded.data)
|
||||
|
||||
if decoded.hrp == "note" {
|
||||
return .ref(ReferencedId(ref_id: h, relay_id: nil, key: "e"))
|
||||
} else if decoded.hrp == "npub" {
|
||||
return .ref(ReferencedId(ref_id: h, relay_id: nil, key: "p"))
|
||||
}
|
||||
// TODO: handle nprofile, etc
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decode_nostr_uri(_ s: String) -> NostrLink? {
|
||||
if s.starts(with: "https://damus.io/") {
|
||||
return decode_universal_link(s)
|
||||
}
|
||||
|
||||
var uri = s.replacingOccurrences(of: "nostr://", with: "")
|
||||
uri = uri.replacingOccurrences(of: "nostr:", with: "")
|
||||
|
||||
|
||||
@@ -15,10 +15,11 @@ struct NostrMetadata: Codable {
|
||||
let website: String?
|
||||
let nip05: String?
|
||||
let picture: String?
|
||||
let banner: String?
|
||||
let lud06: String?
|
||||
let lud16: String?
|
||||
}
|
||||
|
||||
func create_account_to_metadata(_ model: CreateAccountModel) -> NostrMetadata {
|
||||
return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil, nip05: nil, picture: nil, lud06: nil, lud16: nil)
|
||||
return NostrMetadata(display_name: model.real_name, name: model.nick_name, about: model.about, website: nil, nip05: nil, picture: nil, banner: nil, lud06: nil, lud16: nil)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,17 @@ enum NostrResponse: Decodable {
|
||||
case event(String, NostrEvent)
|
||||
case notice(String)
|
||||
case eose(String)
|
||||
|
||||
var subid: String? {
|
||||
switch self {
|
||||
case .event(let sub_id, _):
|
||||
return sub_id
|
||||
case .eose(let sub_id):
|
||||
return sub_id
|
||||
case .notice:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
var container = try decoder.unkeyedContainer()
|
||||
|
||||
@@ -11,6 +11,11 @@ import UIKit
|
||||
|
||||
class Profiles {
|
||||
var profiles: [String: TimestampedProfile] = [:]
|
||||
var validated: [String: NIP05] = [:]
|
||||
|
||||
func is_validated(_ pk: String) -> NIP05? {
|
||||
return validated[pk]
|
||||
}
|
||||
|
||||
func add(id: String, profile: TimestampedProfile) {
|
||||
profiles[id] = profile
|
||||
|
||||
@@ -38,7 +38,7 @@ class RelayConnection: WebSocketDelegate {
|
||||
self.connect(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func connect(force: Bool = false){
|
||||
if !force && (self.isConnected || self.isConnecting) {
|
||||
return
|
||||
|
||||
@@ -28,9 +28,20 @@ struct RelayHandler {
|
||||
let callback: (String, NostrConnectionEvent) -> ()
|
||||
}
|
||||
|
||||
struct QueuedRequest {
|
||||
let req: NostrRequest
|
||||
let relay: String
|
||||
}
|
||||
|
||||
struct NostrRequestId: Equatable, Hashable {
|
||||
let relay: String?
|
||||
let sub_id: String
|
||||
}
|
||||
|
||||
class RelayPool {
|
||||
var relays: [Relay] = []
|
||||
var handlers: [RelayHandler] = []
|
||||
var request_queue: [QueuedRequest] = []
|
||||
|
||||
var descriptors: [RelayDescriptor] {
|
||||
relays.map { $0.descriptor }
|
||||
@@ -148,13 +159,38 @@ class RelayPool {
|
||||
send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
|
||||
}
|
||||
|
||||
func count_queued(relay: String) -> Int {
|
||||
var c = 0
|
||||
for request in request_queue {
|
||||
if request.relay == relay {
|
||||
c += 1
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func queue_req(r: NostrRequest, relay: String) {
|
||||
let count = count_queued(relay: relay)
|
||||
guard count <= 10 else {
|
||||
print("can't queue, too many queued events for \(relay)")
|
||||
return
|
||||
}
|
||||
|
||||
print("queueing request: \(r) for \(relay)")
|
||||
request_queue.append(QueuedRequest(req: r, relay: relay))
|
||||
}
|
||||
|
||||
func send(_ req: NostrRequest, to: [String]? = nil) {
|
||||
let relays = to.map{ get_relays($0) } ?? self.relays
|
||||
|
||||
for relay in relays {
|
||||
if relay.connection.isConnected {
|
||||
relay.connection.send(req)
|
||||
guard relay.connection.isConnected else {
|
||||
queue_req(r: req, relay: relay.id)
|
||||
continue
|
||||
}
|
||||
|
||||
relay.connection.send(req)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +229,28 @@ class RelayPool {
|
||||
}
|
||||
}
|
||||
|
||||
func run_queue(_ relay_id: String) {
|
||||
self.request_queue = request_queue.reduce(into: Array<QueuedRequest>()) { (q, req) in
|
||||
guard req.relay == relay_id else {
|
||||
q.append(req)
|
||||
return
|
||||
}
|
||||
|
||||
print("running queueing request: \(req.req) for \(relay_id)")
|
||||
self.send(req.req, to: [relay_id])
|
||||
}
|
||||
}
|
||||
|
||||
func handle_event(relay_id: String, event: NostrConnectionEvent) {
|
||||
record_last_pong(relay_id: relay_id, event: event)
|
||||
|
||||
// run req queue when we reconnect
|
||||
if case .ws_event(let ws) = event {
|
||||
if case .connected = ws {
|
||||
run_queue(relay_id)
|
||||
}
|
||||
}
|
||||
|
||||
// handle reconnect logic, etc?
|
||||
for handler in handlers {
|
||||
handler.callback(relay_id, event)
|
||||
|
||||
@@ -11,7 +11,7 @@ public class Constants {
|
||||
|
||||
static let PUB_KEY = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"
|
||||
|
||||
static let EXAMPLE_DEMOS = DamusState(pool: RelayPool(), keypair: Keypair(pubkey: PUB_KEY, privkey: "privkey"), likes: EventCounter(our_pubkey: PUB_KEY), boosts: EventCounter(our_pubkey: PUB_KEY), contacts: Contacts(), tips: TipCounter(our_pubkey: PUB_KEY), profiles: Profiles(), dms: DirectMessagesModel())
|
||||
static let EXAMPLE_DEMOS: DamusState = .empty
|
||||
|
||||
static let EXAMPLE_EVENTS = [
|
||||
NostrEvent(id: UUID().description, content: "Nostr - Damus... Haha get it? Bonjour Le Monde mon Ami! C'est la tres importante", pubkey: "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681"),
|
||||
|
||||
@@ -12,26 +12,24 @@ class CustomLinkView: LPLinkView {
|
||||
override var intrinsicContentSize: CGSize { CGSize(width: 0, height: super.intrinsicContentSize.height) }
|
||||
}
|
||||
|
||||
enum Metadata {
|
||||
case linkmeta(LPLinkMetadata)
|
||||
case url(URL)
|
||||
}
|
||||
|
||||
struct LinkViewRepresentable: UIViewRepresentable {
|
||||
|
||||
typealias UIViewType = CustomLinkView
|
||||
|
||||
var metadata: LPLinkMetadata?
|
||||
var url: URL?
|
||||
let meta: Metadata
|
||||
|
||||
func makeUIView(context: Context) -> CustomLinkView {
|
||||
|
||||
if let metadata {
|
||||
let linkView = CustomLinkView(metadata: metadata)
|
||||
return linkView
|
||||
switch meta {
|
||||
case .linkmeta(let linkmeta):
|
||||
return CustomLinkView(metadata: linkmeta)
|
||||
case .url(let url):
|
||||
return CustomLinkView(url: url)
|
||||
}
|
||||
|
||||
if let url {
|
||||
let linkView = CustomLinkView(url: url)
|
||||
return linkView
|
||||
}
|
||||
|
||||
return CustomLinkView()
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: CustomLinkView, context: Context) {
|
||||
|
||||
@@ -15,12 +15,14 @@ public struct Markdown {
|
||||
return url.contains("://") ? url : "https://" + url
|
||||
}
|
||||
|
||||
/// Parse a string with markdown into an `AttributedString`, if possible, or else return it as regular text.
|
||||
public static func parse(content: String) -> AttributedString {
|
||||
// Similar to the parsing in NoteContentView
|
||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
||||
if let txt = try? AttributedString(markdown: content, options: md_opts) {
|
||||
// TODO: escape unintentional markdown
|
||||
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
||||
if let txt = try? AttributedString(markdown: escaped, options: md_opts) {
|
||||
return txt
|
||||
} else {
|
||||
return AttributedString(stringLiteral: content)
|
||||
@@ -33,11 +35,13 @@ public struct Markdown {
|
||||
var output = input
|
||||
// Start with the last match, because replacing the first would invalidate all subsequent indices
|
||||
for match in matches.reversed() {
|
||||
guard let range = Range(match.range, in: input) else { continue }
|
||||
let url = input[range]
|
||||
output.replaceSubrange(range, with: "[\(url)](\(Markdown.withScheme(url)))")
|
||||
guard let range = Range(match.range, in: input)
|
||||
, let url = match.url else { continue }
|
||||
let text = input[range]
|
||||
// Use the absoluteString from the matched URL, except when it defaults to http (since we default to https)
|
||||
let uri = url.scheme == "http" ? Markdown.withScheme(text) : url.absoluteString
|
||||
output.replaceSubrange(range, with: "[\(text)](\(uri))")
|
||||
}
|
||||
// TODO: escape unintentional markdown
|
||||
return Markdown.parse(content: output)
|
||||
}
|
||||
}
|
||||
|
||||
69
damus/Util/NIP05.swift
Normal file
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// NIP05.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-04.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct NIP05 {
|
||||
let username: String
|
||||
let host: String
|
||||
|
||||
var url: URL? {
|
||||
URL(string: "https://\(host)/.well-known/nostr.json?name=\(username)")
|
||||
}
|
||||
|
||||
var siteUrl: URL? {
|
||||
URL(string: "https://\(host)")
|
||||
}
|
||||
|
||||
static func parse(_ nip05: String) -> NIP05? {
|
||||
let parts = nip05.split(separator: "@")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
}
|
||||
return NIP05(username: String(parts[0]), host: String(parts[1]))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct NIP05Response: Decodable {
|
||||
let names: [String: String]
|
||||
let relays: [String: [String]]?
|
||||
}
|
||||
|
||||
enum NIP05Validation {
|
||||
case invalid
|
||||
case valid
|
||||
}
|
||||
|
||||
func validate_nip05(pubkey: String, nip05_str: String) async -> NIP05? {
|
||||
guard let nip05 = NIP05.parse(nip05_str) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let url = nip05.url else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let ret = try? await URLSession.shared.data(from: url) else {
|
||||
return nil
|
||||
}
|
||||
let dat = ret.0
|
||||
|
||||
guard let decoded = try? JSONDecoder().decode(NIP05Response.self, from: dat) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let stored_pk = decoded.names[nip05.username] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard stored_pk == pubkey else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nip05
|
||||
}
|
||||
35
damus/Util/PreviewCache.swift
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// PreviewCache.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-02.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import LinkPresentation
|
||||
|
||||
enum Preview {
|
||||
case value(LinkViewRepresentable)
|
||||
case failed
|
||||
}
|
||||
|
||||
class PreviewCache {
|
||||
var previews: [String: Preview]
|
||||
|
||||
func lookup(_ evid: String) -> Preview? {
|
||||
return previews[evid]
|
||||
}
|
||||
|
||||
func store(evid: String, preview: LinkViewRepresentable?) {
|
||||
switch preview {
|
||||
case .none:
|
||||
previews[evid] = .failed
|
||||
case .some(let meta):
|
||||
previews[evid] = .value(meta)
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
self.previews = [:]
|
||||
}
|
||||
}
|
||||
28
damus/Util/Theme.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Theme.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Ben Weeks on 1/1/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class Theme {
|
||||
static func navigationBarColors(background : UIColor?,
|
||||
titleColor : UIColor? = nil, tintColor : UIColor? = nil ){
|
||||
|
||||
let navigationAppearance = UINavigationBarAppearance()
|
||||
navigationAppearance.configureWithOpaqueBackground()
|
||||
navigationAppearance.backgroundColor = background ?? .clear
|
||||
|
||||
navigationAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .black]
|
||||
navigationAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .black]
|
||||
|
||||
UINavigationBar.appearance().standardAppearance = navigationAppearance
|
||||
UINavigationBar.appearance().compactAppearance = navigationAppearance
|
||||
UINavigationBar.appearance().scrollEdgeAppearance = navigationAppearance
|
||||
|
||||
UINavigationBar.appearance().tintColor = tintColor ?? titleColor ?? .black
|
||||
}
|
||||
}
|
||||
@@ -24,28 +24,19 @@ struct EventActionBar: View {
|
||||
let generator = UIImpactFeedbackGenerator(style: .medium)
|
||||
@State var sheet: ActionBarSheet? = nil
|
||||
@State var confirm_boost: Bool = false
|
||||
@State var show_share_sheet: Bool = false
|
||||
@StateObject var bar: ActionBarModel
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
/*
|
||||
EventActionButton(img: "square.and.arrow.up") {
|
||||
print("share")
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
*/
|
||||
if damus_state.keypair.privkey != nil {
|
||||
EventActionButton(img: "bubble.left", col: nil) {
|
||||
notify(.reply, event)
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
HStack(alignment: .bottom) {
|
||||
Text("\(bar.boosts > 0 ? "\(bar.boosts)" : "")")
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(bar.boosted ? Color.green : Color.gray)
|
||||
|
||||
EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
|
||||
if bar.boosted {
|
||||
@@ -53,22 +44,35 @@ struct EventActionBar: View {
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
HStack(alignment: .bottom) {
|
||||
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(bar.liked ? Color.orange : Color.gray)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
EventActionButton(img: "square.and.arrow.up", col: Color.gray) {
|
||||
show_share_sheet = true
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
/*
|
||||
HStack(alignment: .bottom) {
|
||||
@@ -86,15 +90,22 @@ struct EventActionBar: View {
|
||||
}
|
||||
*/
|
||||
}
|
||||
.alert("Boost", isPresented: $confirm_boost) {
|
||||
Button("Cancel") {
|
||||
.sheet(isPresented: $show_share_sheet) {
|
||||
if let note_id = bech32_note_id(event.id) {
|
||||
if let url = URL(string: "https://damus.io/" + note_id) {
|
||||
ShareSheet(activityItems: [url])
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(NSLocalizedString("Repost", comment: "Title of alert for confirming to repost a post."), isPresented: $confirm_boost) {
|
||||
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of reposting a post.")) {
|
||||
confirm_boost = false
|
||||
}
|
||||
Button("Boost") {
|
||||
Button(NSLocalizedString("Repost", comment: "Button to confirm reposting a post.")) {
|
||||
send_boost()
|
||||
}
|
||||
} message: {
|
||||
Text("Are you sure you want to boost this post?")
|
||||
Text("Are you sure you want to repost this?", comment: "Alert message to ask if user wants to repost a post.")
|
||||
}
|
||||
.onReceive(handle_notify(.liked)) { n in
|
||||
let liked = n.object as! Counted
|
||||
@@ -138,11 +149,10 @@ struct EventActionBar: View {
|
||||
|
||||
func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) -> some View {
|
||||
Button(action: action) {
|
||||
Label(" ", systemImage: img)
|
||||
Label(NSLocalizedString("\u{00A0}", comment: "Non-breaking space character to fill in blank space next to event action button icons."), systemImage: img)
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(col == nil ? Color.gray : col!)
|
||||
}
|
||||
.padding(.trailing, 40)
|
||||
}
|
||||
|
||||
struct LikeButton: View {
|
||||
@@ -153,13 +163,8 @@ struct LikeButton: View {
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
if liked {
|
||||
Text("🤙")
|
||||
} else {
|
||||
Label(" ", systemImage: "hand.thumbsup")
|
||||
.font(.footnote.weight(.medium))
|
||||
.foregroundColor(Color.gray)
|
||||
}
|
||||
Image(liked ? "shaka-full" : "shaka-line")
|
||||
.foregroundColor(liked ? .accentColor : .gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,8 +174,19 @@ struct EventActionBar_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let pk = "pubkey"
|
||||
let ds = test_damus_state()
|
||||
let bar = ActionBarModel(likes: 0, boosts: 0, tips: 0, our_like: nil, our_boost: nil, our_tip: nil)
|
||||
let ev = NostrEvent(content: "hi", pubkey: pk)
|
||||
EventActionBar(damus_state: ds, event: ev, bar: bar)
|
||||
|
||||
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)
|
||||
|
||||
VStack(spacing: 50) {
|
||||
EventActionBar(damus_state: ds, event: ev, bar: bar)
|
||||
|
||||
EventActionBar(damus_state: ds, event: ev, bar: likedbar)
|
||||
|
||||
EventActionBar(damus_state: ds, event: ev, bar: likedbar_ours)
|
||||
}
|
||||
.padding(20)
|
||||
}
|
||||
}
|
||||
39
damus/Views/ActionBar/EventDetailBar.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// EventDetailBar.swift
|
||||
// damus
|
||||
//
|
||||
// Created by William Casarin on 2023-01-08.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct EventDetailBar: View {
|
||||
let state: DamusState
|
||||
let target: String
|
||||
@StateObject var bar: ActionBarModel
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if bar.boosts > 0 {
|
||||
Text("\(Text("\(bar.boosts)", comment: "Number of reposts.").font(.body.bold())) \(Text(String(format: NSLocalizedString("reposts_count", comment: "Part of a larger sentence to describe how many reposts there are."), bar.boosts)).foregroundColor(.gray))", comment: "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'.")
|
||||
}
|
||||
|
||||
if bar.likes > 0 {
|
||||
NavigationLink(destination: ReactionsView(damus_state: state, model: ReactionsModel(state: state, target: target))) {
|
||||
Text("\(Text("\(bar.likes)", comment: "Number of reactions on a post.").font(.body.bold())) \(Text(String(format: NSLocalizedString("reactions_count", comment: "Part of a larger sentence to describe how many reactions there are on a post."), bar.likes)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many reactions there are on a post. In source English, the first variable is the number of reactions, and the second variable is 'Reaction' or 'Reactions'.")
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
|
||||
if bar.tips > 0 {
|
||||
Text("\(Text("\(bar.tips)", comment: "Number of tip payments on a post.").font(.body.bold())) \(Text(String(format: NSLocalizedString("tips_count", comment: "Part of a larger sentence to describe how many tip payments there are on a post."), bar.boosts)).foregroundColor(.gray))", comment: "Sentence composed of 2 variables to describe how many tip payments there are on a post. In source English, the first variable is the number of tip payments, and the second variable is 'Tip' or 'Tips'.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EventDetailBar_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
EventDetailBar(state: test_damus_state(), target: "", bar: ActionBarModel.empty())
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,10 @@ struct AddRelayView: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Form {
|
||||
Section("Add Relay") {
|
||||
Section(NSLocalizedString("Add Relay", comment: "Label for section for adding a relay server.")) {
|
||||
ZStack(alignment: .leading) {
|
||||
HStack{
|
||||
TextField("wss://some.relay.com", text: $relay)
|
||||
TextField(NSLocalizedString("wss://some.relay.com", comment: "Placeholder example for relay server address."), text: $relay)
|
||||
.padding(2)
|
||||
.padding(.leading, 25)
|
||||
.autocorrectionDisabled(true)
|
||||
@@ -47,7 +47,7 @@ struct AddRelayView: View {
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Button("Cancel") {
|
||||
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted relay.")) {
|
||||
show_add_relay = false
|
||||
action(nil)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ struct AddRelayView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
Button("Add") {
|
||||
Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted relay.")) {
|
||||
show_add_relay = false
|
||||
action(relay)
|
||||
relay = ""
|
||||
|
||||
85
damus/Views/BannerImageView.swift
Normal file
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// BannerImageView.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Jason Jōb on 2023-01-10.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
struct InnerBannerImageView: View {
|
||||
let url: URL?
|
||||
let pubkey: String
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color(uiColor: .systemBackground)
|
||||
|
||||
if (url != nil) {
|
||||
KFAnimatedImage(url)
|
||||
.callbackQueue(.dispatch(.global(qos: .background)))
|
||||
.processingQueue(.dispatch(.global(qos: .background)))
|
||||
.appendProcessor(LargeImageProcessor.shared)
|
||||
.configure { view in
|
||||
view.framePreloadCount = 1
|
||||
}
|
||||
.placeholder { _ in
|
||||
Image("profile-banner").resizable()
|
||||
}
|
||||
.scaleFactor(UIScreen.main.scale)
|
||||
.loadDiskFileSynchronously()
|
||||
.fade(duration: 0.1)
|
||||
} else {
|
||||
Image("profile-banner").resizable()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BannerImageView: View {
|
||||
let pubkey: String
|
||||
let profiles: Profiles
|
||||
|
||||
@State var banner: String?
|
||||
|
||||
init (pubkey: String, profiles: Profiles, banner: String? = nil) {
|
||||
self.pubkey = pubkey
|
||||
self.profiles = profiles
|
||||
self._banner = State(initialValue: banner)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
InnerBannerImageView(url: get_banner_url(banner: banner, pubkey: pubkey, profiles: profiles), pubkey: pubkey)
|
||||
.onReceive(handle_notify(.profile_updated)) { notif in
|
||||
let updated = notif.object as! ProfileUpdate
|
||||
|
||||
guard updated.pubkey == self.pubkey else {
|
||||
return
|
||||
}
|
||||
|
||||
if let bannerImage = updated.profile.banner {
|
||||
self.banner = bannerImage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func get_banner_url(banner: String?, pubkey: String, profiles: Profiles) -> URL? {
|
||||
let bannerUrlString = banner ?? profiles.lookup(id: pubkey)?.banner ?? ""
|
||||
if let url = URL(string: bannerUrlString) {
|
||||
return url
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
struct BannerImageView_Previews: PreviewProvider {
|
||||
static let pubkey = "ca48854ac6555fed8e439ebb4fa2d928410e0eef13fa41164ec45aaaa132d846"
|
||||
|
||||
static var previews: some View {
|
||||
BannerImageView(
|
||||
pubkey: pubkey,
|
||||
profiles: make_preview_profiles(pubkey))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ struct CarouselItem: Identifiable {
|
||||
}
|
||||
|
||||
let carousel_items = [
|
||||
CarouselItem(image: Image("digital-nomad"), text: Text("Welcome to the social network \(Text("you").italic()) control.")),
|
||||
CarouselItem(image: Image("digital-nomad"), text: Text("Welcome to the social network \(Text("you", comment: "You, in this context, is the person who controls their own social network. You is used in the context of a larger sentence that welcomes the reader to the social network that they control themself.").italic()) control.", comment: "Welcoming message to the reader. The variable is 'you', the reader.")),
|
||||
CarouselItem(image: Image("encrypted-message"),
|
||||
text: Text("\(Text("Encrypted").bold()). End-to-End encrypted private messaging. Keep Big Tech out of your DMs")),
|
||||
text: Text("\(Text("Encrypted", comment: "Heading indicating that this application keeps private messaging end-to-end encrypted.").bold()). End-to-End encrypted private messaging. Keep Big Tech out of your DMs", comment: "Explanation of what is done to keep private data encrypted. There is a heading that precedes this explanation which is a variable to this string.")),
|
||||
CarouselItem(image: Image("undercover"),
|
||||
text: Text("\(Text("Private").bold()). Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.")),
|
||||
text: Text("\(Text("Private", comment: "Heading indicating that this application keeps personally identifiable information private. A sentence describing what is done to keep data private comes after this heading.").bold()). Creating an account doesn't require a phone number, email or name. Get started right away with zero friction.", comment: "Explanation of what is done to keep personally identifiable information private. There is a heading that precedes this explanation which is a variable to this string.")),
|
||||
CarouselItem(image: Image("bitcoin-p2p"),
|
||||
text: Text("\(Text("Earn Money").bold()). Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet."))
|
||||
text: Text("\(Text("Earn Money", comment: "Heading indicating that this application allows users to earn money.").bold()). Tip your friend's posts and stack sats with Bitcoin⚡️, the native currency of the internet.", comment: "Explanation of what can be done by users to earn money. There is a heading that precedes this explanation which is a variable to this string."))
|
||||
]
|
||||
|
||||
struct CarouselView: View {
|
||||
|
||||
@@ -86,7 +86,7 @@ struct ChatView: View {
|
||||
VStack(alignment: .leading) {
|
||||
if just_started {
|
||||
HStack {
|
||||
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: event.pubkey, profile: damus_state.profiles.lookup(id: event.pubkey), damus: damus_state, show_friend_confirmed: true)
|
||||
.foregroundColor(colorScheme == .dark ? id_to_color(event.pubkey) : Color.black)
|
||||
//.shadow(color: Color.black, radius: 2)
|
||||
Text("\(format_relative_time(event.created_at))")
|
||||
@@ -96,7 +96,7 @@ struct ChatView: View {
|
||||
|
||||
if let ref_id = thread.replies.lookup(event.id) {
|
||||
if !is_reply_to_prev() {
|
||||
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles)
|
||||
ReplyQuoteView(privkey: damus_state.keypair.privkey, quoter: event, event_id: ref_id, profiles: damus_state.profiles, previews: damus_state.previews)
|
||||
.frame(maxHeight: expand_reply ? nil : 100)
|
||||
.environmentObject(thread)
|
||||
.onTapGesture {
|
||||
@@ -106,7 +106,7 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey), artifacts: .just_content(event.content), size: .normal)
|
||||
|
||||
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||
let bar = make_actionbar_model(ev: event, damus: damus_state)
|
||||
|
||||
@@ -24,7 +24,7 @@ struct ChatroomView: View {
|
||||
next_ev: ind == count-1 ? nil : thread.events[ind+1],
|
||||
damus_state: damus
|
||||
)
|
||||
.event_context_menu(ev, privkey: damus.keypair.privkey)
|
||||
.event_context_menu(ev, pubkey: ev.pubkey, privkey: damus.keypair.privkey)
|
||||
.onTapGesture {
|
||||
if thread.initial_event.id == ev.id {
|
||||
//dismiss()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
import AVFoundation
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
|
||||
struct ConfigView: View {
|
||||
let state: DamusState
|
||||
@@ -54,19 +55,30 @@ struct ConfigView: View {
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
Form {
|
||||
Section("Relays") {
|
||||
Section {
|
||||
List(Array(relays), id: \.url) { relay in
|
||||
RelayView(state: state, relay: relay.url.absoluteString)
|
||||
}
|
||||
}
|
||||
|
||||
Section("Recommended Relays") {
|
||||
List(recommended, id: \.url) { r in
|
||||
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Relays", comment: "Header text for relay server list for configuration.")
|
||||
Spacer()
|
||||
Button(action: { show_add_relay = true }) {
|
||||
Image(systemName: "plus")
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section("Public Account ID") {
|
||||
if recommended.count > 0 {
|
||||
Section(NSLocalizedString("Recommended Relays", comment: "Section title for recommend relay servers that could be added as part of configuration")) {
|
||||
List(recommended, id: \.url) { r in
|
||||
RecommendedRelayView(damus: state, relay: r.url.absoluteString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section(NSLocalizedString("Public Account ID", comment: "Section title for the user's public account ID.")) {
|
||||
HStack {
|
||||
Text(state.keypair.pubkey_bech32)
|
||||
|
||||
@@ -76,10 +88,10 @@ struct ConfigView: View {
|
||||
}
|
||||
|
||||
if let sec = state.keypair.privkey_bech32 {
|
||||
Section("Secret Account Login Key") {
|
||||
Section(NSLocalizedString("Secret Account Login Key", comment: "Section title for user's secret account login key.")) {
|
||||
HStack {
|
||||
if show_privkey == false {
|
||||
SecureField("PrivateKey", text: $privkey)
|
||||
SecureField(NSLocalizedString("PrivateKey", comment: "Title of the secure field that holds the user's private key."), text: $privkey)
|
||||
.disabled(true)
|
||||
} else {
|
||||
Text(sec)
|
||||
@@ -89,13 +101,13 @@ struct ConfigView: View {
|
||||
CopyButton(is_pk: false)
|
||||
}
|
||||
|
||||
Toggle("Show", isOn: $show_privkey)
|
||||
Toggle(NSLocalizedString("Show", comment: "Toggle to show or hide user's secret account login key."), isOn: $show_privkey)
|
||||
}
|
||||
}
|
||||
|
||||
Section("Wallet Selector") {
|
||||
Toggle("Show wallet selector", isOn: $user_settings.show_wallet_selector).toggleStyle(.switch)
|
||||
Picker("Select default wallet",
|
||||
Section(NSLocalizedString("Wallet Selector", comment: "Section title for selection of wallet.")) {
|
||||
Toggle(NSLocalizedString("Show wallet selector", comment: "Toggle to show or hide selection of wallet."), isOn: $user_settings.show_wallet_selector).toggleStyle(.switch)
|
||||
Picker(NSLocalizedString("Select default wallet", comment: "Prompt selection of user's default wallet"),
|
||||
selection: $user_settings.default_wallet) {
|
||||
ForEach(Wallet.allCases, id: \.self) { wallet in
|
||||
Text(wallet.model.displayName)
|
||||
@@ -103,50 +115,53 @@ struct ConfigView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section("Reset") {
|
||||
Button("Logout") {
|
||||
|
||||
Section(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen")) {
|
||||
Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $user_settings.left_handed)
|
||||
.toggleStyle(.switch)
|
||||
}
|
||||
|
||||
Section(NSLocalizedString("Clear Cache", comment: "Section title for clearing cached data.")) {
|
||||
Button(NSLocalizedString("Clear", comment: "Button for clearing cached data.")) {
|
||||
KingfisherManager.shared.cache.clearMemoryCache()
|
||||
KingfisherManager.shared.cache.clearDiskCache()
|
||||
KingfisherManager.shared.cache.cleanExpiredDiskCache()
|
||||
}
|
||||
}
|
||||
|
||||
Section(NSLocalizedString("Reset", comment: "Section title for resetting the user")) {
|
||||
Button(NSLocalizedString("Logout", comment: "Button to logout the user.")) {
|
||||
confirm_logout = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button(action: { show_add_relay = true }) {
|
||||
Label("", systemImage: "plus")
|
||||
.foregroundColor(.accentColor)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
.navigationTitle(NSLocalizedString("Settings", comment: "Navigation title for Settings view."))
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
.alert("Logout", isPresented: $confirm_logout) {
|
||||
Button("Cancel") {
|
||||
.alert(NSLocalizedString("Logout", comment: "Alert for logging out the user."), isPresented: $confirm_logout) {
|
||||
Button(NSLocalizedString("Cancel", comment: "Cancel out of logging out the user.")) {
|
||||
confirm_logout = false
|
||||
}
|
||||
Button("Logout") {
|
||||
Button(NSLocalizedString("Logout", comment: "Button for logging out the user.")) {
|
||||
notify(.logout, ())
|
||||
}
|
||||
} message: {
|
||||
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account")
|
||||
Text("Make sure your nsec account key is saved before you logout or you will lose access to this account", comment: "Reminder message in alert to get customer to verify that their private security account key is saved saved before logging out.")
|
||||
}
|
||||
.sheet(isPresented: $show_add_relay) {
|
||||
AddRelayView(show_add_relay: $show_add_relay, relay: $new_relay) { m_relay in
|
||||
guard let relay = m_relay else {
|
||||
guard var relay = m_relay else {
|
||||
return
|
||||
}
|
||||
|
||||
if relay.starts(with: "wss://") == false {
|
||||
relay = "wss://" + relay
|
||||
}
|
||||
|
||||
guard let url = URL(string: relay) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let ev = state.contacts.event else {
|
||||
return
|
||||
}
|
||||
@@ -161,9 +176,9 @@ struct ConfigView: View {
|
||||
return
|
||||
}
|
||||
|
||||
state.pool.connect(to: [new_relay])
|
||||
state.pool.connect(to: [relay])
|
||||
|
||||
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: new_relay, info: info) else {
|
||||
guard let new_ev = add_relay(ev: ev, privkey: privkey, current_relays: state.pool.descriptors, relay: relay, info: info) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -35,14 +35,14 @@ struct CreateAccountView: View {
|
||||
|
||||
HStack(alignment: .top) {
|
||||
VStack {
|
||||
Text(" ")
|
||||
Text(" ", comment: "Blank space to separate profile picture from profile editor form.")
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
VStack {
|
||||
SignupForm {
|
||||
FormLabel(NSLocalizedString("Username", comment: "Label to prompt username entry."))
|
||||
HStack(spacing: 0.0) {
|
||||
Text("@")
|
||||
Text("@", comment: "Prefix character to username.")
|
||||
.foregroundColor(.white)
|
||||
.padding(.leading, -25.0)
|
||||
|
||||
@@ -151,7 +151,7 @@ func FormLabel(_ title: String, optional: Bool = false) -> some View {
|
||||
.bold()
|
||||
.foregroundColor(.white)
|
||||
if optional {
|
||||
Text("optional")
|
||||
Text("optional", comment: "Label indicating that a form input is optional.")
|
||||
.font(.callout)
|
||||
.foregroundColor(.white.opacity(0.5))
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ struct DMChatView: View {
|
||||
VStack(alignment: .leading) {
|
||||
ForEach(Array(zip(dms.events, dms.events.indices)), id: \.0.id) { (ev, ind) in
|
||||
DMView(event: dms.events[ind], damus_state: damus_state)
|
||||
.event_context_menu(ev, privkey: damus_state.keypair.privkey)
|
||||
.event_context_menu(ev, pubkey: ev.pubkey, privkey: damus_state.keypair.privkey)
|
||||
}
|
||||
EndBlock(height: 80)
|
||||
}
|
||||
@@ -27,6 +27,10 @@ struct DMChatView: View {
|
||||
}
|
||||
.onAppear {
|
||||
scroller.scrollTo("endblock")
|
||||
}.onChange(of: dms.events.count) { _ in
|
||||
withAnimation {
|
||||
scroller.scrollTo("endblock")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +44,7 @@ struct DMChatView: View {
|
||||
HStack {
|
||||
ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles)
|
||||
|
||||
ProfileName(pubkey: pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: true)
|
||||
ProfileName(pubkey: pubkey, profile: profile, damus: damus_state, show_friend_confirmed: true)
|
||||
}
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
@@ -138,14 +142,14 @@ struct DMChatView: View {
|
||||
|
||||
Footer
|
||||
}
|
||||
Text("Send a message to start the conversation...")
|
||||
Text("Send a message to start the conversation...", comment: "Text prompt for user to send a message to the other user.")
|
||||
.lineLimit(nil)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 40)
|
||||
.opacity(((dms.events.count == 0) ? 1.0 : 0.0))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.navigationTitle("DM")
|
||||
.navigationTitle(NSLocalizedString("DM", comment: "Navigation title for DM view, which is the English abbreviation for Direct Message."))
|
||||
.toolbar { Header }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,25 +10,28 @@ import SwiftUI
|
||||
struct DMView: View {
|
||||
let event: NostrEvent
|
||||
let damus_state: DamusState
|
||||
|
||||
|
||||
var is_ours: Bool {
|
||||
event.pubkey == damus_state.pubkey
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if is_ours {
|
||||
Spacer()
|
||||
Spacer(minLength: UIScreen.main.bounds.width * 0.2)
|
||||
}
|
||||
|
||||
|
||||
let should_show_img = should_show_images(contacts: damus_state.contacts, ev: event, our_pubkey: damus_state.pubkey)
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
|
||||
|
||||
NoteContentView(privkey: damus_state.keypair.privkey, event: event, profiles: damus_state.profiles, previews: damus_state.previews, show_images: should_show_img, artifacts: .just_content(event.get_content(damus_state.keypair.privkey)), size: .normal)
|
||||
.foregroundColor(is_ours ? Color.white : Color.primary)
|
||||
.padding(10)
|
||||
.background(is_ours ? Color.accentColor : Color.secondary.opacity(0.15))
|
||||
.cornerRadius(8.0)
|
||||
.tint(is_ours ? Color.white : Color.accentColor)
|
||||
if !is_ours {
|
||||
Spacer(minLength: UIScreen.main.bounds.width * 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ struct DirectMessagesView: View {
|
||||
|
||||
var body: some View {
|
||||
MainContent
|
||||
.navigationTitle("Encrypted DMs")
|
||||
.navigationTitle(NSLocalizedString("Encrypted DMs", comment: "Navigation title for view of encrypted DMs, where DM is an English abbreviation for Direct Message."))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import SwiftUI
|
||||
|
||||
let PPM_SIZE: CGFloat = 80.0
|
||||
let BANNER_HEIGHT: CGFloat = 150.0;
|
||||
|
||||
func isHttpsUrl(_ string: String) -> Bool {
|
||||
let urlRegEx = "^https://.*$"
|
||||
@@ -15,11 +16,6 @@ func isHttpsUrl(_ string: String) -> Bool {
|
||||
return urlTest.evaluate(with: string)
|
||||
}
|
||||
|
||||
struct NIP05 {
|
||||
let username: String
|
||||
let host: String
|
||||
}
|
||||
|
||||
func isImage(_ urlString: String) -> Bool {
|
||||
let imageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/tiff", "image/bmp", "image/webp"]
|
||||
|
||||
@@ -61,12 +57,14 @@ struct EditMetadataView: View {
|
||||
@State var display_name: String
|
||||
@State var about: String
|
||||
@State var picture: String
|
||||
@State var banner: String
|
||||
@State var nip05: String
|
||||
@State var name: String
|
||||
@State var ln: String
|
||||
@State var website: String
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
init (damus_state: DamusState) {
|
||||
self.damus_state = damus_state
|
||||
@@ -77,10 +75,15 @@ struct EditMetadataView: View {
|
||||
_about = State(initialValue: data?.about ?? "")
|
||||
_website = State(initialValue: data?.website ?? "")
|
||||
_picture = State(initialValue: data?.picture ?? "")
|
||||
_banner = State(initialValue: data?.banner ?? "")
|
||||
_nip05 = State(initialValue: data?.nip05 ?? "")
|
||||
_ln = State(initialValue: data?.lud16 ?? data?.lud06 ?? "")
|
||||
}
|
||||
|
||||
func imageBorderColor() -> Color {
|
||||
colorScheme == .light ? Color("DamusWhite") : Color("DamusBlack")
|
||||
}
|
||||
|
||||
func save() {
|
||||
let metadata = NostrMetadata(
|
||||
display_name: display_name,
|
||||
@@ -89,6 +92,7 @@ struct EditMetadataView: View {
|
||||
website: website,
|
||||
nip05: nip05.isEmpty ? nil : nip05,
|
||||
picture: picture.isEmpty ? nil : picture,
|
||||
banner: banner.isEmpty ? nil : banner,
|
||||
lud06: ln.contains("@") ? nil : ln,
|
||||
lud16: ln.contains("@") ? ln : nil
|
||||
);
|
||||
@@ -101,47 +105,68 @@ struct EditMetadataView: View {
|
||||
}
|
||||
|
||||
var nip05_parts: NIP05? {
|
||||
let parts = nip05.split(separator: "@")
|
||||
guard parts.count == 2 else {
|
||||
return nil
|
||||
return NIP05.parse(nip05)
|
||||
}
|
||||
|
||||
var TopSection: some View {
|
||||
ZStack(alignment: .top) {
|
||||
GeometryReader { geo in
|
||||
BannerImageView(pubkey: damus_state.pubkey, profiles: damus_state.profiles)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: geo.size.width, height: BANNER_HEIGHT)
|
||||
.clipped()
|
||||
}.frame(height: BANNER_HEIGHT)
|
||||
VStack(alignment: .leading) {
|
||||
let pfp_size: CGFloat = 90.0
|
||||
|
||||
HStack(alignment: .center) {
|
||||
ProfilePicView(pubkey: damus_state.pubkey, size: pfp_size, highlight: .custom(imageBorderColor(), 4.0), profiles: damus_state.profiles)
|
||||
.offset(y: -(pfp_size/2.0)) // Increase if set a frame
|
||||
|
||||
Spacer()
|
||||
}.padding(.bottom,-(pfp_size/2.0))
|
||||
}
|
||||
.padding(.horizontal,18)
|
||||
.padding(.top,BANNER_HEIGHT)
|
||||
}
|
||||
return NIP05(username: String(parts[0]), host: String(parts[1]))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Spacer()
|
||||
InnerProfilePicView(url: URL(string: picture), pubkey: damus_state.pubkey, size: PPM_SIZE, highlight: .none)
|
||||
Spacer()
|
||||
}
|
||||
TopSection
|
||||
Form {
|
||||
Section("Your Name") {
|
||||
Section(NSLocalizedString("Your Name", comment: "Label for Your Name section of user profile form.")) {
|
||||
TextField("Satoshi Nakamoto", text: $display_name)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section("Username") {
|
||||
Section(NSLocalizedString("Username", comment: "Label for Username section of user profile form.")) {
|
||||
TextField("satoshi", text: $name)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
|
||||
}
|
||||
|
||||
Section ("Profile Picture") {
|
||||
TextField("https://example.com/pic.jpg", text: $picture)
|
||||
Section (NSLocalizedString("Profile Picture", comment: "Label for Profile Picture section of user profile form.")) {
|
||||
TextField(NSLocalizedString("https://example.com/pic.jpg", comment: "Placeholder example text for profile picture URL."), text: $picture)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section("Website") {
|
||||
TextField("https://jb55.com", text: $website)
|
||||
Section (NSLocalizedString("Banner Image", comment: "Label for Banner Image section of user profile form.")) {
|
||||
TextField(NSLocalizedString("https://example.com/pic.jpg", comment: "Placeholder example text for profile picture URL."), text: $banner)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section(NSLocalizedString("Website", comment: "Label for Website section of user profile form.")) {
|
||||
TextField(NSLocalizedString("https://jb55.com", comment: "Placeholder example text for website URL for user profile."), text: $website)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section("About Me") {
|
||||
Section(NSLocalizedString("About Me", comment: "Label for About Me section of user profile form.")) {
|
||||
let placeholder = NSLocalizedString("Absolute Boss", comment: "Placeholder text for About Me description.")
|
||||
ZStack(alignment: .topLeading) {
|
||||
TextEditor(text: $about)
|
||||
@@ -155,33 +180,33 @@ struct EditMetadataView: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section("Bitcoin Lightning Tips") {
|
||||
TextField("Lightning Address or LNURL", text: $ln)
|
||||
Section(NSLocalizedString("Bitcoin Lightning Tips", comment: "Label for Bitcoin Lightning Tips section of user profile form.")) {
|
||||
TextField(NSLocalizedString("Lightning Address or LNURL", comment: "Placeholder text for entry of Lightning Address or LNURL."), text: $ln)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}
|
||||
|
||||
Section(content: {
|
||||
TextField("jb55@jb55.com", text: $nip05)
|
||||
TextField(NSLocalizedString("jb55@jb55.com", comment: "Placeholder example text for identifier used for NIP-05 verification."), text: $nip05)
|
||||
.autocorrectionDisabled(true)
|
||||
.textInputAutocapitalization(.never)
|
||||
}, header: {
|
||||
Text("NIP-05 Verification")
|
||||
Text("NIP-05 Verification", comment: "Label for NIP-05 Verification section of user profile form.")
|
||||
}, footer: {
|
||||
if let parts = nip05_parts {
|
||||
Text(String.localizedStringWithFormat("'%@' at '%@' will be used for verification", parts.username, parts.host))
|
||||
Text("'\(parts.username)' at '\(parts.host)' will be used for verification", comment: "Description of how the nip05 identifier would be used for verification.")
|
||||
} else {
|
||||
Text(String.localizedStringWithFormat("'%@' is an invalid nip05 identifier. It should look like an email.", nip05))
|
||||
Text("'\(nip05)' is an invalid nip05 identifier. It should look like an email.", comment: "Description of why the nip05 identifier is invalid.")
|
||||
}
|
||||
})
|
||||
|
||||
Button("Save") {
|
||||
|
||||
Button(NSLocalizedString("Save", comment: "Button for saving profile.")) {
|
||||
save()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Edit Profile")
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ struct EmptyTimelineView: View {
|
||||
Image(systemName: "tray.fill")
|
||||
.font(.system(size: 35))
|
||||
.padding()
|
||||
Text("Nothing to see here. Check back later!")
|
||||
Text("Nothing to see here. Check back later!", comment: "Indicates that there are no notes in the timeline to view.")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.callout.weight(.medium))
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ struct EventDetailView: View {
|
||||
Group {
|
||||
switch cev {
|
||||
case .collapsed(let c):
|
||||
Text("··· \(c.count) other notes ···")
|
||||
Text(String(format: NSLocalizedString("collapsed_event_view_other_notes", comment: "Text to indicate that the thread was collapsed and that there are other notes to view if tapped."), c.count))
|
||||
.padding([.top,.bottom], 8)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
@@ -114,7 +114,7 @@ struct EventDetailView: View {
|
||||
scroll_after_load(thread: thread, proxy: proxy)
|
||||
}
|
||||
}
|
||||
.navigationBarTitle("Thread")
|
||||
.navigationBarTitle(NSLocalizedString("Thread", comment: "Navigation bar title for note thread."))
|
||||
|
||||
}
|
||||
|
||||
|
||||