Compare commits

...

12 Commits

Author SHA1 Message Date
1f634e6e9a Fix relative time test 2023-01-02 12:21:20 -05:00
William Casarin
ed36a8c062 Add brb.io to recommended relay list
Changelog-Added: Added brb.io to recommended relay list
2023-01-02 08:44:54 -08:00
Thomas Rademaker
2f81a144c1 ContentTimelineView inside a page tabView to get a nice swipe gesture
Changelog-Changed: Add swipe gesture to switch between tabs
Closes: #202
2023-01-02 08:42:03 -08:00
William Casarin
4adb26e784 wallet: refactor a few things 2023-01-02 08:39:39 -08:00
Benjamin Hakes
707f31fca1 Add Blixt & River to Wallet Selector
Changelog-Added: Add Blixt Wallet to Wallet Selector
Changelog-Added: Add River Wallet to Wallet Selector
Closes: #206
2023-01-02 08:38:37 -08:00
9e9077bab5 Internationalize all bundled user-facing strings
Enables localization to non-English locales in the future
2023-01-02 08:35:02 -08:00
@RandyMcMillan
7242c0bca3 target: decrement to 15.0
Closes: #207
2023-01-02 08:22:52 -08:00
badbd8a92e Fix flakey TimeAgo test by anchoring locale to en_US
Closes: #210
2023-01-02 08:22:17 -08:00
Lio李歐
aec61906ae Update CHANGELOG.md
Hey @jb55. I appreciate everything you’re doing for nostr but that
particular change was done by me. Not sure how come your name ended up
besides it. I was a bit bummed when I saw the latest changelog in
TestFlight.

Changelog-Changed: Parse links in profiles (Lionello Lunesu)
Closes: #208
2023-01-02 08:21:25 -08:00
4f4557e4b6 Replace deprecated Data.withUnsafeMutableBytes call
Closes: #214
2023-01-02 08:19:47 -08:00
William Casarin
9c6b802f19 Fix follows and relays getting out of sync on profile pages
Fixes: #216
Changelog-Fixed: Fixed follows and relays getting out of sync on profile pages
2023-01-02 08:03:09 -08:00
William Casarin
4c7b8dc10b app category 2023-01-01 11:47:15 -08:00
24 changed files with 231 additions and 99 deletions

View File

@@ -2,7 +2,7 @@
### Added
- Parse links in profiles (William Casarin)
- Parse links in profiles (Lionello Lunesu)
- Added Breez wallet to wallet selector (Lee Salminen)
- Added Bitcoin Beach wallet to wallet selector (Lee Salminen)
- Added ability to copy relay urls (Matt Ward)

View File

@@ -11,6 +11,7 @@
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAE5294E69C000EE4006 /* EmptyTimelineView.swift */; };
3169CAED294FCCFC00EE4006 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3169CAEC294FCCFC00EE4006 /* Constants.swift */; };
31D2E847295218AF006D67F8 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
@@ -164,6 +165,7 @@
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>"; };
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>"; };
@@ -593,6 +595,7 @@
4CE6DEE827F7A08100C66700 /* ContentView.swift */,
4CE6DEEA27F7A08200C66700 /* Assets.xcassets */,
4CE6DEEC27F7A08200C66700 /* Preview Content */,
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */,
);
path = damus;
sourceTree = "<group>";
@@ -755,6 +758,7 @@
files = (
4CE6DEEE27F7A08200C66700 /* Preview Assets.xcassets in Resources */,
4CE6DEEB27F7A08200C66700 /* Assets.xcassets in Resources */,
3A4325A82961E11400BFCD9D /* Localizable.stringsdict in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -938,6 +942,17 @@
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */ = {
isa = PBXVariantGroup;
children = (
3A4325A92961E11400BFCD9D /* en */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
4CE6DF0527F7A08200C66700 /* Debug */ = {
isa = XCBuildConfiguration;
@@ -989,7 +1004,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
@@ -1044,7 +1059,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MACOSX_DEPLOYMENT_TARGET = 12.3;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
@@ -1070,6 +1085,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -1109,6 +1125,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = damus/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Damus;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -11,9 +11,20 @@ func open_with_wallet(wallet: Wallet.Model, invoice: String) {
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {
if let url = URL(string: wallet.appStoreLink), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
guard let store_link = wallet.appStoreLink else {
// TODO: do something here if we don't have an appstore link
return
}
guard let url = URL(string: store_link) else {
return
}
guard UIApplication.shared.canOpenURL(url) else {
return
}
UIApplication.shared.open(url)
}
}

View File

@@ -16,7 +16,7 @@ var BOOTSTRAP_RELAYS = [
"wss://relay.nostr.bg",
"wss://nostr.oxtr.dev",
"wss://nostr.v0l.io",
"wss://nostr-2.zebedee.cloud",
"wss://brb.io",
]
struct TimestampedProfile {
@@ -82,17 +82,14 @@ struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
var PostingTimelineView: some View {
VStack{
ZStack {
if let damus = self.damus_state {
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
}
if privkey != nil {
PostButtonContainer {
self.active_sheet = .post
}
}
}.ignoresSafeArea(.keyboard, edges: .bottom)
VStack {
TabView(selection: $filter_state) {
ContentTimelineView
.tag(FilterState.posts)
ContentTimelineView
.tag(FilterState.posts_and_replies)
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
.safeAreaInset(edge: .top) {
VStack(spacing: 0) {
@@ -106,6 +103,19 @@ struct ContentView: View {
}
}
var ContentTimelineView: some View {
ZStack {
if let damus = self.damus_state {
TimelineView(events: $home.events, loading: $home.loading, damus: damus, show_friend_icon: false, filter: filter_event)
}
if privkey != nil {
PostButtonContainer {
self.active_sheet = .post
}
}
}
}
var FiltersView: some View {
VStack{
Picker("Filter State", selection: $filter_state) {

View File

@@ -17,6 +17,7 @@
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>river</string>
<string>bitcoinbeach</string>
<string>breez</string>
<string>muun</string>
@@ -29,6 +30,7 @@
<string>strike</string>
<string>bluewallet</string>
<string>walletofsatoshi</string>
<string>blixtwallet</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>

View File

@@ -61,7 +61,7 @@ class ProfileModel: ObservableObject, Equatable {
profile_filter.authors = [pubkey]
text_filter.authors = [pubkey]
text_filter.limit = 1000
text_filter.limit = 500
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])
@@ -70,12 +70,18 @@ class ProfileModel: ObservableObject, Equatable {
}
func handle_profile_contact_event(_ ev: NostrEvent) {
process_contact_event(pool: damus.pool, contacts: damus.contacts, pubkey: damus.pubkey, ev: ev)
// only use new stuff
if let current_ev = self.contacts {
guard ev.created_at > current_ev.created_at else {
return
}
}
self.contacts = ev
self.following = count_pubkeys(ev.tags)
self.relays = decode_json_relays(ev.content)
if damus.contacts.is_friend(ev.pubkey) {
self.damus.contacts.add_friend_contact(ev)
}
}
func add_event(_ ev: NostrEvent) {
@@ -90,6 +96,8 @@ class ProfileModel: ObservableObject, Equatable {
let _ = insert_uniq_sorted_event(events: &self.events, new_ev: ev, cmp: { $0.created_at > $1.created_at})
} else if ev.known_kind == .contacts {
handle_profile_contact_event(ev)
} else if ev.known_kind == .metadata {
process_metadata_event(profiles: damus.profiles, ev: ev)
}
seen_event.insert(ev.id)
}

View File

@@ -16,7 +16,7 @@ enum Wallet: String, CaseIterable, Identifiable {
var tag: String
var displayName : String
var link : String
var appStoreLink : String
var appStoreLink : String?
var image: String
}
@@ -33,44 +33,53 @@ enum Wallet: String, CaseIterable, Identifiable {
case phoenix
case breez
case bitcoinbeach
case blixtwallet
case river
var model: Model {
switch self {
case .system_default_wallet:
return .init(index: -1, tag: "systemdefaultwallet", displayName: "Local default",
return .init(index: -1, tag: "systemdefaultwallet", displayName: NSLocalizedString("Local default", comment: "Dropdown option label for system default for Lightning wallet."),
link: "lightning:", appStoreLink: "lightning:", image: "")
case .strike:
return .init(index: 0, tag: "strike", displayName: "Strike", link: "strike:",
return .init(index: 0, tag: "strike", displayName: NSLocalizedString("Strike", comment: "Dropdown option label for Lightning wallet, Strike."), link: "strike:",
appStoreLink: "https://apps.apple.com/us/app/strike-bitcoin-payments/id1488724463", image: "strike")
case .cashapp:
return .init(index: 1, tag: "cashapp", displayName: "Cash App", link: "squarecash://",
return .init(index: 1, tag: "cashapp", displayName: NSLocalizedString("Cash App", comment: "Dropdown option label for Lightning wallet, Cash App."), link: "squarecash://",
appStoreLink: "https://apps.apple.com/us/app/cash-app/id711923939", image: "cashapp")
case .muun:
return .init(index: 2, tag: "muun", displayName: "Muun", link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun")
return .init(index: 2, tag: "muun", displayName: NSLocalizedString("Muun", comment: "Dropdown option label for Lightning wallet, Muun."), link: "muun:", appStoreLink: "https://apps.apple.com/us/app/muun-wallet/id1482037683", image: "muun")
case .bluewallet:
return .init(index: 3, tag: "bluewallet", displayName: "Blue Wallet", link: "bluewallet:lightning:",
return .init(index: 3, tag: "bluewallet", displayName: NSLocalizedString("Blue Wallet", comment: "Dropdown option label for Lightning wallet, Blue Wallet."), link: "bluewallet:lightning:",
appStoreLink: "https://apps.apple.com/us/app/bluewallet-bitcoin-wallet/id1376878040", image: "bluewallet")
case .walletofsatoshi:
return .init(index: 4, tag: "walletofsatoshi", displayName: "Wallet Of Satoshi", link: "walletofsatoshi:lightning:",
return .init(index: 4, tag: "walletofsatoshi", displayName: NSLocalizedString("Wallet Of Satoshi", comment: "Dropdown option label for Lightning wallet, Wallet Of Satoshi."), link: "walletofsatoshi:lightning:",
appStoreLink: "https://apps.apple.com/us/app/wallet-of-satoshi/id1438599608", image: "walletofsatoshi")
case .zebedee:
return .init(index: 5, tag: "zebedee", displayName: "Zebedee", link: "zebedee:lightning:",
return .init(index: 5, tag: "zebedee", displayName: NSLocalizedString("Zebedee", comment: "Dropdown option label for Lightning wallet, Zebedee."), link: "zebedee:lightning:",
appStoreLink: "https://apps.apple.com/us/app/zebedee-wallet/id1484394401", image: "zebedee")
case .zeusln:
return .init(index: 6, tag: "zeusln", displayName: "Zeus LN", link: "zeusln:lightning:",
return .init(index: 6, tag: "zeusln", displayName: NSLocalizedString("Zeus LN", comment: "Dropdown option label for Lightning wallet, Zeus LN."), link: "zeusln:lightning:",
appStoreLink: "https://apps.apple.com/us/app/zeus-ln/id1456038895", image: "zeusln")
case .lnlink:
return .init(index: 7, tag: "lnlink", displayName: "LNLink", link: "lnlink:lightning:",
return .init(index: 7, tag: "lnlink", displayName: NSLocalizedString("LNLink", comment: "Dropdown option label for Lightning wallet, LNLink."), link: "lnlink:lightning:",
appStoreLink: "https://testflight.apple.com/join/aNY4yuuZ", image: "lnlink")
case .phoenix:
return .init(index: 8, tag: "phoenix", displayName: "Phoenix", link: "phoenix://",
return .init(index: 8, tag: "phoenix", displayName: NSLocalizedString("Phoenix", comment: "Dropdown option label for Lightning wallet, Phoenix."), link: "phoenix://",
appStoreLink: "https://apps.apple.com/us/app/phoenix-wallet/id1544097028", image: "phoenix")
case .breez:
return .init(index: 9, tag: "breez", displayName: "Breez", link: "breez:",
return .init(index: 9, tag: "breez", displayName: NSLocalizedString("Breez", comment: "Dropdown option label for Lightning wallet, Breez."), link: "breez:",
appStoreLink: "https://apps.apple.com/us/app/breez-lightning-client-pos/id1463604142", image: "breez")
case .bitcoinbeach:
return .init(index: 10, tag: "bitcoinbeach", displayName: "Bitcoin Beach", link: "bitcoinbeach://",
return .init(index: 10, tag: "bitcoinbeach", displayName: NSLocalizedString("Bitcoin Beach", comment: "Dropdown option label for Lightning wallet, Bitcoin Beach."), link: "bitcoinbeach://",
appStoreLink: "https://apps.apple.com/sv/app/bitcoin-beach-wallet/id1531383905", image: "bbw")
case .blixtwallet:
return .init(index: 11, tag: "blixtwallet", displayName: NSLocalizedString("Blixt Wallet", comment: "Dropdown option label for Lightning wallet, Blixt Wallet"), link: "blixtwallet:lightning:",
appStoreLink: nil, image: "blixt-wallet")
case .river:
return .init(index: 12, tag: "river", displayName: NSLocalizedString("River", comment: "Dropdown option label for Lightning wallet, River"), link: "river://",
appStoreLink: "https://apps.apple.com/us/app/river-buy-mine-bitcoin/id1536176542", image: "river")
}
}

View File

@@ -448,7 +448,7 @@ func hex_encode(_ data: Data) -> String {
func random_bytes(count: Int) -> Data {
var data = Data(count: count)
_ = data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes)
SecRandomCopyBytes(kSecRandomDefault, count, mutableBytes.baseAddress!)
}
return data
}

View File

@@ -7,9 +7,8 @@
import Foundation
public func time_ago_since(_ date: Date) -> String {
public func time_ago_since(_ date: Date, _ calendar: Calendar = Calendar.current) -> String {
let calendar = Calendar.current
let now = Date()
let unitFlags: NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfMonth, .month, .year]

View File

@@ -40,26 +40,26 @@ struct CreateAccountView: View {
}
VStack {
SignupForm {
FormLabel("Username")
FormLabel(NSLocalizedString("Username", comment: "Label to prompt username entry."))
HStack(spacing: 0.0) {
Text("@")
.foregroundColor(.white)
.padding(.leading, -25.0)
FormTextInput("satoshi", text: $account.nick_name)
FormTextInput(NSLocalizedString("satoshi", comment: "Example username of Bitcoin creator(s), Satoshi Nakamoto."), text: $account.nick_name)
.autocorrectionDisabled(true)
.textInputAutocapitalization(.never)
}
FormLabel("Display Name", optional: true)
FormTextInput("Satoshi Nakamoto", text: $account.real_name)
FormLabel(NSLocalizedString("Display Name", comment: "Label to prompt display name entry."), optional: true)
FormTextInput(NSLocalizedString("Satoshi Nakamoto", comment: "Name of Bitcoin creator(s)."), text: $account.real_name)
.textInputAutocapitalization(.words)
FormLabel("About", optional: true)
FormTextInput("Creator(s) of Bitcoin. Absolute legend.", text: $account.about)
FormLabel(NSLocalizedString("About", comment: "Label to prompt for about text entry for user to describe about themself."), optional: true)
FormTextInput(NSLocalizedString("Creator(s) of Bitcoin. Absolute legend.", comment: "Example description about Bitcoin creator(s), Satoshi Nakamoto."), text: $account.about)
FormLabel("Account ID")
FormLabel(NSLocalizedString("Account ID", comment: "Label to indicate the public ID of the account."))
.onTapGesture {
regen_key()
}
@@ -75,7 +75,7 @@ struct CreateAccountView: View {
NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) {
EmptyView()
}
DamusWhiteButton("Create") {
DamusWhiteButton(NSLocalizedString("Create", comment: "Button to create account.")) {
self.is_done = true
}
.padding()

View File

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

View File

@@ -401,7 +401,7 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String {
let n = desc.others
if desc.pubkeys.count == 0 {
return "Reply to self"
return NSLocalizedString("Reply to self", comment: "Label to indicate that the user is replying to themself.")
}
let names: [String] = pubkeys.map {
@@ -411,20 +411,14 @@ func reply_desc(profiles: Profiles, event: NostrEvent) -> String {
if names.count == 2 {
if n > 2 {
let and_other = reply_others_desc(n: n, n_pubkeys: pubkeys.count)
return "Replying to \(names[0]), \(names[1])\(and_other)"
let othersCount = n - pubkeys.count
return String(format: NSLocalizedString("replying_to_two_and_others", comment: "Label to indicate that the user is replying to 2 users and others."), othersCount, names[0], names[1])
}
return "Replying to \(names[0]) & \(names[1])"
return String.localizedStringWithFormat("Replying to %@ & %@", names[0], names[1])
}
let and_other = reply_others_desc(n: n, n_pubkeys: pubkeys.count)
return "Replying to \(names[0])\(and_other)"
}
func reply_others_desc(n: Int, n_pubkeys: Int) -> String {
let other = n - n_pubkeys
let plural = other == 1 ? "" : "s"
return n > 1 ? " & \(other) other\(plural)" : ""
let othersCount = n - pubkeys.count
return String(format: NSLocalizedString("replying_to_one_and_others", comment: "Label to indicate that the user is replying to 1 user and others."), othersCount, names[0])
}

View File

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

View File

@@ -22,13 +22,13 @@ enum FollowState {
func follow_btn_txt(_ fs: FollowState) -> String {
switch fs {
case .follows:
return "Unfollow"
return NSLocalizedString("Unfollow", comment: "Button to unfollow a user.")
case .following:
return "Following..."
return NSLocalizedString("Following...", comment: "Label to indicate that the user is in the process of following another user.")
case .unfollowing:
return "Unfollowing..."
return NSLocalizedString("Unfollowing...", comment: "Label to indicate that the user is in the process of unfollowing another user.")
case .unfollows:
return "Follow"
return NSLocalizedString("Follow", comment: "Button to follow a user.")
}
}

View File

@@ -67,7 +67,7 @@ struct SaveKeysView: View {
complete_account_creation(account)
}
} else {
DamusWhiteButton("Let's go!") {
DamusWhiteButton(NSLocalizedString("Let's go!", comment: "Button to complete account creation and start using the app.")) {
complete_account_creation(account)
}
}

View File

@@ -38,14 +38,8 @@ struct SelectWalletView: View {
Section("Select a lightning wallet"){
List{
Button() {
let walletModel = user_settings.default_wallet.model
if let url = URL(string: "\(walletModel.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
openURL(url)
} else {
if let url = URL(string: walletModel.appStoreLink), UIApplication.shared.canOpenURL(url) {
openURL(url)
}
}
let wallet_model = user_settings.default_wallet.model
open_with_wallet(wallet: wallet_model, invoice: invoice)
} label: {
HStack {
Text("Default Wallet").font(.body).foregroundColor(.blue)
@@ -54,13 +48,7 @@ struct SelectWalletView: View {
List($allWalletModels) { $wallet in
if wallet.index >= 0 {
Button() {
if let url = URL(string: "\(wallet.link)\(invoice)"), UIApplication.shared.canOpenURL(url) {
openURL(url)
} else {
if let url = URL(string: wallet.appStoreLink), UIApplication.shared.canOpenURL(url) {
openURL(url)
}
}
open_with_wallet(wallet: wallet, invoice: invoice)
} label: {
HStack {
Image(wallet.image).resizable().frame(width: 32.0, height: 32.0,alignment: .center).cornerRadius(5)

View File

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

View File

@@ -0,0 +1,42 @@
<?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 %2$@%#@others@</string>
<key>others</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string></string>
<key>one</key>
<string> &amp; 1 other</string>
<key>other</key>
<string> &amp; %d others</string>
</dict>
</dict>
<key>replying_to_two_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>Replying to %2$@, %3$@%#@others@</string>
<key>others</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string></string>
<key>one</key>
<string> &amp; 1 other</string>
<key>other</key>
<string> &amp; %d others</string>
</dict>
</dict>
</dict>
</plist>

View File

@@ -11,24 +11,27 @@ import XCTest
final class TimeAgoTests: XCTestCase {
func testTimeAgoSince() {
XCTAssertEqual(time_ago_since(Date.now), "now")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-2)), "now")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3)), "3s")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-59)), "59s")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-60)), "1min")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3599)), "59min")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3600)), "1h")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-86399)), "23h")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-86400)), "1d")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -1, to: Date.now)!.addingTimeInterval(1)), "6d")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -1, to: Date.now)!), "1w")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -2, to: Date.now)!), "2w")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .weekOfMonth, value: -3, to: Date.now)!), "3w")
let locale = Locale(identifier: "en_US")
let calendar = locale.calendar
XCTAssertEqual(time_ago_since(Date.now, calendar), "now")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-2), calendar), "now")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-3), calendar), "3s")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-59), calendar), "59s")
XCTAssertEqual(time_ago_since(Date.now.addingTimeInterval(-60), calendar), "1m")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .hour, value: -1, to: Date.now)!.addingTimeInterval(1), calendar), "59m")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .hour, value: -1, to: Date.now)!, calendar), "1h")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .day, value: -1, to: Date.now)!.addingTimeInterval(1), calendar), "23h")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .day, value: -1, to: Date.now)!, calendar), "1d")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .weekOfMonth, value: -1, to: Date.now)!.addingTimeInterval(1), calendar), "6d")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .weekOfMonth, value: -1, to: Date.now)!, calendar), "1w")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .weekOfMonth, value: -2, to: Date.now)!, calendar), "2w")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .weekOfMonth, value: -3, to: Date.now)!, calendar), "3w")
// Not testing the 4-5 week boundary since how it is formatted depends on which month and year it is currently when this test executes.
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .month, value: -1, to: Date.now)!), "1mo")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .year, value: -1, to: Date.now)!.addingTimeInterval(1)), "11mo")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .year, value: -1, to: Date.now)!), "1y")
XCTAssertEqual(time_ago_since(Calendar.current.date(byAdding: .year, value: -1000, to: Date.now)!), "1,000y")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .month, value: -1, to: Date.now)!, calendar), "1mo")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .year, value: -1, to: Date.now)!.addingTimeInterval(1), calendar), "11mo")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .year, value: -1, to: Date.now)!, calendar), "1y")
XCTAssertEqual(time_ago_since(calendar.date(byAdding: .year, value: -1000, to: Date.now)!, calendar), "1,000y")
}
}

View File

@@ -33,6 +33,13 @@ class damusTests: XCTestCase {
}
}
func testRandomBytes() {
let bytes = random_bytes(count: 32)
print("testRandomBytes \(hex_encode(bytes))")
XCTAssertEqual(bytes.count, 32)
}
func testParseMentionWithMarkdown() {
let md = """
Testing markdown in damus