Compare commits

..

26 Commits

Author SHA1 Message Date
685eb3e277 Fix missing profile zap notification text
Changelog-Fixed: Fix missing profile zap notification text
2023-06-27 07:06:08 -04:00
William Casarin
82c53e43e5 v1.5 (8) 2023-06-27 06:30:50 +02:00
William Casarin
3e274a820a nozaps: restore zap button with zap info, just make it not clickable 2023-06-27 06:04:36 +02:00
William Casarin
1a0282fe21 Revert "nozaps: don't pull thread zaps in nozaps mode"
This reverts commit 6003a3c6f8.
2023-06-27 05:59:59 +02:00
William Casarin
b2b687fb79 Revert "nozaps: hide zap total"
This reverts commit 57789de5cd.
2023-06-27 05:59:33 +02:00
William Casarin
94448a10bd Revert "nozaps: hide zap details on notes for now"
This reverts commit b0d6d33573.
2023-06-27 05:58:42 +02:00
William Casarin
66db4c5215 Revert "nozaps: don't show note zaps in notifications"
This reverts commit c5b0e539d8.
2023-06-27 05:58:39 +02:00
William Casarin
1e2326cccf v1.5 (7)
rip zap button
2023-06-27 05:35:53 +02:00
William Casarin
959f208e36 profile: make profile loading more lightweight for now 2023-06-27 05:31:22 +02:00
William Casarin
7d80985b06 nozaps: remove zap button on posts 2023-06-27 05:31:14 +02:00
William Casarin
40a51edafe readme: patchstr bounties 2023-06-26 12:03:05 +02:00
cfe14fac23 Deduplicate users in group notifications
Changelog-Fixed: Deduplicate users in notifications
Closes: #1326
2023-06-26 11:31:36 +02:00
2046fe5502 Fix notification content rendering of repost and reaction events
Closes: #1318
Changelog-Fixed: Fix notification content rendering of repost and reaction events
2023-06-26 11:20:11 +02:00
Bryan Montz
2d4ddc7b9c Fix crash related to VideoPlayer and CMTime
Closes: #1321
Changelog-Fixed: Fix crash related to VideoPlayer
2023-06-26 11:20:11 +02:00
William Casarin
6003a3c6f8 nozaps: don't pull thread zaps in nozaps mode 2023-06-26 11:20:11 +02:00
transifex-integration[bot]
572cae7dc5 Translate Localizable.strings in nl
100% translated source file: 'Localizable.strings'
on 'nl'.
2023-06-26 08:59:40 +00:00
transifex-integration[bot]
8d7d3d0d37 Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-26 08:58:56 +00:00
transifex-integration[bot]
c76fc5bcce Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-26 08:58:25 +00:00
transifex-integration[bot]
3a357c8d82 Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2023-06-26 08:58:10 +00:00
transifex-integration[bot]
ac59ee6285 Translate Localizable.strings in el_GR
100% translated source file: 'Localizable.strings'
on 'el_GR'.
2023-06-26 08:52:08 +00:00
transifex-integration[bot]
a870b86490 Translate Localizable.stringsdict in el_GR
100% translated source file: 'Localizable.stringsdict'
on 'el_GR'.
2023-06-26 08:43:40 +00:00
transifex-integration[bot]
cb2da7f3c6 Translate Localizable.stringsdict in sv_SE
100% translated source file: 'Localizable.stringsdict'
on 'sv_SE'.
2023-06-26 06:28:24 +00:00
transifex-integration[bot]
71f3b9b013 Translate Localizable.strings in sv_SE
100% translated source file: 'Localizable.strings'
on 'sv_SE'.
2023-06-26 06:27:13 +00:00
e220b0756f Export strings for translation 2023-06-25 23:46:53 -04:00
transifex-integration[bot]
83abedb4d6 Translate Localizable.stringsdict in sv_SE
100% translated source file: 'Localizable.stringsdict'
on 'sv_SE'.
2023-06-25 23:45:19 -04:00
transifex-integration[bot]
23b057779a Translate Localizable.strings in sv_SE
100% translated source file: 'Localizable.strings'
on 'sv_SE'.
2023-06-25 23:45:19 -04:00
24 changed files with 240 additions and 113 deletions

View File

@@ -96,9 +96,10 @@ Contributors welcome! Start by examining known issues: https://github.com/damus-
### Code
[Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on GitHub as well.
[Email patches][git-send-email] to jb55@jb55.com are preferred, but I accept PRs on GitHub as well. 50,000 sats will be rewarded for patches sent via email or [nostr][patchstr]
[git-send-email]: http://git-send-email.io
[patchstr]: https://patchstr.com
### Privacy
Your internet protocol (IP) address is exposed to the relays you connect to, and third party media hosters (e.g. nostr.build, imgur.com, giphy.com, youtube.com etc.) that render on Damus. If you want to improve your privacy, consider utilizing a service that masks your IP address (e.g. a VPN) from trackers online.

View File

@@ -2236,7 +2236,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;
@@ -2284,7 +2284,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = damus/damus.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 6;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"damus/Preview Content\"";
DEVELOPMENT_TEAM = XK7H4JAB3D;
ENABLE_PREVIEWS = YES;

View File

@@ -38,10 +38,6 @@ struct ZapButton: View {
}
var zap_img: String {
if damus_state.settings.nozaps {
return "zap"
}
switch our_zap {
case .none:
return "zap"
@@ -53,10 +49,6 @@ struct ZapButton: View {
}
var zap_color: Color {
if damus_state.settings.nozaps {
return Color.gray
}
if our_zap == nil {
return Color.gray
}
@@ -114,17 +106,17 @@ struct ZapButton: View {
var body: some View {
HStack(spacing: 4) {
Button(action: {
}, label: {
Image(zap_img)
.resizable()
.foregroundColor(zap_color)
.font(.footnote.weight(.medium))
.aspectRatio(contentMode: .fit)
.frame(width:20, height: 20)
})
if zaps.zap_total > 0 {
Button(action: {
}, label: {
Image(zap_img)
.resizable()
.foregroundColor(zap_color)
.font(.footnote.weight(.medium))
.aspectRatio(contentMode: .fit)
.frame(width:20, height: 20)
})
if !damus_state.settings.nozaps && zaps.zap_total > 0 {
Text(verbatim: format_msats_abbrev(zaps.zap_total))
.font(.footnote)
.foregroundColor(zap_color)
@@ -134,7 +126,7 @@ struct ZapButton: View {
.simultaneousGesture(LongPressGesture().onEnded {_ in
// when we don't have nozaps mode enable, long press shows the zap customizer
if !damus_state.settings.nozaps {
present_sheet(.zap(target: target, lnurl: lnurl))
//present_sheet(.zap(target: target, lnurl: lnurl))
}
// long press does nothing in nozaps mode
@@ -142,7 +134,7 @@ struct ZapButton: View {
.highPriorityGesture(TapGesture().onEnded {
// when we have appstore mode on, only show the zap customizer as "user zaps"
if damus_state.settings.nozaps {
present_sheet(.zap(target: target, lnurl: lnurl))
//present_sheet(.zap(target: target, lnurl: lnurl))
} else {
// otherwise we restore the original behavior of one-tap zaps
tap()

View File

@@ -1076,7 +1076,7 @@ func zap_notification_title(_ zap: Zap) -> String {
func zap_notification_body(profiles: Profiles, zap: Zap, locale: Locale = Locale.current) -> String {
let src = zap.request.ev
let pk = zap.is_anon ? "anon" : src.pubkey
let pk = zap.is_anon ? ANON_PUBKEY : src.pubkey
let profile = profiles.lookup(id: pk)
let sats = NSNumber(value: (Double(zap.invoice.amount) / 1000.0))
let formattedSats = format_msats_abbrev(zap.invoice.amount)
@@ -1164,13 +1164,15 @@ func process_local_notification(damus_state: DamusState, event ev: NostrEvent) {
create_local_notification(profiles: damus_state.profiles, notify: notify )
}
} else if type == .boost && damus_state.settings.repost_notification, let inner_ev = ev.get_inner_event(cache: damus_state.events) {
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: inner_ev.content)
let content = NSAttributedString(render_note_content(ev: inner_ev, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
let notify = LocalNotification(type: .repost, event: ev, target: inner_ev, content: content)
create_local_notification(profiles: damus_state.profiles, notify: notify)
} else if type == .like && damus_state.settings.like_notification,
let evid = ev.referenced_ids.last?.ref_id,
let liked_event = damus_state.events.lookup(evid)
{
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: liked_event.content)
let content = NSAttributedString(render_note_content(ev: liked_event, profiles: damus_state.profiles, privkey: damus_state.keypair.privkey).content.attributed).string
let notify = LocalNotification(type: .like, event: ev, target: liked_event, content: content)
create_local_notification(profiles: damus_state.profiles, notify: notify)
}

View File

@@ -76,7 +76,7 @@ class ProfileModel: ObservableObject, Equatable {
profile_filter.authors = [pubkey]
text_filter.authors = [pubkey]
text_filter.limit = 500
text_filter.limit = 50
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
print_filters(relay_id: "profile", filters: [[text_filter], [profile_filter]])

View File

@@ -38,7 +38,7 @@ enum DisplayName {
func parse_display_name(profile: Profile?, pubkey: String) -> DisplayName {
if pubkey == "anon" {
if pubkey == ANON_PUBKEY {
return .one(NSLocalizedString("Anonymous", comment: "Placeholder display name of anonymous user."))
}

View File

@@ -9,6 +9,7 @@ import Foundation
import secp256k1
let PUBKEY_HRP = "npub"
let ANON_PUBKEY = "anon"
struct FullKeypair: Equatable {
let pubkey: String

View File

@@ -19,11 +19,7 @@ struct EventDetailBar: View {
self.target = target
self.target_pk = target_pk
self._bar = ObservedObject(wrappedValue: make_actionbar_model(ev: target, damus: state))
}
var ZapDetails: Text {
let noun = Text(verbatim: zapsCountString(bar.zaps)).foregroundColor(.gray)
return Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.")
}
var body: some View {
@@ -44,11 +40,13 @@ struct EventDetailBar: View {
.buttonStyle(PlainButtonStyle())
}
if !state.settings.nozaps && bar.zaps > 0 {
if bar.zaps > 0 {
let dst = ZapsView(state: state, target: .note(id: target, author: target_pk))
NavigationLink(destination: dst) {
ZapDetails
}.buttonStyle(PlainButtonStyle())
let noun = Text(verbatim: zapsCountString(bar.zaps)).foregroundColor(.gray)
Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.")
}
.buttonStyle(PlainButtonStyle())
}
}
}

View File

@@ -132,7 +132,7 @@ struct TextEvent: View {
func ProfileName(is_anon: Bool) -> some View {
let profile = damus.profiles.lookup(id: pubkey)
let pk = is_anon ? "anon" : pubkey
let pk = is_anon ? ANON_PUBKEY : pubkey
return EventProfileName(pubkey: pk, profile: profile, damus: damus, size: .normal)
}

View File

@@ -56,16 +56,11 @@ enum ReactingTo {
case your_profile
}
func determine_reacting_to(our_pubkey: String, ev: NostrEvent?, group: EventGroupType, nozaps: Bool) -> ReactingTo {
func determine_reacting_to(our_pubkey: String, ev: NostrEvent?) -> ReactingTo {
guard let ev else {
return .your_profile
}
if nozaps && group.is_note_zap {
// ZAPPING NOTES IS NOT ALLOWED!!!! EVIL!!!
return .your_profile
}
if ev.pubkey == our_pubkey {
return .your_note
}
@@ -78,19 +73,42 @@ func event_author_name(profiles: Profiles, pubkey: String) -> String {
return Profile.displayName(profile: alice_prof, pubkey: pubkey).username.truncate(maxLength: 50)
}
func event_group_author_name(profiles: Profiles, ind: Int, group: EventGroupType) -> String {
func event_group_unique_pubkeys(profiles: Profiles, group: EventGroupType) -> [String] {
var seen = Set<String>()
var sorted = [String]()
if let zapgrp = group.zap_group {
let zap = zapgrp.zaps[ind]
if zap.is_anon {
return NSLocalizedString("Anonymous", comment: "Placeholder author name of the anonymous person who zapped an event.")
let zaps = zapgrp.zaps
for i in 0..<zaps.count {
let zap = zapgrp.zaps[i]
let pubkey: String
if zap.is_anon {
pubkey = ANON_PUBKEY
} else {
pubkey = zap.request.ev.pubkey
}
if !seen.contains(pubkey) {
seen.insert(pubkey)
sorted.append(pubkey)
}
}
return event_author_name(profiles: profiles, pubkey: zap.request.ev.pubkey)
} else {
let ev = group.events[ind]
return event_author_name(profiles: profiles, pubkey: ev.pubkey)
let events = group.events
for i in 0..<events.count {
let ev = events[i]
let pubkey = ev.pubkey
if !seen.contains(pubkey) {
seen.insert(pubkey)
sorted.append(pubkey)
}
}
}
return sorted
}
/**
@@ -130,29 +148,29 @@ func event_group_author_name(profiles: Profiles, ind: Int, group: EventGroupType
"zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile
"zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile
*/
func reacting_to_text(profiles: Profiles, our_pubkey: String, group: EventGroupType, ev: NostrEvent?, nozaps: Bool, locale: Locale? = nil) -> String {
func reacting_to_text(profiles: Profiles, our_pubkey: String, group: EventGroupType, ev: NostrEvent?, pubkeys: [String], locale: Locale? = nil) -> String {
if group.events.count == 0 {
return "??"
}
let verb = reacting_to_verb(group: group)
let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev, group: group, nozaps: nozaps)
let localization_key = "\(verb)_\(reacting_to)_\(min(group.events.count, 3))"
let reacting_to = determine_reacting_to(our_pubkey: our_pubkey, ev: ev)
let localization_key = "\(verb)_\(reacting_to)_\(min(pubkeys.count, 3))"
let format = localizedStringFormat(key: localization_key, locale: locale)
switch group.events.count {
switch pubkeys.count {
case 1:
let display_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
let display_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
return String(format: format, locale: locale, display_name)
case 2:
let alice_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
let bob_name = event_group_author_name(profiles: profiles, ind: 1, group: group)
let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
let bob_name = event_author_name(profiles: profiles, pubkey: pubkeys[1])
return String(format: format, locale: locale, alice_name, bob_name)
default:
let alice_name = event_group_author_name(profiles: profiles, ind: 0, group: group)
let count = group.events.count - 1
let alice_name = event_author_name(profiles: profiles, pubkey: pubkeys[0])
let count = pubkeys.count - 1
return String(format: format, locale: locale, count, alice_name)
}
@@ -175,8 +193,8 @@ struct EventGroupView: View {
let event: NostrEvent?
let group: EventGroupType
var GroupDescription: some View {
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, nozaps: state.settings.nozaps))")
func GroupDescription(_ pubkeys: [String]) -> some View {
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys))")
}
func ZapIcon(_ zapgrp: ZapGroup) -> some View {
@@ -216,25 +234,25 @@ struct EventGroupView: View {
.frame(width: PFP_SIZE + 10)
VStack(alignment: .leading) {
ProfilePicturesView(state: state, pubkeys: group.events.map { $0.pubkey })
let unique_pubkeys = event_group_unique_pubkeys(profiles: state.profiles, group: group)
ProfilePicturesView(state: state, pubkeys: unique_pubkeys)
if let event {
let thread = ThreadModel(event: event, damus_state: state)
let dest = ThreadView(state: state, thread: thread)
GroupDescription
if !state.settings.nozaps || !group.is_note_zap {
NavigationLink(destination: dest) {
VStack(alignment: .leading) {
EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content])
.padding([.top], 1)
.padding([.trailing])
.foregroundColor(.gray)
}
NavigationLink(destination: dest) {
VStack(alignment: .leading) {
GroupDescription(unique_pubkeys)
EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content])
.padding([.top], 1)
.padding([.trailing])
.foregroundColor(.gray)
}
.buttonStyle(.plain)
}
.buttonStyle(.plain)
} else {
GroupDescription
GroupDescription(unique_pubkeys)
}
}
}

View File

@@ -111,14 +111,6 @@ struct PostView: View {
var is_post_empty: Bool {
return post.string.allSatisfy { $0.isWhitespace } && uploadedMedias.isEmpty
}
var uploading_disabled: Bool {
return image_upload.progress != nil
}
var posting_disabled: Bool {
return is_post_empty || uploading_disabled
}
var ImageButton: some View {
Button(action: {
@@ -143,7 +135,7 @@ struct PostView: View {
ImageButton
CameraButton
}
.disabled(uploading_disabled)
.disabled(image_upload.progress != nil)
}
var PostButton: some View {
@@ -154,12 +146,12 @@ struct PostView: View {
self.send_post()
}
}
.disabled(posting_disabled)
.disabled(is_post_empty)
.font(.system(size: 14, weight: .bold))
.frame(width: 80, height: 30)
.foregroundColor(.white)
.background(LINEAR_GRADIENT)
.opacity(posting_disabled ? 0.5 : 1.0)
.opacity(is_post_empty ? 0.5 : 1.0)
.clipShape(Capsule())
}

View File

@@ -38,6 +38,6 @@ struct MaybeAnonPfpView: View {
struct MaybeAnonPfpView_Previews: PreviewProvider {
static var previews: some View {
MaybeAnonPfpView(state: test_damus_state(), is_anon: true, pubkey: "anon", size: PFP_SIZE)
MaybeAnonPfpView(state: test_damus_state(), is_anon: true, pubkey: ANON_PUBKEY, size: PFP_SIZE)
}
}

View File

@@ -48,7 +48,8 @@ public class VideoPlayerModel: ObservableObject {
@Published var has_audio: Bool? = nil
@Published var contentMode: UIView.ContentMode = .scaleAspectFill
var time: CMTime = CMTime()
fileprivate var time: CMTime?
var handlers: [VideoHandler] = []
init() {
@@ -262,8 +263,9 @@ extension VideoPlayer: UIViewRepresentable {
uiView.isMuted = model.muted
uiView.isAutoReplay = model.autoReplay
if let observerTime = context.coordinator.observerTime, model.time != observerTime {
uiView.seek(to: model.time, toleranceBefore: model.time, toleranceAfter: model.time, completion: { _ in })
if let observerTime = context.coordinator.observerTime, let modelTime = model.time,
modelTime != observerTime && modelTime.isValid && modelTime.isNumeric {
uiView.seek(to: modelTime, completion: { _ in })
}
}

View File

@@ -23,6 +23,6 @@ struct ZapUserView: View {
struct ZapUserView_Previews: PreviewProvider {
static var previews: some View {
ZapUserView(state: test_damus_state(), pubkey: "anon")
ZapUserView(state: test_damus_state(), pubkey: ANON_PUBKEY)
}
}

View File

@@ -18,6 +18,22 @@
<string>... %d άλλες σημειώσεις ...</string>
</dict>
</dict>
<key>followed_by_three_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Σας ακολούθησε %2$@, %3$@, %4$@ &amp; %1$d ακόμα</string>
<key>other</key>
<string>Σας ακολούθησαν %2$@, %3$@, %4$@ &amp; %1$d ακόμα</string>
</dict>
</dict>
<key>followers_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
@@ -317,9 +333,9 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%2$@ και %1$d ακόμα έστειλε zap στο προφίλ σας</string>
<string>%2$@ και %1$d ακόμα σας έστειλε zap</string>
<key>other</key>
<string>%2$@ και %1$d ακόμα έστειλαν zap στο προφίλ σας</string>
<string>%2$@ και %1$d ακόμα σας έστειλαν zap</string>
</dict>
</dict>
<key>zaps_count</key>

View File

@@ -45,7 +45,7 @@
<trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source>
<target>%@ %@</target>
<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'.
<note>Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.
Sentence composed of 2 variables to describe how many people are following a user. In source English, the first variable is the number of followers, and the second variable is 'Follower' or 'Followers'.</note>
</trans-unit>
<trans-unit id="%@ has been muted" xml:space="preserve">
@@ -276,11 +276,6 @@ Sentence composed of 2 variables to describe how many people are following a use
<note>Sidebar menu label for Bookmarks view.
Title of bookmarks view</note>
</trans-unit>
<trans-unit id="Boosts" xml:space="preserve">
<source>Boosts</source>
<target>Boosts</target>
<note>Accessibility label for boosts button</note>
</trans-unit>
<trans-unit id="Broadcast" xml:space="preserve">
<source>Broadcast</source>
<target>Broadcast</target>
@@ -576,11 +571,31 @@ Sentence composed of 2 variables to describe how many people are following a use
<target>Follow me on Nostr</target>
<note>Text on QR code view to prompt viewer looking at screen to follow the user.</note>
</trans-unit>
<trans-unit id="Followed by %@" xml:space="preserve">
<source>Followed by %@</source>
<target>Followed by %@</target>
<note>Text to indicate that the user is followed by one of our follows.</note>
</trans-unit>
<trans-unit id="Followed by %@ &amp; %@" xml:space="preserve">
<source>Followed by %1$@ &amp; %2$@</source>
<target>Followed by %1$@ &amp; %2$@</target>
<note>Text to indicate that the user is followed by two of our follows.</note>
</trans-unit>
<trans-unit id="Followed by %@, %@ &amp; %@" xml:space="preserve">
<source>Followed by %1$@, %2$@ &amp; %3$@</source>
<target>Followed by %1$@, %2$@ &amp; %3$@</target>
<note>Text to indicate that the user is followed by three of our follows.</note>
</trans-unit>
<trans-unit id="Followers" xml:space="preserve">
<source>Followers</source>
<target>Followers</target>
<note>Navigation bar title for view that shows who is following a user.</note>
</trans-unit>
<trans-unit id="Followers You Know" xml:space="preserve">
<source>Followers You Know</source>
<target>Followers You Know</target>
<note>Navigation bar title for view that shows who is following a user.</note>
</trans-unit>
<trans-unit id="Following" xml:space="preserve">
<source>Following</source>
<target>Following</target>
@@ -1151,7 +1166,8 @@ Button text to indicate that the zap type is a private zap.</note>
<trans-unit id="Reposts" xml:space="preserve">
<source>Reposts</source>
<target>Reposts</target>
<note>Navigation bar title for Reposts view.
<note>Accessibility label for boosts button
Navigation bar title for Reposts view.
Setting to enable Repost Local Notification</note>
</trans-unit>
<trans-unit id="Requests" xml:space="preserve">
@@ -1230,9 +1246,9 @@ Button text to indicate that the zap type is a private zap.</note>
<target>Send a message to start the conversation...</target>
<note>Text prompt for user to send a message to the other user.</note>
</trans-unit>
<trans-unit id="Send a reply with your zap..." xml:space="preserve">
<source>Send a reply with your zap...</source>
<target>Send a reply with your zap...</target>
<trans-unit id="Send a message with your zap..." xml:space="preserve">
<source>Send a message with your zap...</source>
<target>Send a message with your zap...</target>
<note>Placeholder text for a comment to send as part of a zap to the user.</note>
</trans-unit>
<trans-unit id="Server" xml:space="preserve">
@@ -1620,9 +1636,13 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<source>Zap</source>
<target>Zap</target>
<note>Accessibility label for zap button
Button to send a zap.
Title of notification when a non-private zap is received.</note>
</trans-unit>
<trans-unit id="Zap User" xml:space="preserve">
<source>Zap User</source>
<target>Zap User</target>
<note>Button to send a zap.</note>
</trans-unit>
<trans-unit id="Zap Vibration" xml:space="preserve">
<source>Zap Vibration</source>
<target>Zap Vibration</target>
@@ -1834,6 +1854,21 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<target>%#@NOTES@</target>
<note/>
</trans-unit>
<trans-unit id="/followed_by_three_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@OTHERS@</source>
<target>%#@OTHERS@</target>
<note/>
</trans-unit>
<trans-unit id="/followed_by_three_and_others:dict/OTHERS:dict/one:dict/:string" xml:space="preserve">
<source>Followed by %2$@, %3$@, %4$@ &amp; %1$d other</source>
<target>Followed by %2$@, %3$@, %4$@ &amp; %1$d other</target>
<note/>
</trans-unit>
<trans-unit id="/followed_by_three_and_others:dict/OTHERS:dict/other:dict/:string" xml:space="preserve">
<source>Followed by %2$@, %3$@, %4$@ &amp; %1$d others</source>
<target>Followed by %2$@, %3$@, %4$@ &amp; %1$d others</target>
<note/>
</trans-unit>
<trans-unit id="/followers_count:dict/FOLLOWERS:dict/one:dict/:string" xml:space="preserve">
<source>Follower</source>
<target>Follower</target>

View File

@@ -18,6 +18,22 @@
<string>... %d other notes ...</string>
</dict>
</dict>
<key>followed_by_three_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Followed by %2$@, %3$@, %4$@ &amp; %1$d other</string>
<key>other</key>
<string>Followed by %2$@, %3$@, %4$@ &amp; %1$d others</string>
</dict>
</dict>
<key>followers_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>

Binary file not shown.

View File

@@ -18,6 +18,22 @@
<string>... %d andere notities ...</string>
</dict>
</dict>
<key>followed_by_three_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Gevolgd door %2$@, %3$@, %4$@ en %1$d andere gebruiker</string>
<key>other</key>
<string>Gevolgd door %2$@, %3$@, %4$@ en %1$d andere gebruikers</string>
</dict>
</dict>
<key>followers_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>

View File

@@ -18,6 +18,22 @@
<string>...%d andra anteckningar...</string>
</dict>
</dict>
<key>followed_by_three_and_others</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@OTHERS@</string>
<key>OTHERS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Följd av %2$@, %3$@, %4$@ &amp; %1$d andra</string>
<key>other</key>
<string>Följd av %2$@, %3$@, %4$@ &amp; %1$d andra</string>
</dict>
</dict>
<key>followers_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
@@ -317,9 +333,9 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>%2$@ och %1$d annan zappade din profil</string>
<string>%2$@ och %1$d andra zapped dig</string>
<key>other</key>
<string>%2$@ och %1$d andra zappade din profil</string>
<string>%2$@ och %1$d andra zapped dig</string>
</dict>
</dict>
<key>zaps_count</key>

View File

@@ -18,6 +18,27 @@ final class EventGroupViewTests: XCTestCase {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testEventAuthorName() {
let damusState = test_damus_state()
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "pk1"), "pk1:pk1")
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "pk2"), "pk2:pk2")
XCTAssertEqual(event_author_name(profiles: damusState.profiles, pubkey: "anon"), "Anonymous")
}
func testEventGroupUniquePubkeys() {
let damusState = test_damus_state()
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
let repost3 = NostrEvent(id: "", content: encodedPost, pubkey: "pk3", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: []))), [])
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1]))), ["pk1"])
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1, repost2]))), ["pk1", "pk2"])
XCTAssertEqual(event_group_unique_pubkeys(profiles: damusState.profiles, group: .repost(EventGroup(events: [repost1, repost2, repost3]))), ["pk1", "pk2", "pk3"])
}
func testReactingToText() throws {
let enUsLocale = Locale(identifier: "en-US")
let damusState = test_damus_state()
@@ -25,18 +46,19 @@ final class EventGroupViewTests: XCTestCase {
let encodedPost = "{\"id\": \"8ba545ab96959fe0ce7db31bc10f3ac3aa5353bc4428dbf1e56a7be7062516db\",\"pubkey\": \"7e27509ccf1e297e1df164912a43406218f8bd80129424c3ef798ca3ef5c8444\",\"created_at\": 1677013417,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"93684f15eddf11f42afbdd81828ee9fc35350344d8650c78909099d776e9ad8d959cd5c4bff7045be3b0b255144add43d0feef97940794a1bc9c309791bebe4a\"}"
let repost1 = NostrEvent(id: "", content: encodedPost, pubkey: "pk1", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
let repost2 = NostrEvent(id: "", content: encodedPost, pubkey: "pk2", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
let repost3 = NostrEvent(id: "", content: encodedPost, pubkey: "pk3", kind: NostrKind.boost.rawValue, tags: [], createdAt: 1)
let nozaps = true
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "??")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 reposted a note you were tagged in")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 and pk2:pk2 reposted a note you were tagged in")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, locale: enUsLocale), "pk1:pk1 and 2 others reposted a note you were tagged in")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, pubkeys: [], locale: enUsLocale), "??")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, pubkeys: ["pk1"], locale: enUsLocale), "pk1:pk1 reposted a note you were tagged in")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, pubkeys: ["pk1", "pk2"], locale: enUsLocale), "pk1:pk1 and pk2:pk2 reposted a note you were tagged in")
XCTAssertEqual(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, pubkeys: ["pk1", "pk2", "pk3"], locale: enUsLocale), "pk1:pk1 and 2 others reposted a note you were tagged in")
Bundle.main.localizations.map { Locale(identifier: $0) }.forEach {
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, nozaps: nozaps, locale: $0), "??")
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, nozaps: nozaps, locale: $0))
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, nozaps: nozaps, locale: $0))
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost2])), ev: test_event, nozaps: nozaps, locale: $0))
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [])), ev: test_event, pubkeys: [], locale: $0), "??")
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1])), ev: test_event, pubkeys: ["pk1"], locale: $0))
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2])), ev: test_event, pubkeys: ["pk1", "pk2"], locale: $0))
XCTAssertNoThrow(reacting_to_text(profiles: damusState.profiles, our_pubkey: damusState.pubkey, group: .repost(EventGroup(events: [repost1, repost2, repost3])), ev: test_event, pubkeys: ["pk1", "pk2", "pk3"], locale: $0))
}
}