1
0
mirror of git://jb55.com/damus synced 2024-09-28 16:00:43 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
William Casarin
95cf45073d Merge translations 2024-04-25 12:41:12 -07:00
transifex-integration[bot]
efd3c95c10
Translate Localizable.stringsdict in es_ES
100% translated source file: 'Localizable.stringsdict'
on 'es_ES'.
2024-04-25 19:08:23 +00:00
transifex-integration[bot]
d198206cc2
Translate Localizable.strings in de
100% translated source file: 'Localizable.strings'
on 'de'.
2024-04-25 18:13:02 +00:00
transifex-integration[bot]
0b0bcedb1e
Translate Localizable.strings in de
100% translated source file: 'Localizable.strings'
on 'de'.
2024-04-25 17:57:59 +00:00
transifex-integration[bot]
5f7855d6d3
Translate Localizable.strings in de
100% translated source file: 'Localizable.strings'
on 'de'.
2024-04-25 17:56:48 +00:00
transifex-integration[bot]
e3db84778a
Translate Localizable.stringsdict in de
100% translated source file: 'Localizable.stringsdict'
on 'de'.
2024-04-25 17:22:18 +00:00
transifex-integration[bot]
5363a0313f
Translate Localizable.strings in de
100% translated source file: 'Localizable.strings'
on 'de'.
2024-04-25 17:21:37 +00:00
transifex-integration[bot]
cb116b0f85
Translate Localizable.strings in nl
100% translated source file: 'Localizable.strings'
on 'nl'.
2024-04-25 16:20:39 +00:00
transifex-integration[bot]
62e40d2824
Translate Localizable.stringsdict in nl
100% translated source file: 'Localizable.stringsdict'
on 'nl'.
2024-04-25 15:31:39 +00:00
Terry Yiu
e0b1985df5
Fix localization issues 2024-04-25 09:30:27 -04:00
Terry Yiu
be585e914d
Add Finnish translations 2024-04-24 23:34:50 -04:00
transifex-integration[bot]
e515bf7322
Translate Localizable.strings in fa
100% translated source file: 'Localizable.strings'
on 'fa'.
2024-04-24 23:11:36 -04:00
transifex-integration[bot]
050f38feac
Translate Localizable.strings in ko
100% translated source file: 'Localizable.strings'
on 'ko'.
2024-04-24 23:11:36 -04:00
transifex-integration[bot]
b94a435a9b
Translate Localizable.strings in pl_PL
100% translated source file: 'Localizable.strings'
on 'pl_PL'.
2024-04-24 23:11:35 -04:00
Daniel D’Aquino
279854a9fd ui: add First Aid view to settings
also create the contact list reset First Aid action

Automatically detecting whether or not to create a blank contact list
when we could not find any is very tricky. It could mean that no contact
list exists, but it could also mean that a temporary network or relay
outage occurred.

Since resetting the contact list when one already exists is a
destructive action, we should make no assumptions. Instead, we should
provide users the tool to fix it based on their own judgement.

For that reason, the first aid view was created. It detects if no
contact list was found, and in those cases, it gives them an option to
reset (with appropriate warning messages).

Testing 1: Contact list creation robustness
-----------------------------

Setup:
1. Network Link Conditioner installed and configured to this profile:
  - DNS delay: 400 ms
  - Downlink bandwidth: 100 kbps
  - Uplink bandwidth: 50 kbps
  - Packets dropped: 50% (On both uplink and downlink)
  - Delay: 1000 ms (Both uplink and downlink)

Procedure:
1. Turn Network Link conditioner ON
2. Go through the account creation steps
3. At the moment the onboarding follow suggestions screen shows up, quit the app
3. Turn Network Link conditioner OFF
4. Start the app again
5. Verify the home screen. It should present notes from the Damus account (the default follow)
6. Follow someone and wait for 5 seconds
7. Restart app
8. Look at the home feed. Notes from user from step 6 should appear, and that user should appear as being followed by you.

- Repro details:
  - Damus version: ada99418f6
  - Device: iPhone 15 simulator
  - iOS: 17.4
  - Number of runs: 3 times
  - Result: FAILS (issue is reproduced) 3 out of 3 times
- Test details:
  - Damus version: This commit
  - Device: iPhone 15 simulator
  - iOS: 17.4
  - Number of runs: 3 times
  - Result: PASSES all criteria 3 out of 3 times

Testing 2: Contact list First Aid
------------------------------

Setup:
1. Reproduce the issue with the old version as outlined in "Testing 1" above
2. Upgrade to the version in this commit

Steps:
1. Go to Settings > First Aid
2. A button to reset the contact list (and some text for context) should appear. PASS
3. Click on the button. A warning message should appear. PASS
4. Click "cancel". The action should be cancelled and nothing should have changed. PASS
5. Click on the reset button again.
6. Click "Continue" on the warning prompt. The reset button will now show "Contact list has been reset" with a green checkmark. PASS
5. Go back to the home tab. Notes from the Damus account should immediately appear. PASS
6. Try to follow someone and restart the app. Follows should now stick persistently. PASS
7. Go to the First Aid screen again. The reset option should no longer be present. PASS

Changelog-Added: Add First Aid solution for users who do not have a contact list created for their account
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240422230912.65056-4-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
2024-04-23 16:41:01 -07:00
Daniel D’Aquino
19ba020bd0 contacts: save first list to storage during onboarding
This commit adds a mechanism to add the contact list to storage as soon
as it is generated, and thus it reduces the risk of poor network
conditions causing issues.

Changelog-Fixed: Improve reliability of contact list creation during onboarding
Closes: https://github.com/damus-io/damus/issues/2057
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240422230912.65056-3-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
2024-04-23 16:40:50 -07:00
Daniel D’Aquino
43a5bbd53a contacts: save the users' latest contact event ID
... to a persistent setting, and try to load it from NostrDB on app start.

This commit causes the user's contact list event ID to be saved
persistently as a user-specific setting, and to be loaded immediately
after startup from the local NostrDB instance.

This helps improve reliability around contact lists, since we previously
relied on fetching that contact list from other relays.

Eventually we will not need the event ID to be stored at all, as we will
be able to query NostrDB, but for now having the latest event ID
persistently stored will allow us to get around this limitation in the
cleanest possible way (i.e. without having to store the event itself
into another mechanism, and migrating it later to NostrDB)

Other notes:

- It uses a mechanism similar to other user settings, so it is
  pubkey-specific and should handle login/logout cases

Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
Reviewed-by: William Casarin <jb55@jb55.com>
Link: 20240422230912.65056-2-daniel@daquino.me
Signed-off-by: William Casarin <jb55@jb55.com>
2024-04-23 16:40:25 -07:00
William Casarin
43630cbfa6 zaps: don't verify deschash
seems like most clients don't do this and apparently simplfies some
zapper implementations. It's not a huge deal for us since people can
fake bolt11s anyways.

Suggested-by: bumi, calle
Link: 20240418230321.1907519-1-jb55@jb55.com
Signed-off-by: William Casarin <jb55@jb55.com>
2024-04-23 09:05:13 -07:00
77 changed files with 746 additions and 408 deletions

View File

@ -282,7 +282,6 @@
4CA927632A290EB10098A105 /* EventTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927622A290EB10098A105 /* EventTop.swift */; };
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927642A290F1A0098A105 /* TimeDot.swift */; };
4CA927672A290F8B0098A105 /* RelativeTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927662A290F8B0098A105 /* RelativeTime.swift */; };
4CA9276A2A290FC00098A105 /* ContextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927692A290FC00098A105 /* ContextButton.swift */; };
4CA9276C2A2910D10098A105 /* ReplyPart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9276B2A2910D10098A105 /* ReplyPart.swift */; };
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; };
@ -319,7 +318,6 @@
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; };
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; };
4CCEB7AE29B53D260078AA28 /* SearchingEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CCEB7AD29B53D260078AA28 /* SearchingEventView.swift */; };
4CCEB7B029B5415A0078AA28 /* SearchingProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CCEB7AF29B5415A0078AA28 /* SearchingProfileView.swift */; };
4CD348EF29C3659D00497EB2 /* ImageUploadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD348EE29C3659D00497EB2 /* ImageUploadModel.swift */; };
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128929E9D10C0006FA5A /* SignalView.swift */; };
@ -640,6 +638,7 @@
D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; };
D7EDED342B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; };
D7FB10A72B0C371A00FA8D42 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C2B10272A7B0F5C008AA43E /* Log.swift */; };
D7FD12262BD345A700CF195B /* FirstAidSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */; };
D7FF94002AC7AC5300FD969D /* RelayURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */; };
E02429952B7E97740088B16C /* CameraController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02429942B7E97740088B16C /* CameraController.swift */; };
E02B54182B4DFADA0077FF42 /* Bech32ObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */; };
@ -741,6 +740,9 @@
3A41E55A299D52BE001FA465 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
3A41E55B299D52BE001FA465 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = id; path = id.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
3A4647CE2A413ADC00386AD8 /* CondensedProfilePicturesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CondensedProfilePicturesView.swift; sourceTree = "<group>"; };
3A47CB772BDA05A200728A7C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3A47CB782BDA05A200728A7C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
3A47CB792BDA05A200728A7C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fi; path = fi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
3A48E7AF29DFBE9D006E787E /* MutedThreadsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutedThreadsManager.swift; sourceTree = "<group>"; };
3A5C4575296A879E0032D398 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-419"; path = "es-419.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
@ -1194,7 +1196,6 @@
4CA927622A290EB10098A105 /* EventTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTop.swift; sourceTree = "<group>"; };
4CA927642A290F1A0098A105 /* TimeDot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeDot.swift; sourceTree = "<group>"; };
4CA927662A290F8B0098A105 /* RelativeTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelativeTime.swift; sourceTree = "<group>"; };
4CA927692A290FC00098A105 /* ContextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextButton.swift; sourceTree = "<group>"; };
4CA9276B2A2910D10098A105 /* ReplyPart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyPart.swift; sourceTree = "<group>"; };
4CA9276D2A2A5D110098A105 /* wasm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wasm.h; sourceTree = "<group>"; };
4CA9276E2A2A5D110098A105 /* wasm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wasm.c; sourceTree = "<group>"; };
@ -1238,7 +1239,6 @@
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfile.swift; sourceTree = "<group>"; };
4CC7AAF9297F64AC00430951 /* EventMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventMenu.swift; sourceTree = "<group>"; };
4CCEB7AD29B53D260078AA28 /* SearchingEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingEventView.swift; sourceTree = "<group>"; };
4CCEB7AF29B5415A0078AA28 /* SearchingProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchingProfileView.swift; sourceTree = "<group>"; };
4CD348EE29C3659D00497EB2 /* ImageUploadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageUploadModel.swift; sourceTree = "<group>"; };
4CD7641A28A1641400B6928F /* EndBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndBlock.swift; sourceTree = "<group>"; };
4CDA128929E9D10C0006FA5A /* SignalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalView.swift; sourceTree = "<group>"; };
@ -1434,6 +1434,7 @@
D7EDED202B117DCA0018B19C /* SequenceUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceUtils.swift; sourceTree = "<group>"; };
D7EDED2D2B128E8A0018B19C /* CollectionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionExtension.swift; sourceTree = "<group>"; };
D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusUserDefaults.swift; sourceTree = "<group>"; };
D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstAidSettingsView.swift; sourceTree = "<group>"; };
D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayURL.swift; sourceTree = "<group>"; };
E02429942B7E97740088B16C /* CameraController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraController.swift; sourceTree = "<group>"; };
E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bech32ObjectTests.swift; sourceTree = "<group>"; };
@ -1725,6 +1726,7 @@
4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */,
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */,
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */,
D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -2289,7 +2291,6 @@
4CA927622A290EB10098A105 /* EventTop.swift */,
4CC7AAF3297F18B400430951 /* ReplyDescription.swift */,
4CA927662A290F8B0098A105 /* RelativeTime.swift */,
4CA927692A290FC00098A105 /* ContextButton.swift */,
4CA9276B2A2910D10098A105 /* ReplyPart.swift */,
5C7389B02B6EFA7100781E0A /* ProxyView.swift */,
);
@ -2400,7 +2401,6 @@
isa = PBXGroup;
children = (
4CCEB7AD29B53D260078AA28 /* SearchingEventView.swift */,
4CCEB7AF29B5415A0078AA28 /* SearchingProfileView.swift */,
4C9D6D1A2B1D35D7004E5CD9 /* PullDownSearch.swift */,
);
path = Search;
@ -2937,6 +2937,7 @@
"es-419",
"es-ES",
fa,
fi,
fr,
"hu-HU",
id,
@ -3079,7 +3080,6 @@
4C32B9572A9AD44700DC3548 /* Root.swift in Sources */,
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */,
4CA9276A2A290FC00098A105 /* ContextButton.swift in Sources */,
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */,
4C32B9542A9AD44700DC3548 /* FlatBuffersUtils.swift in Sources */,
D7EDED1C2B1178FE0018B19C /* NoteContent.swift in Sources */,
@ -3110,6 +3110,7 @@
D7EDED1E2B11797D0018B19C /* LongformEvent.swift in Sources */,
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
D7FD12262BD345A700CF195B /* FirstAidSettingsView.swift in Sources */,
D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */,
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
@ -3253,7 +3254,6 @@
4C2B7BF22A71B6540049DEE7 /* Id.swift in Sources */,
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */,
4C7D09722A0AEF5E00943473 /* DamusGradient.swift in Sources */,
4CCEB7B029B5415A0078AA28 /* SearchingProfileView.swift in Sources */,
4C463CBF2B960B96008A8C36 /* PurpleBackdrop.swift in Sources */,
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
@ -3767,6 +3767,7 @@
3AC59CA929CDDB78007E04A6 /* pt-BR */,
3A821C4029E819D500B4BCA7 /* fr */,
3ABACEC02A5B3ED10037A847 /* sw */,
3A47CB792BDA05A200728A7C /* fi */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
@ -3802,6 +3803,7 @@
3AC59CA829CDDB78007E04A6 /* pt-BR */,
3A821C3F29E819D500B4BCA7 /* fr */,
3ABACEBF2A5B3ED10037A847 /* sw */,
3A47CB772BDA05A200728A7C /* fi */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
@ -3838,6 +3840,7 @@
3AC59CA729CDDB78007E04A6 /* pt-BR */,
3A821C3E29E819D500B4BCA7 /* fr */,
3ABACEC12A5B3ED10037A847 /* sw */,
3A47CB782BDA05A200728A7C /* fi */,
);
name = Localizable.strings;
sourceTree = "<group>";

View File

@ -35,7 +35,7 @@ struct SearchHeaderView: View {
}
var SearchText: Text {
Text(verbatim: described.description)
Text(described.description)
}
var body: some View {
@ -83,9 +83,9 @@ struct SingleCharacterAvatar: View {
var body: some View {
NonImageAvatar {
Text(verbatim: character)
Text(character)
.font(.largeTitle.bold())
.mask(Text(verbatim: character)
.mask(Text(character)
.font(.largeTitle.bold()))
}
}

View File

@ -192,7 +192,7 @@ struct UserStatusSheet: View {
Picker(NSLocalizedString("Duration", comment: "Label for profile status expiration duration picker."), selection: $duration) {
ForEach(StatusDuration.allCases, id: \.self) { d in
Text(verbatim: d.description)
Text(d.description)
.tag(d)
}
}

View File

@ -29,7 +29,8 @@ struct SupporterBadge: View {
.frame(width:size, height:size)
.foregroundStyle(GoldGradient)
if self.style == .full {
Text(verbatim: format_date(date: purple_account.created_at, time_style: .none))
let date = format_date(date: purple_account.created_at, time_style: .none)
Text(date)
.foregroundStyle(.secondary)
.font(.caption)
}

View File

@ -680,10 +680,7 @@ struct ContentView: View {
let relay_filters = RelayFilters(our_pubkey: pubkey)
let bootstrap_relays = load_bootstrap_relays(pubkey: pubkey)
// dumb stuff needed for property wrappers
UserSettingsStore.pubkey = pubkey
let settings = UserSettingsStore()
UserSettingsStore.shared = settings
let settings = UserSettingsStore.globally_load_for(pubkey: pubkey)
let new_relay_filters = load_relay_filters(pubkey) == nil
for relay in bootstrap_relays {

View File

@ -7,7 +7,6 @@
import Foundation
class Contacts {
private var friends: Set<Pubkey> = Set()
private var friend_of_friends: Set<Pubkey> = Set()
@ -15,7 +14,13 @@ class Contacts {
private var pubkey_to_our_friends = [Pubkey : Set<Pubkey>]()
let our_pubkey: Pubkey
var event: NostrEvent?
var delegate: ContactsDelegate? = nil
var event: NostrEvent? {
didSet {
guard let event else { return }
self.delegate?.latest_contact_event_changed(new_event: event)
}
}
init(our_pubkey: Pubkey) {
self.our_pubkey = our_pubkey
@ -88,3 +93,8 @@ class Contacts {
return Array((pubkey_to_our_friends[pubkey] ?? Set()))
}
}
/// Delegate protocol for `Contacts`. Use this to listen to significant updates from a `Contacts` instance
protocol ContactsDelegate {
func latest_contact_event_changed(new_event: NostrEvent)
}

View File

@ -41,11 +41,15 @@ enum HomeResubFilter {
}
}
class HomeModel {
class HomeModel: ContactsDelegate {
// Don't trigger a user notification for events older than a certain age
static let event_max_age_for_notification: TimeInterval = EVENT_MAX_AGE_FOR_NOTIFICATION
var damus_state: DamusState
var damus_state: DamusState {
didSet {
self.load_our_stuff_from_damus_state()
}
}
// NDBTODO: let's get rid of this entirely, let nostrdb handle it
var has_event: [String: Set<NoteId>] = [:]
@ -108,6 +112,32 @@ class HomeModel {
self.should_debounce_dms = false
}
}
// MARK: - Loading items from DamusState
/// This is called whenever DamusState gets set. This function is used to load or setup anything we need from the new DamusState
func load_our_stuff_from_damus_state() {
self.load_latest_contact_event_from_damus_state()
}
/// This loads the latest contact event we have on file from NostrDB. This should be called as soon as we get the new DamusState
/// Loading the latest contact list event into our `Contacts` instance from storage is important to avoid getting into weird states when the network is unreliable or when relays delete such information
func load_latest_contact_event_from_damus_state() {
guard let latest_contact_event_id_hex = damus_state.settings.latest_contact_event_id_hex else { return }
guard let latest_contact_event_id = NoteId(hex: latest_contact_event_id_hex) else { return }
guard let latest_contact_event: NdbNote = damus_state.ndb.lookup_note( latest_contact_event_id)?.unsafeUnownedValue?.to_owned() else { return }
process_contact_event(state: damus_state, ev: latest_contact_event)
damus_state.contacts.delegate = self
}
// MARK: - ContactsDelegate functions
func latest_contact_event_changed(new_event: NostrEvent) {
// When the latest user contact event has changed, save its ID so we know exactly where to find it next time
damus_state.settings.latest_contact_event_id_hex = new_event.id.hex()
}
// MARK: - Nostr event and subscription handling
func resubscribe(_ resubbing: Resubscribe) {
if self.should_debounce_dms {

View File

@ -96,6 +96,14 @@ class UserSettingsStore: ObservableObject {
static var shared: UserSettingsStore? = nil
static var bool_options = Set<String>()
static func globally_load_for(pubkey: Pubkey) -> UserSettingsStore {
// dumb stuff needed for property wrappers
UserSettingsStore.pubkey = pubkey
let settings = UserSettingsStore()
UserSettingsStore.shared = settings
return settings
}
@StringSetting(key: "default_wallet", default_value: .system_default_wallet)
var default_wallet: Wallet
@ -312,6 +320,12 @@ class UserSettingsStore: ObservableObject {
return internal_winetranslate_api_key != nil
}
}
// MARK: Internal, hidden settings
@Setting(key: "latest_contact_event_id", default_value: nil)
var latest_contact_event_id_hex: String?
}
func pk_setting_key(_ pubkey: Pubkey, key: String) -> String {

View File

@ -226,19 +226,23 @@ class RelayPool {
print("queueing request for \(relay)")
request_queue.append(QueuedRequest(req: r, relay: relay, skip_ephemeral: skip_ephemeral))
}
func send_raw_to_local_ndb(_ req: NostrRequestType) {
// send to local relay (nostrdb)
switch req {
case .typical(let r):
if case .event = r, let rstr = make_nostr_req(r) {
let _ = ndb.process_client_event(rstr)
}
case .custom(let string):
let _ = ndb.process_client_event(string)
}
}
func send_raw(_ req: NostrRequestType, to: [RelayURL]? = nil, skip_ephemeral: Bool = true) {
let relays = to.map{ get_relays($0) } ?? self.relays
// send to local relay (nostrdb)
switch req {
case .typical(let r):
if case .event = r, let rstr = make_nostr_req(r) {
let _ = ndb.process_client_event(rstr)
}
case .custom(let string):
let _ = ndb.process_client_event(string)
}
self.send_raw_to_local_ndb(req)
for relay in relays {
if req.is_read && !(relay.descriptor.info.read ?? true) {

View File

@ -17,7 +17,7 @@ class CompatibleText: Equatable {
return AnyView(
VStack {
Image("warning")
Text(NSLocalizedString("This note contains too many items and cannot be rendered", comment: "Error message indicating that a note is too big and cannot be rendered"))
Text("This note contains too many items and cannot be rendered", comment: "Error message indicating that a note is too big and cannot be rendered")
.multilineTextAlignment(.center)
}
.foregroundColor(.secondary)

View File

@ -30,6 +30,7 @@ enum Route: Hashable {
case ReactionsSettings(settings: UserSettingsStore)
case SearchSettings(settings: UserSettingsStore)
case DeveloperSettings(settings: UserSettingsStore)
case FirstAidSettings(settings: UserSettingsStore)
case Thread(thread: ThreadModel)
case Reposts(reposts: EventsModel)
case QuoteReposts(quotes: EventsModel)
@ -89,6 +90,8 @@ enum Route: Hashable {
SearchSettingsView(settings: settings)
case .DeveloperSettings(let settings):
DeveloperSettingsView(settings: settings)
case .FirstAidSettings(settings: let settings):
FirstAidSettingsView(damus_state: damusState, settings: settings)
case .Thread(let thread):
ThreadView(state: damusState, thread: thread)
case .Reposts(let reposts):
@ -175,6 +178,8 @@ enum Route: Hashable {
hasher.combine("searchSettings")
case .DeveloperSettings:
hasher.combine("developerSettings")
case .FirstAidSettings:
hasher.combine("firstAidSettings")
case .Thread(let threadModel):
hasher.combine("thread")
hasher.combine(threadModel.event.id)

View File

@ -411,9 +411,6 @@ func get_zap_description(_ ev: NostrEvent, inv_desc: InvoiceDescription) -> Stri
guard let data = desc.data(using: .utf8) else {
return nil
}
guard sha256(data) == deschash else {
return nil
}
return desc
}

View File

@ -36,7 +36,7 @@ struct ShareActionButton: View {
.frame(width: 55.0, height: 55.0)
}
.frame(height: 25)
Text(verbatim: text)
Text(text)
.foregroundColor(col)
.font(.footnote)
.multilineTextAlignment(.center)

View File

@ -121,7 +121,7 @@ struct AddRelayView: View {
dismiss()
}) {
HStack {
Text(verbatim: "Add relay")
Text("Add relay", comment: "Button to add a relay.")
.bold()
}
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)

View File

@ -33,7 +33,7 @@ struct BookmarksView: View {
.resizable()
.scaledToFit()
.frame(width: 32.0, height: 32.0)
Text(NSLocalizedString("You have no bookmarks yet, add them in the context menu", comment: "Text indicating that there are no bookmarks to be viewed"))
Text("You have no bookmarks yet, add them in the context menu", comment: "Text indicating that there are no bookmarks to be viewed")
}
} else {
ScrollView {

View File

@ -22,7 +22,8 @@ struct GradientFollowButton: View {
Button(action: {
follow_state = perform_follow_btn_action(follow_state, target: target)
}) {
Text(follow_btn_txt(follow_state, follows_you: follows_you))
let followButtonText = follow_btn_txt(follow_state, follows_you: follows_you)
Text(followButtonText)
.foregroundColor(follow_state == .unfollows ? .white : grayTextColor)
.font(.callout)
.fontWeight(.medium)

View File

@ -67,6 +67,10 @@ struct ConfigView: View {
NavigationLink(value: Route.DeveloperSettings(settings: settings)) {
IconLabel(NSLocalizedString("Developer", comment: "Section header for developer settings"), img_name: "magic-stick2.fill", color: DamusColors.adaptableBlack)
}
NavigationLink(value: Route.FirstAidSettings(settings: settings)) {
IconLabel(NSLocalizedString("First Aid", comment: "Section header for first aid tools and settings"), img_name: "help2", color: .red)
}
}
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
@ -84,7 +88,7 @@ struct ConfigView: View {
}
if state.is_privkey_user {
Section(header: Text(NSLocalizedString("Permanently Delete Account", comment: "Section title for deleting the user"))) {
Section(header: Text("Permanently Delete Account", comment: "Section title for deleting the user")) {
Button(action: {
delete_account_warning = true
}, label: {

View File

@ -28,7 +28,7 @@ struct CreateAccountView: View {
VStack(alignment: .center) {
EditPictureControl(uploader: .nostrBuild, pubkey: account.pubkey, image_url: $account.profile_image , uploadObserver: profileUploadObserver, callback: uploadedProfilePicture)
Text(NSLocalizedString("Public Key", comment: "Label to indicate the public key of the account."))
Text("Public Key", comment: "Label to indicate the public key of the account.")
.bold()
.padding()
.onTapGesture {

View File

@ -1,20 +0,0 @@
//
// ContextButton.swift
// damus
//
// Created by William Casarin on 2023-06-01.
//
import SwiftUI
struct ContextButton: View {
var body: some View {
Text(verbatim: /*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
struct ContextButton_Previews: PreviewProvider {
static var previews: some View {
ContextButton()
}
}

View File

@ -36,7 +36,7 @@ struct ProxyView: View {
HStack {
let protocolLogo = get_protocol_image(protocolName: proxy.protocolName)
if protocolLogo.isEmpty {
Text("\(proxy.protocolName)")
Text(proxy.protocolName)
.font(.caption)
} else {
Image(protocolLogo)

View File

@ -145,7 +145,7 @@ struct FullScreenCarouselView_Previews: PreviewProvider {
HStack {
Spacer()
Text("Some content")
Text(verbatim: "Some content")
.padding()
.foregroundColor(.white)

View File

@ -17,7 +17,7 @@ struct MuteDurationMenu<T: View>: View {
Button {
action(duration)
} label: {
Text("\(duration.title)")
Text(duration.title)
}
}
} label: {
@ -30,6 +30,6 @@ struct MuteDurationMenu<T: View>: View {
MuteDurationMenu { _ in
} label: {
Text("Mute hashtag")
Text(verbatim: "Mute hashtag")
}
}

View File

@ -62,7 +62,7 @@ struct MutelistView: View {
Section(NSLocalizedString("Hashtags", comment: "Section header title for a list of hashtags that are muted.")) {
ForEach(hashtags, id: \.self) { item in
if case let MuteItem.hashtag(hashtag, _) = item {
Text("#\(hashtag.hashtag)")
Text(verbatim: "#\(hashtag.hashtag)")
.id(hashtag.hashtag)
.swipeActions {
RemoveAction(item: .hashtag(hashtag, nil))
@ -76,7 +76,7 @@ struct MutelistView: View {
Section(NSLocalizedString("Words", comment: "Section header title for a list of words that are muted.")) {
ForEach(words, id: \.self) { item in
if case let MuteItem.word(word, _) = item {
Text("\(word)")
Text(word)
.id(word)
.swipeActions {
RemoveAction(item: .word(word, nil))
@ -94,7 +94,7 @@ struct MutelistView: View {
RemoveAction(item: .thread(note_id, nil))
}
} else {
Text(NSLocalizedString("Error retrieving muted event", comment: "Text for an item that application failed to retrieve the muted event for."))
Text("Error retrieving muted event", comment: "Text for an item that application failed to retrieve the muted event for.")
}
}
}

View File

@ -37,9 +37,9 @@ struct DamusAppNotificationView: View {
.shadow(radius: 5, y: 5)
VStack(alignment: .leading, spacing: 5) {
HStack(alignment: .center, spacing: 3) {
Text(NSLocalizedString("Damus", comment: "Name of the app for the title of an internal notification"))
Text("Damus", comment: "Name of the app for the title of an internal notification")
.font(.body.weight(.bold))
Text("·")
Text(verbatim: "·")
.foregroundStyle(.secondary)
Text(relative_date)
.font(.system(size: 16))
@ -49,7 +49,7 @@ struct DamusAppNotificationView: View {
Image("check-circle.fill")
.resizable()
.frame(width: 15, height: 15)
Text(NSLocalizedString("Internal app notification", comment: "Badge indicating that a notification is an official internal app notification"))
Text("Internal app notification", comment: "Badge indicating that a notification is an official internal app notification")
.font(.caption2)
.bold()
}

View File

@ -196,7 +196,7 @@ struct EventGroupView: View {
return VStack(alignment: .center) {
Image("zap.fill")
.foregroundColor(.orange)
Text(verbatim: fmt)
Text(fmt)
.foregroundColor(Color.orange)
}
}

View File

@ -36,7 +36,7 @@ struct OnboardingSuggestionsView: View {
.navigationBarItems(leading: Button(action: {
self.next_page()
}, label: {
Text(NSLocalizedString("Skip", comment: "Button to dismiss the suggested users screen"))
Text("Skip", comment: "Button to dismiss the suggested users screen")
.font(.subheadline.weight(.semibold))
}))
.tag(0)
@ -48,7 +48,7 @@ struct OnboardingSuggestionsView: View {
AnyView(
HStack {
Image(systemName: "sparkles")
Text(NSLocalizedString("Add your first post", comment: "Prompt given to the user during onboarding, suggesting them to write their first post"))
Text("Add your first post", comment: "Prompt given to the user during onboarding, suggesting them to write their first post")
}
.foregroundColor(.secondary)
.font(.callout)
@ -97,7 +97,7 @@ fileprivate struct SuggestedUsersPageView: View {
Button(action: {
self.next_page()
}) {
Text(NSLocalizedString("Continue", comment: "Button to dismiss suggested users view and continue to the main app"))
Text("Continue", comment: "Button to dismiss suggested users view and continue to the main app")
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
}
.buttonStyle(GradientButtonStyle())

View File

@ -268,7 +268,7 @@ struct PostView: View {
Button(action: {
self.cancel()
}, label: {
Text(NSLocalizedString("Cancel", comment: "Button to cancel out of posting a note."))
Text("Cancel", comment: "Button to cancel out of posting a note.")
.padding(10)
})
.buttonStyle(NeutralButtonStyle())

View File

@ -199,7 +199,7 @@ struct ProfileView: View {
MuteDurationMenu { duration in
notify(.mute(.user(profile.pubkey, duration?.date_from_now)))
} label: {
Text(NSLocalizedString("Mute", comment: "Button to mute a profile."))
Text("Mute", comment: "Button to mute a profile.")
.foregroundStyle(.red)
}
}

View File

@ -59,7 +59,7 @@ struct ProfileActionSheetView: View {
}
)
.buttonStyle(NeutralButtonShape.circle.style)
Text(NSLocalizedString("Message", comment: "Button label that allows the user to start a direct message conversation with the user shown on-screen"))
Text("Message", comment: "Button label that allows the user to start a direct message conversation with the user shown on-screen")
.foregroundStyle(.secondary)
.font(.caption)
}
@ -114,7 +114,7 @@ struct ProfileActionSheetView: View {
label: {
HStack {
Spacer()
Text(NSLocalizedString("View full profile", comment: "A button label that allows the user to see the full profile of the profile they are previewing"))
Text("View full profile", comment: "A button label that allows the user to see the full profile of the profile they are previewing")
Image(systemName: "arrow.up.right")
Spacer()
}
@ -305,9 +305,9 @@ fileprivate struct ProfileActionSheetZapButton: View {
})
.alert(isPresented: $show_error_alert) {
Alert(
title: Text(NSLocalizedString("Zap failed", comment: "Title of an alert indicating that a zap action failed")),
title: Text("Zap failed", comment: "Title of an alert indicating that a zap action failed"),
message: Text(zap_state.error_message() ?? ""),
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Button label to dismiss an error dialog")))
dismissButton: .default(Text("OK", comment: "Button label to dismiss an error dialog"))
)
}
.onChange(of: zap_state) { new_zap_state in

View File

@ -57,7 +57,7 @@ struct PubkeyView: View {
.resizable()
.foregroundColor(DamusColors.green)
.frame(width: 20, height: 20)
Text(NSLocalizedString("Copied", comment: "Label indicating that a user's key was copied."))
Text("Copied", comment: "Label indicating that a user's key was copied.")
.font(.footnote)
.layoutPriority(1)
.foregroundColor(DamusColors.green)

View File

@ -31,9 +31,10 @@ struct DamusPurpleAccountView: View {
// TODO: Generalize this view instead of setting up dividers and paddings manually
VStack {
HStack {
Text(NSLocalizedString("Expiry date", comment: "Label for Purple subscription expiry date"))
Text("Expiry date", comment: "Label for Purple subscription expiry date")
Spacer()
Text(DateFormatter.localizedString(from: account.expiry, dateStyle: .short, timeStyle: .none))
let formattedDate = DateFormatter.localizedString(from: account.expiry, dateStyle: .short, timeStyle: .none)
Text(formattedDate)
}
.padding(.horizontal)
.padding(.top, 20)
@ -43,9 +44,10 @@ struct DamusPurpleAccountView: View {
.padding(.vertical, 10)
HStack {
Text(NSLocalizedString("Account creation", comment: "Label for Purple account creation date"))
Text("Account creation", comment: "Label for Purple account creation date")
Spacer()
Text(DateFormatter.localizedString(from: account.created_at, dateStyle: .short, timeStyle: .none))
let formattedDate = DateFormatter.localizedString(from: account.created_at, dateStyle: .short, timeStyle: .none)
Text(formattedDate)
}
.padding(.horizontal)
@ -54,7 +56,7 @@ struct DamusPurpleAccountView: View {
.padding(.vertical, 10)
HStack {
Text(NSLocalizedString("Subscriber number", comment: "Label for Purple account subscriber number"))
Text("Subscriber number", comment: "Label for Purple account subscriber number")
Spacer()
Text(verbatim: "#\(account.subscriber_number)")
}
@ -90,7 +92,7 @@ struct DamusPurpleAccountView: View {
.resizable()
.frame(width: 15, height: 15)
Text(NSLocalizedString("Active account", comment: "Badge indicating user has an active Damus Purple account"))
Text("Active account", comment: "Badge indicating user has an active Damus Purple account")
.font(.caption)
.bold()
}
@ -107,7 +109,7 @@ struct DamusPurpleAccountView: View {
.resizable()
.frame(width: 15, height: 15)
Text(NSLocalizedString("Expired account", comment: "Badge indicating user has an expired Damus Purple account"))
Text("Expired account", comment: "Badge indicating user has an expired Damus Purple account")
.font(.caption)
.bold()
}

View File

@ -61,7 +61,7 @@ struct DamusPurpleTranslationSetupView: View {
.opacity(start ? 1.0 : 0.0)
.animation(.content(), value: start)
Text(NSLocalizedString("You unlocked", comment: "Part 1 of 2 in message 'You unlocked automatic translations' the user gets when they sign up for Damus Purple" ))
Text("You unlocked", comment: "Part 1 of 2 in message 'You unlocked automatic translations' the user gets when they sign up for Damus Purple" )
.font(.largeTitle)
.fontWeight(.bold)
.foregroundStyle(
@ -95,7 +95,7 @@ struct DamusPurpleTranslationSetupView: View {
.opacity(start ? 1.0 : 0.0)
.animation(Animation.snappy(duration: 2).delay(0), value: start)
Text(NSLocalizedString("Automatic translations", comment: "Part 1 of 2 in message 'You unlocked automatic translations' the user gets when they sign up for Damus Purple"))
Text("Automatic translations", comment: "Part 1 of 2 in message 'You unlocked automatic translations' the user gets when they sign up for Damus Purple")
.font(.headline)
.fontWeight(.bold)
.foregroundStyle(
@ -110,7 +110,7 @@ struct DamusPurpleTranslationSetupView: View {
.animation(.content(), value: start)
.padding(.top, 10)
Text(NSLocalizedString("As part of your Damus Purple membership, you get complimentary and automated translations. Would you like to enable Damus Purple translations?\n\nTip: You can always change this later in Settings → Translations", comment: "Message notifying the user that they get auto-translations as part of their service"))
Text("As part of your Damus Purple membership, you get complimentary and automated translations. Would you like to enable Damus Purple translations?\n\nTip: You can always change this later in Settings → Translations", comment: "Message notifying the user that they get auto-translations as part of their service")
.lineSpacing(5)
.multilineTextAlignment(.center)
.foregroundStyle(.white.opacity(0.8))
@ -125,7 +125,7 @@ struct DamusPurpleTranslationSetupView: View {
}, label: {
HStack {
Spacer()
Text(NSLocalizedString("Enable Purple auto-translations", comment: "Label for button that allows users to enable Damus Purple translations"))
Text("Enable Purple auto-translations", comment: "Label for button that allows users to enable Damus Purple translations")
Spacer()
}
})
@ -139,7 +139,7 @@ struct DamusPurpleTranslationSetupView: View {
}, label: {
HStack {
Spacer()
Text(NSLocalizedString("No, thanks", comment: "Label for button that allows users to reject enabling Damus Purple translations"))
Text("No, thanks", comment: "Label for button that allows users to reject enabling Damus Purple translations")
Spacer()
}
})

View File

@ -53,7 +53,7 @@ struct DamusPurpleVerifyNpubView: View {
}, label: {
HStack {
Spacer()
Text(NSLocalizedString("Verify my npub", comment: "Button label to verify the user's npub for the purpose of Purple subscription checkout"))
Text("Verify my npub", comment: "Button label to verify the user's npub for the purpose of Purple subscription checkout")
Spacer()
}
})
@ -61,7 +61,7 @@ struct DamusPurpleVerifyNpubView: View {
.buttonStyle(GradientButtonStyle())
}
else {
Text(NSLocalizedString("Verified!", comment: "Instructions after the user has verified their npub for Damus Purple purchase checkout"))
Text("Verified!", comment: "Instructions after the user has verified their npub for Damus Purple purchase checkout")
.frame(height: subtitle_height)
.multilineTextAlignment(.center)
.foregroundColor(.green)
@ -71,7 +71,7 @@ struct DamusPurpleVerifyNpubView: View {
}, label: {
HStack {
Spacer()
Text(NSLocalizedString("Continue", comment: "Prompt to user to continue"))
Text("Continue", comment: "Prompt to user to continue")
Spacer()
}
})

View File

@ -138,7 +138,7 @@ struct DamusPurpleView: View, DamusPurpleStoreKitManagerDelegate {
if let account_uuid {
DamusPurpleView.IAPProductStateView(products: products, purchased: purchased, account_uuid: account_uuid, subscribe: subscribe)
if let iap_error {
Text(String(format: NSLocalizedString("There has been an unexpected error with the in-app purchase. Please try again later or contact support@damus.io. Error: %@", comment: "In-app purchase error message for the user"), iap_error))
Text("There has been an unexpected error with the in-app purchase. Please try again later or contact support@damus.io. Error: \(iap_error)", comment: "In-app purchase error message for the user")
.foregroundStyle(.red)
.multilineTextAlignment(.center)
.padding(.horizontal)
@ -158,7 +158,7 @@ struct DamusPurpleView: View, DamusPurpleStoreKitManagerDelegate {
}
var ManageOnWebsiteNote: some View {
Text(NSLocalizedString("Visit the Damus website on a web browser to manage billing", comment: "Instruction on how to manage billing externally"))
Text("Visit the Damus website on a web browser to manage billing", comment: "Instruction on how to manage billing externally")
.font(.caption)
.foregroundColor(.white.opacity(0.6))
.multilineTextAlignment(.center)

View File

@ -37,7 +37,7 @@ struct DamusPurpleWelcomeView: View {
.opacity(start ? 1.0 : 0.0)
.animation(.content(), value: start)
Text(NSLocalizedString("Welcome to Purple", comment: "Greeting to subscription service"))
Text("Welcome to Purple", comment: "Greeting to subscription service")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundStyle(
@ -70,7 +70,7 @@ struct DamusPurpleWelcomeView: View {
.opacity(start ? 1.0 : 0.0)
.animation(Animation.snappy(duration: 2).delay(0), value: start)
Text(NSLocalizedString("Thank you very much for signing up for Damus\u{00A0}Purple. Your contribution helps us continue our fight for a more Open and Free\u{00A0}internet.\n\nYou will also get access to premium features, and a star badge on your profile.\n\nEnjoy!", comment: "Appreciation to user for purchasing subscription service"))
Text("Thank you very much for signing up for Damus\u{00A0}Purple. Your contribution helps us continue our fight for a more Open and Free\u{00A0}internet.\n\nYou will also get access to premium features, and a star badge on your profile.\n\nEnjoy!", comment: "Appreciation to user for purchasing subscription service")
.lineSpacing(5)
.multilineTextAlignment(.center)
.foregroundStyle(.white.opacity(0.8))
@ -85,7 +85,7 @@ struct DamusPurpleWelcomeView: View {
}, label: {
HStack {
Spacer()
Text(NSLocalizedString("Continue", comment: "Prompt to user to continue"))
Text("Continue", comment: "Prompt to user to continue")
Spacer()
}
})

View File

@ -26,7 +26,7 @@ extension DamusPurpleView {
var body: some View {
if subscription_purchase_loading {
HStack(spacing: 10) {
Text(NSLocalizedString("Purchasing", comment: "Loading label indicating the purchase action is in progress"))
Text("Purchasing", comment: "Loading label indicating the purchase action is in progress")
.foregroundStyle(.white)
ProgressView()
.progressViewStyle(.circular)
@ -66,7 +66,7 @@ extension DamusPurpleView {
}
func PurchasedUnmanageableView(_ purchased: PurchasedProduct) -> some View {
Text(NSLocalizedString("This device's in-app purchase is registered to a different Nostr account. Unable to manage this Purple account. If you believe this was a mistake, please contact us via support@damus.io.", comment: "Notice label that user cannot manage their In-App purchases"))
Text("This device's in-app purchase is registered to a different Nostr account. Unable to manage this Purple account. If you believe this was a mistake, please contact us via support@damus.io.", comment: "Notice label that user cannot manage their In-App purchases")
.font(.caption)
.foregroundColor(.white.opacity(0.6))
.multilineTextAlignment(.center)
@ -76,21 +76,21 @@ extension DamusPurpleView {
func PurchasedManageView(_ purchased: PurchasedProduct) -> some View {
VStack(spacing: 10) {
if SHOW_IAP_DEBUG_INFO == true {
Text(NSLocalizedString("Purchased!", comment: "User purchased a subscription"))
Text("Purchased!", comment: "User purchased a subscription")
.font(.title2)
.foregroundColor(.white)
price_description(product: purchased.product)
.foregroundColor(.white)
.opacity(0.65)
.frame(width: 200)
Text(NSLocalizedString("Purchased on", comment: "Indicating when the user purchased the subscription"))
Text("Purchased on", comment: "Indicating when the user purchased the subscription")
.font(.title2)
.foregroundColor(.white)
Text(format_date(date: purchased.tx.purchaseDate))
.foregroundColor(.white)
.opacity(0.65)
if let expiry = purchased.tx.expirationDate {
Text(NSLocalizedString("Renews on", comment: "Indicating when the subscription will renew"))
Text("Renews on", comment: "Indicating when the subscription will renew")
.font(.title2)
.foregroundColor(.white)
Text(format_date(date: expiry))
@ -101,7 +101,7 @@ extension DamusPurpleView {
Button(action: {
show_manage_subscriptions = true
}, label: {
Text(NSLocalizedString("Manage", comment: "Manage the damus subscription"))
Text("Manage", comment: "Manage the damus subscription")
.padding(.horizontal, 20)
})
.buttonStyle(GradientButtonStyle())
@ -112,7 +112,7 @@ extension DamusPurpleView {
func ProductsView(_ products: [Product]) -> some View {
VStack(spacing: 10) {
Text(NSLocalizedString("Save 20% off on an annual subscription", comment: "Savings for purchasing an annual subscription"))
Text("Save 20% off on an annual subscription", comment: "Savings for purchasing an annual subscription")
.font(.callout.bold())
.foregroundColor(.white)
ForEach(products) { product in
@ -132,7 +132,7 @@ extension DamusPurpleView {
.buttonStyle(GradientButtonStyle())
}
Text("By subscribing to Damus Purple you are accepting our [privacy policy](https://damus.io/privacy-policy.txt) and Apple's Standard [EULA](https://www.apple.com/legal/internet-services/itunes/dev/stdeula/)")
Text("By subscribing to Damus Purple, you are accepting our [privacy policy](https://damus.io/privacy-policy.txt) and Apple's Standard [EULA](https://www.apple.com/legal/internet-services/itunes/dev/stdeula/)", comment: "Text explaining the terms and conditions of subscribing to Damus Purple. EULA stands for End User License Agreement.")
.foregroundColor(.white.opacity(0.6))
.font(.caption)
.padding()
@ -148,11 +148,11 @@ extension DamusPurpleView {
Text(purple_type?.label() ?? product.displayName)
Spacer()
if let non_discounted_price = purple_type?.non_discounted_price(product: product) {
Text(verbatim: non_discounted_price)
Text(non_discounted_price)
.strikethrough()
.foregroundColor(DamusColors.white.opacity(0.5))
}
Text(verbatim: product.displayPrice)
Text(product.displayPrice)
.fontWeight(.bold)
}
)

View File

@ -27,7 +27,7 @@ extension DamusPurpleView {
.shadow(radius: 5)
VStack(alignment: .leading) {
Text(NSLocalizedString("Purple", comment: "Subscription service name"))
Text("Purple", comment: "Subscription service name")
.font(.system(size: 60.0).weight(.bold))
.foregroundStyle(
LinearGradient(

View File

@ -38,7 +38,7 @@ extension DamusPurpleView {
.resizable()
.frame(width: 15, height: 15)
Text(NSLocalizedString("Coming soon", comment: "Feature is still in development and will be available soon"))
Text("Coming soon", comment: "Feature is still in development and will be available soon")
.font(.caption)
.bold()
}

View File

@ -25,6 +25,6 @@ struct PurpleBackdrop<T: View>: View {
#Preview {
PurpleBackdrop {
Text("Hello, World")
Text(verbatim: "Hello, World")
}
}

View File

@ -67,14 +67,14 @@ struct PurpleViewPrimitives {
struct ProductLoadErrorView: View {
var body: some View {
Text(NSLocalizedString("Subscription Error", comment: "Ah dang there was an error loading subscription information from the AppStore. Please try again later :("))
Text("Subscription Error", comment: "Ah dang there was an error loading subscription information from the AppStore. Please try again later :(")
.foregroundColor(.white)
}
}
struct SaveTextView: View {
var body: some View {
Text(NSLocalizedString("Save 14%", comment: "Percentage of purchase price the user will save"))
Text("Save 14%", comment: "Percentage of purchase price the user will save")
.font(.callout)
.italic()
.foregroundColor(DamusColors.green)

View File

@ -15,7 +15,7 @@ struct RelayAdminDetail: View {
var body: some View {
HStack(spacing: 15) {
VStack(spacing: 10) {
Text("ADMIN")
Text("ADMIN", comment: "Text label indicating the profile picture underneath it is the admin of the Nostr relay.")
.font(.caption)
.fontWeight(.heavy)
.foregroundColor(DamusColors.mediumGrey)
@ -36,18 +36,18 @@ struct RelayAdminDetail: View {
Divider().frame(width: 1)
VStack {
Text("CONTACT")
Text("CONTACT", comment: "Text label indicating that the information below is the contact information of the admin of the Nostr relay.")
.font(.caption)
.fontWeight(.heavy)
.foregroundColor(DamusColors.mediumGrey)
Image("messages")
.foregroundColor(.gray)
if nip11?.contact == "" {
Text("N/A")
if let contact = nip11?.contact, !contact.isEmpty {
Text(contact)
.font(.subheadline)
.foregroundColor(.gray)
} else {
Text(nip11?.contact ?? "N/A")
Text("N/A", comment: "Text label indicating that there is no NIP-11 relay admin contact information found. In English, N/A stands for not applicable.")
.font(.subheadline)
.foregroundColor(.gray)
}

View File

@ -15,7 +15,7 @@ struct RelayAuthenticationDetail: View {
case .none:
EmptyView()
case .pending:
Text(NSLocalizedString("Pending", comment: "Label to display that authentication to a server is pending."))
Text("Pending", comment: "Label to display that authentication to a server is pending.")
.font(.caption)
.frame(height: 20)
.padding(.horizontal, 10)
@ -27,7 +27,7 @@ struct RelayAuthenticationDetail: View {
.stroke(DamusColors.warningBorder, lineWidth: 1)
)
case .verified:
Text(NSLocalizedString("Authenticated", comment: "Label to display that authentication to a server has succeeded."))
Text("Authenticated", comment: "Label to display that authentication to a server has succeeded.")
.font(.caption)
.frame(height: 20)
.padding(.horizontal, 10)
@ -39,7 +39,7 @@ struct RelayAuthenticationDetail: View {
.stroke(DamusColors.successBorder, lineWidth: 1)
)
case .error:
Text(NSLocalizedString("Error", comment: "Label to display that authentication to a server has failed."))
Text("Error", comment: "Label to display that authentication to a server has failed.")
.font(.caption)
.frame(height: 20)
.padding(.horizontal, 10)

View File

@ -47,7 +47,7 @@ struct RelayNipList: View {
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(NSLocalizedString("Supported NIPs", comment: "Label to display relay's supported NIPs."))
Text("Supported NIPs", comment: "Label to display relay's supported NIPs.")
.font(.callout)
.fontWeight(.bold)
.foregroundColor(DamusColors.mediumGrey)

View File

@ -19,18 +19,21 @@ struct RelayPaidDetail: View {
let formattedString = formatter.string(from: TimeInterval(time)) ?? ""
return formattedString
}
func displayAmount(unit: String, amount: Int64) -> String {
if unit == "msats" {
format_msats(amount)
} else {
"\(amount) \(unit)"
}
}
func Amount(unit: String, amount: Int64) -> some View {
HStack {
if unit == "msats" {
Text("\(format_msats(amount))")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
} else {
Text("\(amount) \(unit)")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
}
let displayString = displayAmount(unit: unit, amount: amount)
Text(displayString)
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
}
}
@ -48,26 +51,24 @@ struct RelayPaidDetail: View {
if !admission.isEmpty {
Amount(unit: admission[0].unit, amount: admission[0].amount)
} else {
Text(verbatim: "Paid Relay")
Text("Paid Relay", comment: "Text indicating that this is a paid relay.")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
}
} else if let subscription = fees?.subscription {
if !subscription.isEmpty {
Amount(unit: subscription[0].unit, amount: subscription[0].amount)
Text("/ \(timeString(time: subscription[0].period))")
Text("\(displayAmount(unit: subscription[0].unit, amount: subscription[0].amount)) / \(timeString(time: subscription[0].period))", comment: "Amount of money required to subscribe to the Nostr relay. In English, this would look something like '4,000 sats / 30 days', meaning it costs 4000 sats to subscribe to the Nostr relay for 30 days.")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
}
} else if let publication = fees?.publication {
if !publication.isEmpty {
Amount(unit: publication[0].unit, amount: publication[0].amount)
Text("/ event")
Text("\(displayAmount(unit: publication[0].unit, amount: publication[0].amount)) / event", comment: "Amount of money required to publish to the Nostr relay. In English, this would look something like '10 sats / event', meaning it costs 10 sats to publish one event.")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
}
} else {
Text(verbatim: "Paid Relay")
Text("Paid Relay", comment: "Text indicating that this is a paid relay.")
.font(.system(size: 13, weight: .heavy))
.foregroundColor(DamusColors.white)
}

View File

@ -14,7 +14,7 @@ struct RelaySoftwareDetail: View {
var body: some View {
HStack(spacing: 15) {
VStack {
Text("SOFTWARE")
Text("SOFTWARE", comment: "Text label indicating which relay software is used to run this Nostr relay.")
.font(.caption)
.fontWeight(.heavy)
.foregroundColor(DamusColors.mediumGrey)
@ -24,26 +24,37 @@ struct RelaySoftwareDetail: View {
let software = nip11?.software
let softwareSeparated = software?.components(separatedBy: "/")
let softwareShortened = softwareSeparated?.last
Text(softwareShortened ?? "N/A")
.font(.subheadline)
.foregroundColor(.gray)
if let softwareShortened = softwareSeparated?.last {
Text(softwareShortened)
.font(.subheadline)
.foregroundColor(.gray)
} else {
Text("N/A", comment: "Text label indicating that there is no NIP-11 relay software information found. In English, N/A stands for not applicable.")
.font(.subheadline)
.foregroundColor(.gray)
}
}
Divider().frame(width: 1)
VStack {
Text("VERSION")
Text("VERSION", comment: "Text label indicating which version of the relay software is being run for this Nostr relay.")
.font(.caption)
.fontWeight(.heavy)
.foregroundColor(DamusColors.mediumGrey)
Image("branches")
.foregroundColor(.gray)
Text(nip11?.version ?? "N/A")
.font(.subheadline)
.foregroundColor(.gray)
if let version = nip11?.version, !version.isEmpty {
Text(version)
.font(.subheadline)
.foregroundColor(.gray)
} else {
Text("N/A", comment: "Text label indicating that there is no NIP-11 relay software version information found. In English, N/A stands for not applicable.")
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
}

View File

@ -112,7 +112,7 @@ struct RelayConfigView: View {
func RelayList(title: String, relayList: [RelayDescriptor], recommended: Bool) -> some View {
ScrollView(showsIndicators: false) {
HStack {
Text(NSLocalizedString(title, comment: "Section title for type of relay server list"))
Text(title)
.font(.system(size: 32, weight: .bold))
@ -123,7 +123,7 @@ struct RelayConfigView: View {
show_add_relay.toggle()
}) {
HStack {
Text(verbatim: "Add relay")
Text("Add relay", comment: "Button text to add a relay")
.padding(10)
}
}

View File

@ -144,13 +144,18 @@ struct RelayDetailView: View {
Divider()
Text("Description")
Text("Description", comment: "Description of the specific Nostr relay server.")
.font(.subheadline)
.foregroundColor(DamusColors.mediumGrey)
Text(nip11?.description ?? "N/A")
.font(.subheadline)
if let description = nip11?.description, !description.isEmpty {
Text(description)
.font(.subheadline)
} else {
Text("N/A", comment: "Text label indicating that there is no NIP-11 relay description information found. In English, N/A stands for not applicable.")
.font(.subheadline)
}
Divider()
RelayInfo
@ -175,7 +180,7 @@ struct RelayDetailView: View {
}
if state.settings.developer_mode {
Text("Relay Logs")
Text("Relay Logs", comment: "Text label indicating that the text below it are developer mode logs.")
.padding(.top)
Divider()
Text(log.contents ?? NSLocalizedString("No logs to display", comment: "Label to indicate that there are no developer mode logs available to be displayed on the screen"))

View File

@ -75,7 +75,7 @@ struct RelayView: View {
Button(action: {
remove_action(privkey: keypair.privkey)
}) {
Text(NSLocalizedString("Added", comment: "Button to show relay server is already added to list."))
Text("Added", comment: "Button to show relay server is already added to list.")
.font(.caption)
}
.buttonStyle(NeutralButtonShape.capsule.style)
@ -147,7 +147,7 @@ struct RelayView: View {
Button(action: {
add_action(keypair: keypair)
}) {
Text(NSLocalizedString("Add", comment: "Button to add relay server to list."))
Text("Add", comment: "Button to add relay server to list.")
.font(.caption)
}
.buttonStyle(NeutralButtonShape.capsule.style)
@ -166,7 +166,7 @@ struct RelayView: View {
remove_action(privkey: privkey)
}) {
if showText {
Text(NSLocalizedString("Disconnect", comment: "Button to disconnect from a relay server."))
Text("Disconnect", comment: "Button to disconnect from a relay server.")
}
Image("minus-circle")

View File

@ -21,6 +21,13 @@ struct SaveKeysView: View {
@FocusState var pubkey_focused: Bool
@FocusState var privkey_focused: Bool
let first_contact_event: NdbNote?
init(account: CreateAccountModel) {
self.account = account
self.first_contact_event = make_first_contact_event(keypair: account.keypair)
}
var body: some View {
ZStack(alignment: .top) {
VStack(alignment: .center) {
@ -102,6 +109,13 @@ struct SaveKeysView: View {
}
func complete_account_creation(_ account: CreateAccountModel) {
guard let first_contact_event else {
error = NSLocalizedString("Could not create your initial contact list event. This is a software bug, please contact Damus support via support@damus.io or through our Nostr account for help.", comment: "Error message to the user indicating that the initial contact list failed to be created.")
return
}
// Save contact list to storage right away so that we don't need to depend on the network to complete this important step
self.save_to_storage(first_contact_event: first_contact_event, for: account)
let bootstrap_relays = load_bootstrap_relays(pubkey: account.pubkey)
for relay in bootstrap_relays {
add_rw_relay(self.pool, relay)
@ -115,6 +129,15 @@ struct SaveKeysView: View {
self.pool.connect()
}
func save_to_storage(first_contact_event: NdbNote, for account: CreateAccountModel) {
// Send to NostrDB so that we have a local copy in storage
self.pool.send_raw_to_local_ndb(.typical(.event(first_contact_event)))
// Save the ID to user settings so that we can easily find it later.
let settings = UserSettingsStore.globally_load_for(pubkey: account.pubkey)
settings.latest_contact_event_id_hex = first_contact_event.id.hex()
}
func handle_event(relay: RelayURL, ev: NostrConnectionEvent) {
switch ev {
@ -122,15 +145,14 @@ struct SaveKeysView: View {
switch wsev {
case .connected:
let metadata = create_account_to_metadata(account)
let contacts_ev = make_first_contact_event(keypair: account.keypair)
if let keypair = account.keypair.to_full(),
let metadata_ev = make_metadata_event(keypair: keypair, metadata: metadata) {
self.pool.send(.event(metadata_ev))
}
if let contacts_ev {
self.pool.send(.event(contacts_ev))
if let first_contact_event {
self.pool.send(.event(first_contact_event))
}
do {

View File

@ -84,7 +84,7 @@ struct PullDownSearchView: View {
if results.count > 0 {
HStack {
Image("search")
Text(NSLocalizedString("Top hits", comment: "A label indicating that the notes being displayed below it are all top note search results"))
Text("Top hits", comment: "A label indicating that the notes being displayed below it are all top note search results")
Spacer()
}
.padding(.horizontal)
@ -101,7 +101,7 @@ struct PullDownSearchView: View {
HStack {
Image("notes.fill")
Text(NSLocalizedString("Notes", comment: "A label indicating that the notes being displayed below it are from a timeline, not search results"))
Text("Notes", comment: "A label indicating that the notes being displayed below it are from a timeline, not search results")
Spacer()
}
.foregroundColor(.secondary)
@ -109,7 +109,7 @@ struct PullDownSearchView: View {
} else if results.count == 0 && !search_text.isEmpty {
HStack {
Image("search")
Text(NSLocalizedString("No results", comment: "A label indicating that note search resulted in no results"))
Text("No results", comment: "A label indicating that note search resulted in no results")
Spacer()
}
.padding(.horizontal)

View File

@ -1,20 +0,0 @@
//
// SearchingProfileView.swift
// damus
//
// Created by William Casarin on 2023-03-05.
//
import SwiftUI
struct SearchingProfileView: View {
var body: some View {
Text(verbatim: /*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
struct SearchingProfileView_Previews: PreviewProvider {
static var previews: some View {
SearchingProfileView()
}
}

View File

@ -85,7 +85,7 @@ struct SearchHomeView: View {
HStack {
Image("notes.fill")
Text(NSLocalizedString("All recent notes", comment: "A label indicating that the notes being displayed below it are all recent notes"))
Text("All recent notes", comment: "A label indicating that the notes being displayed below it are all recent notes")
Spacer()
}
.foregroundColor(.secondary)

View File

@ -54,7 +54,7 @@ struct AppearanceSettingsView: View {
}
// MARK: - Text Truncation
Section(header: Text(NSLocalizedString("Text Truncation", comment: "Section header for damus text truncation user configuration"))) {
Section(header: Text("Text Truncation", comment: "Section header for damus text truncation user configuration")) {
Toggle(NSLocalizedString("Truncate timeline text", comment: "Setting to truncate text in timeline"), isOn: $settings.truncate_timeline_text)
.toggleStyle(.switch)
Toggle(NSLocalizedString("Truncate notification mention text", comment: "Setting to truncate text in mention notifications"), isOn: $settings.truncate_mention_text)
@ -70,7 +70,7 @@ struct AppearanceSettingsView: View {
}
// MARK: - Accessibility
Section(header: Text(NSLocalizedString("Accessibility", comment: "Section header for accessibility settings"))) {
Section(header: Text("Accessibility", comment: "Section header for accessibility settings")) {
Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $settings.left_handed)
.toggleStyle(.switch)
}
@ -97,8 +97,8 @@ struct AppearanceSettingsView: View {
// MARK: - Content filters and moderation
Section(
header: Text(NSLocalizedString("Content filters", comment: "Section title for content filtering/moderation configuration.")),
footer: Text(NSLocalizedString("Notes with the #nsfw tag usually contains adult content or other \"Not safe for work\" content", comment: "Section footer clarifying what #nsfw (not safe for work) tags mean"))
header: Text("Content filters", comment: "Section title for content filtering/moderation configuration."),
footer: Text("Notes with the #nsfw tag usually contains adult content or other \"Not safe for work\" content", comment: "Section footer clarifying what #nsfw (not safe for work) tags mean")
) {
Toggle(NSLocalizedString("Hide notes with #nsfw tags", comment: "Setting to hide notes with the #nsfw (not safe for work) tags"), isOn: $settings.hide_nsfw_tagged_content)
.toggleStyle(.switch)
@ -106,8 +106,8 @@ struct AppearanceSettingsView: View {
// MARK: - Profiles
Section(
header: Text(NSLocalizedString("Profiles", comment: "Section title for profile view configuration.")),
footer: Text(NSLocalizedString("Profile action sheets allow you to follow, zap, or DM profiles more quickly without having to view their full profile", comment: "Section footer clarifying what the profile action sheet feature does"))
header: Text("Profiles", comment: "Section title for profile view configuration."),
footer: Text("Profile action sheets allow you to follow, zap, or DM profiles more quickly without having to view their full profile", comment: "Section footer clarifying what the profile action sheet feature does")
) {
Toggle(NSLocalizedString("Show profile action sheets", comment: "Setting to show profile action sheets when clicking on a user's profile picture"), isOn: $settings.show_profile_action_sheet_on_pfp_click)
.toggleStyle(.switch)
@ -157,9 +157,9 @@ struct AppearanceSettingsView: View {
}
}
.alert(isPresented: $showing_enable_animation_alert) {
Alert(title: Text(NSLocalizedString("Confirmation", comment: "Confirmation dialog title")),
message: Text(NSLocalizedString("Changing this setting will cause the cache to be cleared. This will free space, but images may take longer to load again. Are you sure you want to proceed?", comment: "Message explaining consequences of changing the 'enable animation' setting")),
primaryButton: .default(Text(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) {
Alert(title: Text("Confirmation", comment: "Confirmation dialog title"),
message: Text("Changing this setting will cause the cache to be cleared. This will free space, but images may take longer to load again. Are you sure you want to proceed?", comment: "Message explaining consequences of changing the 'enable animation' setting"),
primaryButton: .default(Text("OK", comment: "Button label indicating user wants to proceed.")) {
self.clear_cache_button_action()
},
secondaryButton: .cancel() {
@ -176,22 +176,22 @@ struct AppearanceSettingsView: View {
HStack(spacing: 6) {
switch cache_clearing_state {
case .not_cleared:
Text(NSLocalizedString("Clear Cache", comment: "Button to clear image cache."))
Text("Clear Cache", comment: "Button to clear image cache.")
case .clearing:
ProgressView()
Text(NSLocalizedString("Clearing Cache", comment: "Loading message indicating that the cache is being cleared."))
Text("Clearing Cache", comment: "Loading message indicating that the cache is being cleared.")
case .cleared:
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text(NSLocalizedString("Cache has been cleared", comment: "Message indicating that the cache was successfully cleared."))
Text("Cache has been cleared", comment: "Message indicating that the cache was successfully cleared.")
}
}
})
.disabled(self.cache_clearing_state != .not_cleared)
.alert(isPresented: $showing_cache_clear_alert) {
Alert(title: Text(NSLocalizedString("Confirmation", comment: "Confirmation dialog title")),
message: Text(NSLocalizedString("Are you sure you want to clear the cache? This will free space, but images may take longer to load again.", comment: "Message explaining what it means to clear the cache, asking if user wants to proceed.")),
primaryButton: .default(Text(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) {
Alert(title: Text("Confirmation", comment: "Confirmation dialog title"),
message: Text("Are you sure you want to clear the cache? This will free space, but images may take longer to load again.", comment: "Message explaining what it means to clear the cache, asking if user wants to proceed."),
primaryButton: .default(Text("OK", comment: "Button label indicating user wants to proceed.")) {
self.clear_cache_button_action()
},
secondaryButton: .cancel())

View File

@ -13,7 +13,7 @@ struct DeveloperSettingsView: View {
var body: some View {
Form {
Section(footer: Text(NSLocalizedString("Developer Mode enables features and options that may help developers diagnose issues and improve this app. Most users will not need Developer Mode.", comment: "Section header for Developer Settings view"))) {
Section(footer: Text("Developer Mode enables features and options that may help developers diagnose issues and improve this app. Most users will not need Developer Mode.", comment: "Section header for Developer Settings view")) {
Toggle(NSLocalizedString("Developer Mode", comment: "Setting to enable developer mode"), isOn: $settings.developer_mode)
.toggleStyle(.switch)
if settings.developer_mode {

View File

@ -0,0 +1,84 @@
//
// FirstAidSettingsView.swift
// damus
//
// Created by Daniel DAquino on 2024-04-19.
//
import SwiftUI
struct FirstAidSettingsView: View {
let damus_state: DamusState
@ObservedObject var settings: UserSettingsStore
@State var reset_contact_list_state: ContactListResetState = .not_started
enum ContactListResetState: Equatable {
case not_started
case confirming_with_user
case error(String)
case in_progress
case completed
}
var body: some View {
Form {
if damus_state.contacts.event == nil {
Section(
header: Text(NSLocalizedString("Contact list (Follows + Relay list)", comment: "Section title for Contact list first aid tools")),
footer: Text(NSLocalizedString("No contact list was found. You might experience issues using the app. If you suspect you have permanently lost your contact list (or if you never had one), you can fix this by resetting it", comment: "Section footer for Contact list first aid tools"))
) {
Button(action: {
reset_contact_list_state = .confirming_with_user
}, label: {
HStack(spacing: 6) {
switch reset_contact_list_state {
case .not_started, .error:
Label(NSLocalizedString("Reset contact list", comment: "Button to reset contact list."), image: "broom")
.frame(maxWidth: .infinity, alignment: .leading)
.foregroundColor(.red)
case .confirming_with_user, .in_progress:
ProgressView()
Text(NSLocalizedString("In progress…", comment: "Loading message indicating that a contact list reset operation is in progress."))
case .completed:
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text(NSLocalizedString("Contact list has been reset", comment: "Message indicating that the contact list was successfully reset."))
}
}
})
.disabled(reset_contact_list_state == .in_progress || reset_contact_list_state == .completed)
if case let .error(error_message) = reset_contact_list_state {
Text(error_message)
.foregroundStyle(.red)
}
}
.alert(NSLocalizedString("WARNING:\n\nThis will reset your contact list, including the list of everyone you follow and the list of all relays you usually connect to. ONLY PROCEED IF YOU ARE SURE YOU HAVE LOST YOUR CONTACT LIST BEYOND RECOVERABILITY.", comment: "Alert for resetting the user's contact list."),
isPresented: Binding(get: { reset_contact_list_state == .confirming_with_user }, set: { _ in return })
) {
Button(NSLocalizedString("Cancel", comment: "Cancel resetting the contact list."), role: .cancel) {
reset_contact_list_state = .not_started
}
Button(NSLocalizedString("Continue", comment: "Continue with resetting the contact list.")) {
guard let new_contact_list_event = make_first_contact_event(keypair: damus_state.keypair) else {
reset_contact_list_state = .error(NSLocalizedString("An unexpected error happened while trying to create the new contact list. Please contact support.", comment: "Error message for a failed contact list reset operation"))
return
}
damus_state.pool.send(.event(new_contact_list_event))
reset_contact_list_state = .completed
}
}
}
if damus_state.contacts.event != nil {
Text(NSLocalizedString("We did not detect any issues that we can automatically fix for you. If you are having issues, please contact Damus support", comment: "Message indicating that no First Aid actions are available."))
}
}
.navigationTitle(NSLocalizedString("First Aid", comment: "Navigation title for first aid settings and tools"))
}
}
#Preview {
FirstAidSettingsView(damus_state: test_damus_state, settings: test_damus_state.settings)
}

View File

@ -26,7 +26,7 @@ struct NotificationSettingsView: View {
var body: some View {
Form {
Section(header: Text(NSLocalizedString("Local Notifications", comment: "Section header for damus local notifications user configuration"))) {
Section(header: Text("Local Notifications", comment: "Section header for damus local notifications user configuration")) {
Toggle(NSLocalizedString("Zaps", comment: "Setting to enable Zap Local Notification"), isOn: $settings.zap_notification)
.toggleStyle(.switch)
Toggle(NSLocalizedString("Mentions", comment: "Setting to enable Mention Local Notification"), isOn: $settings.mention_notification)
@ -39,12 +39,12 @@ struct NotificationSettingsView: View {
.toggleStyle(.switch)
}
Section(header: Text(NSLocalizedString("Notification Preference", comment: "Section header for Notification Preferences"))) {
Section(header: Text("Notification Preference", comment: "Section header for Notification Preferences")) {
Toggle(NSLocalizedString("Show only from users you follow", comment: "Setting to Show notifications only associated to users your follow"), isOn: $settings.notification_only_from_following)
.toggleStyle(.switch)
}
Section(header: Text(NSLocalizedString("Notification Dots", comment: "Section header for notification indicator dot settings"))) {
Section(header: Text("Notification Dots", comment: "Section header for notification indicator dot settings")) {
Toggle(NSLocalizedString("Zaps", comment: "Setting to enable Zap Local Notification"), isOn: indicator_binding(.zaps))
.toggleStyle(.switch)
Toggle(NSLocalizedString("Mentions", comment: "Setting to enable Mention Local Notification"), isOn: indicator_binding(.mentions))

View File

@ -26,7 +26,7 @@ struct ReactionsSettingsView: View {
isReactionsVisible = true
}
} header: {
Text(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"))
Text("Select default emoji", comment: "Prompt selection of user's default emoji reaction")
}
}
.navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))

View File

@ -13,7 +13,7 @@ struct SearchSettingsView: View {
var body: some View {
Form {
Section(header: Text(NSLocalizedString("Spam", comment: "Section header for Universe/Search spam"))) {
Section(header: Text("Spam", comment: "Section header for Universe/Search spam")) {
Toggle(NSLocalizedString("View multiple events per user", comment: "Setting to only see 1 event per user (npub) in the search/universe"), isOn: $settings.multiple_events_per_pubkey)
.toggleStyle(.switch)
}

View File

@ -28,7 +28,7 @@ struct TranslationSettingsView: View {
if settings.translation_service == .purple && damus_state.purple.enable_purple {
NavigationLink(destination: DamusPurpleView(damus_state: damus_state)) {
Text(NSLocalizedString("Configure Damus Purple", comment: "Button to allow Damus Purple to be configured"))
Text("Configure Damus Purple", comment: "Button to allow Damus Purple to be configured")
}
}

View File

@ -22,8 +22,8 @@ struct ZapSettingsView: View {
var body: some View {
Form {
Section(
header: Text(NSLocalizedString("OnlyZaps", comment: "Section header for enabling OnlyZaps mode (hide reactions)")),
footer: Text(NSLocalizedString("Hide all 🤙's", comment: "Section footer describing OnlyZaps mode"))
header: Text("OnlyZaps", comment: "Section header for enabling OnlyZaps mode (hide reactions)"),
footer: Text("Hide all 🤙's", comment: "Section footer describing OnlyZaps mode")
) {
Toggle(NSLocalizedString("OnlyZaps mode", comment: "Setting toggle to hide reactions."), isOn: $settings.onlyzaps_mode)

View File

@ -57,7 +57,7 @@ struct SuggestedHashtagsView: View {
VStack {
HStack {
Image(systemName: "sparkles")
Text(NSLocalizedString("Suggested hashtags", comment: "A label indicating that the items below it are suggested hashtags"))
Text("Suggested hashtags", comment: "A label indicating that the items below it are suggested hashtags")
Spacer()
Button(action: {
withAnimation(.easeOut(duration: 0.2)) {
@ -105,7 +105,8 @@ struct SuggestedHashtagsView: View {
Text(verbatim: "#\(hashtag)")
.bold()
Text(pluralizedString(key: "users_talking_about_it", count: self.count))
let pluralizedString = pluralizedString(key: "users_talking_about_it", count: self.count)
Text(pluralizedString)
.foregroundStyle(.secondary)
}

View File

@ -39,9 +39,9 @@ struct ConnectWalletView: View {
}
.alert(isPresented: $showAlert) {
Alert(
title: Text(NSLocalizedString("Invalid Nostr wallet connection string", comment: "Error message when an invalid Nostr wallet connection string is provided.")),
message: Text("Make sure the wallet you are connecting to supports NWC."),
dismissButton: .default(Text(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) {
title: Text("Invalid Nostr wallet connection string", comment: "Error message when an invalid Nostr wallet connection string is provided."),
message: Text("Make sure the wallet you are connecting to supports NWC.", comment: "Hint message when an invalid Nostr wallet connection string is provided."),
dismissButton: .default(Text("OK", comment: "Button label indicating user wants to proceed.")) {
wallet_scan_result = .scanning
}
)
@ -80,7 +80,7 @@ struct ConnectWalletView: View {
model.cancel()
}) {
HStack {
Text(NSLocalizedString("Cancel", comment: "Text for button to cancel out of connecting Nostr Wallet Connect lightning wallet."))
Text("Cancel", comment: "Text for button to cancel out of connecting Nostr Wallet Connect lightning wallet.")
.padding()
}
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
@ -175,10 +175,10 @@ struct ConnectWalletView: View {
var TitleSection: some View {
VStack(spacing: 25) {
Text("Damus Wallet")
Text("Damus Wallet", comment: "Title text for Damus Wallet view.")
.fontWeight(.bold)
Text("Securely connect your Damus app to your wallet\nusing Nostr Wallet Connect")
Text("Securely connect your Damus app to your wallet using Nostr\u{00A0}Wallet\u{00A0}Connect", comment: "Text to prompt user to connect their wallet using 'Nostr Wallet Connect'.")
.font(.caption)
.multilineTextAlignment(.center)
}

View File

@ -28,7 +28,7 @@ struct WalletView: View {
VStack(spacing: 5) {
VStack(spacing: 10) {
Text("Wallet Relay")
Text("Wallet Relay", comment: "Label text indicating that below it is the information about the wallet relay.")
.fontWeight(.semibold)
.padding(.top)
@ -47,12 +47,12 @@ struct WalletView: View {
if let lud16 = nwc.lud16 {
VStack(spacing: 10) {
Text("Wallet Address")
Text("Wallet Address", comment: "Label text indicating that below it is the wallet address.")
.fontWeight(.semibold)
Divider()
Text(verbatim: lud16)
Text(lud16)
}
.frame(maxWidth: .infinity, minHeight: 75, alignment: .center)
.padding(.horizontal, 10)
@ -69,7 +69,7 @@ struct WalletView: View {
self.model.disconnect()
}) {
HStack {
Text(NSLocalizedString("Disconnect Wallet", comment: "Text for button to disconnect from Nostr Wallet Connect lightning wallet."))
Text("Disconnect Wallet", comment: "Text for button to disconnect from Nostr Wallet Connect lightning wallet.")
}
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 18, alignment: .center)
}

View File

@ -173,7 +173,7 @@ struct CustomizeZapView: View {
model.zapping = true
}) {
HStack {
Text(NSLocalizedString("Zap User", comment: "Button to send a zap."))
Text("Zap User", comment: "Button to send a zap.")
.font(.system(size: 20, weight: .bold))
}
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)

Binary file not shown.

View File

@ -226,6 +226,22 @@
<string>geteilte Beiträge</string>
</dict>
</dict>
<key>quoted_reposts_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@QUOTE_REPOSTS@</string>
<key>QUOTE_REPOSTS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Zitate</string>
<key>other</key>
<string>Zitat</string>
</dict>
</dict>
<key>sats</key>
<dict>
<key>NSStringLocalizedFormatKey</key>

View File

@ -2,7 +2,7 @@
<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" source-language="en-US" target-language="en-US" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.2" build-num="15C500b"/>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.3" build-num="15E204a"/>
</header>
<body>
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
@ -44,15 +44,25 @@
</file>
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.2" build-num="15C500b"/>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.3" build-num="15E204a"/>
</header>
<body>
<trans-unit id="%@ %@" xml:space="preserve">
<source>%@ %@</source>
<target>%@ %@</target>
<note>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>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 imports were performed from loading a NostrScript. In source English, the first variable is the number of imports, and the second variable is 'Import' or 'Imports'.
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 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="%@ / %@" xml:space="preserve">
<source>%@ / %@</source>
<target>%@ / %@</target>
<note>Amount of money required to subscribe to the Nostr relay. In English, this would look something like '4,000 sats / 30 days', meaning it costs 4000 sats to subscribe to the Nostr relay for 30 days.</note>
</trans-unit>
<trans-unit id="%@ / event" xml:space="preserve">
<source>%@ / event</source>
<target>%@ / event</target>
<note>Amount of money required to publish to the Nostr relay. In English, this would look something like '10 sats / event', meaning it costs 10 sats to publish one event.</note>
</trans-unit>
<trans-unit id="%@ has been muted" xml:space="preserve">
<source>%@ has been muted</source>
@ -104,6 +114,26 @@ Sentence composed of 2 variables to describe how many reposts. In source English
<target>(Contents are encrypted)</target>
<note>Label on push notification indicating that the contents of the message are encrypted</note>
</trans-unit>
<trans-unit id="1 month" xml:space="preserve">
<source>1 month</source>
<target>1 month</target>
<note>A duration of 1 month to be shown to the user. Most likely in the context of how long they want to mute a piece of content for.</note>
</trans-unit>
<trans-unit id="1 week" xml:space="preserve">
<source>1 week</source>
<target>1 week</target>
<note>A duration of 1 week to be shown to the user. Most likely in the context of how long they want to mute a piece of content for.</note>
</trans-unit>
<trans-unit id="24 hours" xml:space="preserve">
<source>24 hours</source>
<target>24 hours</target>
<note>A duration of 24 hours/1 day to be shown to the user. Most likely in the context of how long they want to mute a piece of content for.</note>
</trans-unit>
<trans-unit id="ADMIN" xml:space="preserve">
<source>ADMIN</source>
<target>ADMIN</target>
<note>Text label indicating the profile picture underneath it is the admin of the Nostr relay.</note>
</trans-unit>
<trans-unit id="API Key (optional)" xml:space="preserve">
<source>API Key (optional)</source>
<target>API Key (optional)</target>
@ -152,19 +182,13 @@ Sentence composed of 2 variables to describe how many reposts. In source English
<trans-unit id="Add" xml:space="preserve">
<source>Add</source>
<target>Add</target>
<note>Button to add relay server to list.
Button to confirm adding user inputted emoji.</note>
<note>Button to add relay server to list.</note>
</trans-unit>
<trans-unit id="Add Bookmark" xml:space="preserve">
<source>Add Bookmark</source>
<target>Add Bookmark</target>
<note>Button text to add bookmark to a note.</note>
</trans-unit>
<trans-unit id="Add Emoji" xml:space="preserve">
<source>Add Emoji</source>
<target>Add Emoji</target>
<note>Label for section for adding an emoji to the reactions list.</note>
</trans-unit>
<trans-unit id="Add all" xml:space="preserve">
<source>Add all</source>
<target>Add all</target>
@ -180,26 +204,32 @@ Sentence composed of 2 variables to describe how many reposts. In source English
<target>Add bookmark</target>
<note>Context menu option for adding a note bookmark.</note>
</trans-unit>
<trans-unit id="Add mute item" xml:space="preserve">
<source>Add mute item</source>
<target>Add mute item</target>
<note>Title text to indicate user to an add an item to their mutelist.</note>
</trans-unit>
<trans-unit id="Add relay" xml:space="preserve">
<source>Add relay</source>
<target>Add relay</target>
<note>Title text to indicate user to an add a relay.</note>
<note>Title text to indicate user to an add a relay.
Button text to add a relay</note>
</trans-unit>
<trans-unit id="Add your first post" xml:space="preserve">
<source>Add your first post</source>
<target>Add your first post</target>
<note>Prompt given to the user during onboarding, suggesting them to write their first post</note>
</trans-unit>
<trans-unit id="Added" xml:space="preserve">
<source>Added</source>
<target>Added</target>
<note>Button to show relay server is already added to list.</note>
</trans-unit>
<trans-unit id="Additional information" xml:space="preserve">
<source>Additional information</source>
<target>Additional information</target>
<note>Header text to prompt user to optionally provide additional information when reporting a user or note.</note>
</trans-unit>
<trans-unit id="Admin" xml:space="preserve">
<source>Admin</source>
<target>Admin</target>
<note>Label to display relay contact user.</note>
</trans-unit>
<trans-unit id="All" xml:space="preserve">
<source>All</source>
<target>All</target>
@ -262,16 +292,16 @@ Sentence composed of 2 variables to describe how many reposts. In source English
<target>Are you lost?</target>
<note>Text asking the user if they are lost in the app.</note>
</trans-unit>
<trans-unit id="Are you sure you want to attach this wallet?" xml:space="preserve">
<source>Are you sure you want to attach this wallet?</source>
<target>Are you sure you want to attach this wallet?</target>
<note>Prompt to ask user if they want to attach their Nostr Wallet Connect lightning wallet.</note>
</trans-unit>
<trans-unit id="Are you sure you want to clear the cache? This will free space, but images may take longer to load again." xml:space="preserve">
<source>Are you sure you want to clear the cache? This will free space, but images may take longer to load again.</source>
<target>Are you sure you want to clear the cache? This will free space, but images may take longer to load again.</target>
<note>Message explaining what it means to clear the cache, asking if user wants to proceed.</note>
</trans-unit>
<trans-unit id="Are you sure you want to connect this wallet?" xml:space="preserve">
<source>Are you sure you want to connect this wallet?</source>
<target>Are you sure you want to connect this wallet?</target>
<note>Prompt to ask user if they want to attach their Nostr Wallet Connect lightning wallet.</note>
</trans-unit>
<trans-unit id="Are you sure you want to delete all of your bookmarks?" xml:space="preserve">
<source>Are you sure you want to delete all of your bookmarks?</source>
<target>Are you sure you want to delete all of your bookmarks?</target>
@ -296,36 +326,11 @@ Tip: You can always change this later in Settings → Translations</source>
Tip: You can always change this later in Settings → Translations</target>
<note>Message notifying the user that they get auto-translations as part of their service</note>
</trans-unit>
<trans-unit id="Attach" xml:space="preserve">
<source>Attach</source>
<target>Attach</target>
<note>Text for button to attach Nostr Wallet Connect lightning wallet.</note>
</trans-unit>
<trans-unit id="Attach Alby Wallet" xml:space="preserve">
<source>Attach Alby Wallet</source>
<target>Attach Alby Wallet</target>
<note>Button to attach an Alby Wallet, a service that provides a Lightning wallet for zapping sats. Alby is the name of the service and should not be translated.</note>
</trans-unit>
<trans-unit id="Attach Wallet" xml:space="preserve">
<source>Attach Wallet</source>
<target>Attach Wallet</target>
<note>Text for button to attach Nostr Wallet Connect lightning wallet.</note>
</trans-unit>
<trans-unit id="Attach a Wallet" xml:space="preserve">
<source>Attach a Wallet</source>
<target>Attach a Wallet</target>
<note>Navigation title for attaching Nostr Wallet Connect lightning wallet.</note>
</trans-unit>
<trans-unit id="Authenticated" xml:space="preserve">
<source>Authenticated</source>
<target>Authenticated</target>
<note>Label to display that authentication to a server has succeeded.</note>
</trans-unit>
<trans-unit id="Authentication" xml:space="preserve">
<source>Authentication</source>
<target>Authentication</target>
<note>Header label to display authentication details for a given relay.</note>
</trans-unit>
<trans-unit id="Automatic translations" xml:space="preserve">
<source>Automatic translations</source>
<target>Automatic translations</target>
@ -383,6 +388,16 @@ Tip: You can always change this later in Settings → Translations</target>
<target>By signing up, you agree to our </target>
<note>Ask the user if they already have an account on Nostr</note>
</trans-unit>
<trans-unit id="By subscribing to Damus Purple, you are accepting our [privacy policy](https://damus.io/privacy-policy.txt) and Apple's Standard [EULA](https://www.apple.com/legal/internet-services/itunes/dev/stdeula/)" xml:space="preserve">
<source>By subscribing to Damus Purple, you are accepting our [privacy policy](https://damus.io/privacy-policy.txt) and Apple's Standard [EULA](https://www.apple.com/legal/internet-services/itunes/dev/stdeula/)</source>
<target>By subscribing to Damus Purple, you are accepting our [privacy policy](https://damus.io/privacy-policy.txt) and Apple's Standard [EULA](https://www.apple.com/legal/internet-services/itunes/dev/stdeula/)</target>
<note>Text explaining the terms and conditions of subscribing to Damus Purple. EULA stands for End User License Agreement.</note>
</trans-unit>
<trans-unit id="CONTACT" xml:space="preserve">
<source>CONTACT</source>
<target>CONTACT</target>
<note>Text label indicating that the information below is the contact information of the admin of the Nostr relay.</note>
</trans-unit>
<trans-unit id="Cache has been cleared" xml:space="preserve">
<source>Cache has been cleared</source>
<target>Cache has been cleared</target>
@ -395,13 +410,10 @@ Tip: You can always change this later in Settings → Translations</target>
Button to cancel a repost.
Button to cancel any interaction with the QRCode link.
Button to cancel out of alert that creates a new mutelist.
Button to cancel out of posting a note.
Button to cancel out of view adding user inputted emoji.
Button to cancel the upload.
Cancel deleting bookmarks.
Cancel deleting the user.
Cancel out of logging out the user.
Text for button to cancel out of connecting Nostr Wallet Connect lightning ewallet.</note>
Cancel out of logging out the user.</note>
</trans-unit>
<trans-unit id="Changing this setting will cause the cache to be cleared. This will free space, but images may take longer to load again. Are you sure you want to proceed?" xml:space="preserve">
<source>Changing this setting will cause the cache to be cleared. This will free space, but images may take longer to load again. Are you sure you want to proceed?</source>
@ -453,21 +465,27 @@ Tip: You can always change this later in Settings → Translations</target>
<target>Confirmation</target>
<note>Confirmation dialog title</note>
</trans-unit>
<trans-unit id="Connect To Relay" xml:space="preserve">
<source>Connect To Relay</source>
<target>Connect To Relay</target>
<note>Button to connect to the relay.</note>
<trans-unit id="Connect" xml:space="preserve">
<source>Connect</source>
<target>Connect</target>
<note>Text for button to conect to Nostr Wallet Connect lightning wallet.
Button to connect to the relay.</note>
</trans-unit>
<trans-unit id="Connect to Alby Wallet" xml:space="preserve">
<source>Connect to Alby Wallet</source>
<target>Connect to Alby Wallet</target>
<note>Button to attach an Alby Wallet, a service that provides a Lightning wallet for zapping sats. Alby is the name of the service and should not be translated.</note>
</trans-unit>
<trans-unit id="Connect to Mutiny Wallet" xml:space="preserve">
<source>Connect to Mutiny Wallet</source>
<target>Connect to Mutiny Wallet</target>
<note>Button to attach an Mutiny Wallet, a service that provides a Lightning wallet for zapping sats. Mutiny is the name of the service and should not be translated.</note>
</trans-unit>
<trans-unit id="Connecting" xml:space="preserve">
<source>Connecting</source>
<target>Connecting</target>
<note>Relay status label that indicates a relay is connecting.</note>
</trans-unit>
<trans-unit id="Contact" xml:space="preserve">
<source>Contact</source>
<target>Contact</target>
<note>Label to display relay contact information.</note>
</trans-unit>
<trans-unit id="Content filters" xml:space="preserve">
<source>Content filters</source>
<target>Content filters</target>
@ -476,10 +494,8 @@ Tip: You can always change this later in Settings → Translations</target>
<trans-unit id="Continue" xml:space="preserve">
<source>Continue</source>
<target>Continue</target>
<note>Button to dismiss suggested users view and continue to the main app
Continue with bookmarks.
Continue with deleting the user.
Prompt to user to continue</note>
<note>Continue with bookmarks.
Continue with deleting the user.</note>
</trans-unit>
<trans-unit id="Copied" xml:space="preserve">
<source>Copied</source>
@ -490,7 +506,6 @@ Tip: You can always change this later in Settings → Translations</target>
<source>Copy</source>
<target>Copy</target>
<note>Button to copy a relay server address.
Button to copy an emoji reaction
Button to copy the value found.
Context menu option for copying the version of damus.</note>
</trans-unit>
@ -597,6 +612,11 @@ Tip: You can always change this later in Settings → Translations</target>
Setting to enable DM Local Notification
Toolbar label for DMs view, where DM is the English abbreviation for Direct Message.</note>
</trans-unit>
<trans-unit id="Damus" xml:space="preserve">
<source>Damus</source>
<target>Damus</target>
<note>Name of the app for the title of an internal notification</note>
</trans-unit>
<trans-unit id="Damus Purple" xml:space="preserve">
<source>Damus Purple</source>
<target>Damus Purple</target>
@ -607,6 +627,11 @@ Tip: You can always change this later in Settings → Translations</target>
<target>Damus Purple environment</target>
<note>Prompt selection of the Damus purple environment (Developer feature to switch between real/production mode to test modes).</note>
</trans-unit>
<trans-unit id="Damus Wallet" xml:space="preserve">
<source>Damus Wallet</source>
<target>Damus Wallet</target>
<note>Title text for Damus Wallet view.</note>
</trans-unit>
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
<source>DeepL (Proprietary, Higher Accuracy)</source>
<target>DeepL (Proprietary, Higher Accuracy)</target>
@ -636,7 +661,7 @@ Tip: You can always change this later in Settings → Translations</target>
<trans-unit id="Description" xml:space="preserve">
<source>Description</source>
<target>Description</target>
<note>Label to display relay description.</note>
<note>Description of the specific Nostr relay server.</note>
</trans-unit>
<trans-unit id="Developer" xml:space="preserve">
<source>Developer</source>
@ -657,12 +682,8 @@ Tip: You can always change this later in Settings → Translations</target>
<trans-unit id="Disconnect" xml:space="preserve">
<source>Disconnect</source>
<target>Disconnect</target>
<note>Button to disconnect from a relay server.</note>
</trans-unit>
<trans-unit id="Disconnect From Relay" xml:space="preserve">
<source>Disconnect From Relay</source>
<target>Disconnect From Relay</target>
<note>Button to disconnect from the relay.</note>
<note>Button to disconnect from the relay.
Button to disconnect from a relay server.</note>
</trans-unit>
<trans-unit id="Disconnect Wallet" xml:space="preserve">
<source>Disconnect Wallet</source>
@ -709,11 +730,6 @@ Tip: You can always change this later in Settings → Translations</target>
<target>Edit</target>
<note>Button to edit user's profile.</note>
</trans-unit>
<trans-unit id="Emoji Reactions" xml:space="preserve">
<source>Emoji Reactions</source>
<target>Emoji Reactions</target>
<note>Section title for emoji reactions that are currently added.</note>
</trans-unit>
<trans-unit id="Enable Purple auto-translations" xml:space="preserve">
<source>Enable Purple auto-translations</source>
<target>Enable Purple auto-translations</target>
@ -747,13 +763,19 @@ Tip: You can always change this later in Settings → Translations</target>
<trans-unit id="Error" xml:space="preserve">
<source>Error</source>
<target>Error</target>
<note>Label to display that authentication to a server has failed.</note>
<note>Label to display that authentication to a server has failed.
Relay status label that indicates a relay had an error when connecting</note>
</trans-unit>
<trans-unit id="Error fetching lightning invoice" xml:space="preserve">
<source>Error fetching lightning invoice</source>
<target>Error fetching lightning invoice</target>
<note>Message to display when there was an error fetching a lightning invoice while attempting to zap.</note>
</trans-unit>
<trans-unit id="Error retrieving muted event" xml:space="preserve">
<source>Error retrieving muted event</source>
<target>Error retrieving muted event</target>
<note>Text for an item that application failed to retrieve the muted event for.</note>
</trans-unit>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Error: %@</target>
@ -904,7 +926,7 @@ My side interests include languages and I am striving to be a #polyglot - I am a
<trans-unit id="Hashtags" xml:space="preserve">
<source>Hashtags</source>
<target>Hashtags</target>
<note>Label for filter for seeing only hashtag follows.</note>
<note>Section header title for a list of hashtags that are muted.</note>
</trans-unit>
<trans-unit id="Hello everybody!&#10;&#10;This is my first post on Damus, I am happy to meet you all 🤙. Whats up?&#10;&#10;#introductions" xml:space="preserve">
<source>Hello everybody!
@ -937,7 +959,7 @@ This is my first post on Damus, I am happy to meet you all 🤙. Whats up?
<trans-unit id="Hide" xml:space="preserve">
<source>Hide</source>
<target>Hide</target>
<note>Button to hide a note from a user who has been muted.</note>
<note>Button to hide a note which has been muted.</note>
</trans-unit>
<trans-unit id="Hide all 🤙's" xml:space="preserve">
<source>Hide all 🤙's</source>
@ -983,6 +1005,16 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<target>Impersonation</target>
<note>Description of report type for impersonation.</note>
</trans-unit>
<trans-unit id="Indefinite" xml:space="preserve">
<source>Indefinite</source>
<target>Indefinite</target>
<note>Mute a given item indefinitly (until user unmutes it). As opposed to muting the item for a given period of time.</note>
</trans-unit>
<trans-unit id="Internal app notification" xml:space="preserve">
<source>Internal app notification</source>
<target>Internal app notification</target>
<note>Badge indicating that a notification is an official internal app notification</note>
</trans-unit>
<trans-unit id="Invalid Nostr wallet connection string" xml:space="preserve">
<source>Invalid Nostr wallet connection string</source>
<target>Invalid Nostr wallet connection string</target>
@ -1089,11 +1121,6 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<target>Local default</target>
<note>Dropdown option label for system default for Lightning wallet.</note>
</trans-unit>
<trans-unit id="Log" xml:space="preserve">
<source>Log</source>
<target>Log</target>
<note>Label to display developer mode logs.</note>
</trans-unit>
<trans-unit id="Login" xml:space="preserve">
<source>Login</source>
<target>Login</target>
@ -1115,6 +1142,11 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<target>Make Default</target>
<note>Button label to indicate that tapping it will make the selected zap type be the default for future zaps.</note>
</trans-unit>
<trans-unit id="Make sure the wallet you are connecting to supports NWC." xml:space="preserve">
<source>Make sure the wallet you are connecting to supports NWC.</source>
<target>Make sure the wallet you are connecting to supports NWC.</target>
<note>Hint message when an invalid Nostr wallet connection string is provided.</note>
</trans-unit>
<trans-unit id="Make sure your nsec account key is saved before you logout or you will lose access to this account" xml:space="preserve">
<source>Make sure your nsec account key is saved before you logout or you will lose access to this account</source>
<target>Make sure your nsec account key is saved before you logout or you will lose access to this account</target>
@ -1125,6 +1157,11 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<target>Manage</target>
<note>Manage the damus subscription</note>
</trans-unit>
<trans-unit id="Manage subscription" xml:space="preserve">
<source>Manage subscription</source>
<target>Manage subscription</target>
<note>Button to take user to manage Damus Purple subscription</note>
</trans-unit>
<trans-unit id="Media previews" xml:space="preserve">
<source>Media previews</source>
<target>Media previews</target>
@ -1158,14 +1195,18 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Mute</target>
<note>Alert button to mute a user.
Button to mute a profile.</note>
<note>Alert button to mute a user.</note>
</trans-unit>
<trans-unit id="Mute %@?" xml:space="preserve">
<source>Mute %@?</source>
<target>Mute %@?</target>
<note>Alert message prompt to ask if a user should be muted.</note>
</trans-unit>
<trans-unit id="Mute Hashtag" xml:space="preserve">
<source>Mute Hashtag</source>
<target>Mute Hashtag</target>
<note>Label represnting a button that the user can tap to mute a given hashtag so they don't see it in their feed anymore.</note>
</trans-unit>
<trans-unit id="Mute User" xml:space="preserve">
<source>Mute User</source>
<target>Mute User</target>
@ -1184,17 +1225,15 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<trans-unit id="Muted" xml:space="preserve">
<source>Muted</source>
<target>Muted</target>
<note>Sidebar menu label for muted users view.</note>
<note>Navigation title of view to see list of muted users &amp; phrases.
Sidebar menu label for muted users view.</note>
</trans-unit>
<trans-unit id="Muted Users" xml:space="preserve">
<source>Muted Users</source>
<target>Muted Users</target>
<note>Navigation title of view to see list of muted users.</note>
</trans-unit>
<trans-unit id="My Relays" xml:space="preserve">
<source>My Relays</source>
<target>My Relays</target>
<note>Section title for relay servers that the user is connected to.</note>
<trans-unit id="N/A" xml:space="preserve">
<source>N/A</source>
<target>N/A</target>
<note>Text label indicating that there is no NIP-11 relay admin contact information found. In English, N/A stands for not applicable.
Text label indicating that there is no NIP-11 relay description information found. In English, N/A stands for not applicable.
Text label indicating that there is no NIP-11 relay software information found. In English, N/A stands for not applicable.</note>
</trans-unit>
<trans-unit id="Never" xml:space="preserve">
<source>Never</source>
@ -1226,11 +1265,6 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<target>No</target>
<note>User confirm No</note>
</trans-unit>
<trans-unit id="No data available" xml:space="preserve">
<source>No data available</source>
<target>No data available</target>
<note>Text indicating that there is no data available to show for specific metadata about a relay server.</note>
</trans-unit>
<trans-unit id="No logs to display" xml:space="preserve">
<source>No logs to display</source>
<target>No logs to display</target>
@ -1291,21 +1325,28 @@ Hope to meet folks who are on their own journeys to a peaceful and free life!</t
<target>NostrScript Error</target>
<note>Text indicating that there was an error with loading NostrScript. There is a more descriptive error message shown separately underneath.</note>
</trans-unit>
<trans-unit id="Note from a user you've muted" xml:space="preserve">
<source>Note from a user you've muted</source>
<target>Note from a user you've muted</target>
<note>Text to indicate that what is being shown is a note from a user who has been muted.</note>
<trans-unit id="Note from a %@ you've muted" xml:space="preserve">
<source>Note from a %@ you've muted</source>
<target>Note from a %@ you've muted</target>
<note>Text to indicate that what is being shown is a note which has been muted.</note>
</trans-unit>
<trans-unit id="Note you've muted" xml:space="preserve">
<source>Note you've muted</source>
<target>Note you've muted</target>
<note>Text to indicate that what is being shown is a note which has been muted.</note>
</trans-unit>
<trans-unit id="Notes" xml:space="preserve">
<source>Notes</source>
<target>Notes</target>
<note>A label indicating that the notes being displayed below it are from a timeline, not search results</note>
<note>Label for filter for seeing only notes (instead of notes and replies).
Label for filter for seeing only your notes (instead of notes and replies).
A label indicating that the notes being displayed below it are from a timeline, not search results</note>
</trans-unit>
<trans-unit id="Notes &amp; Replies" xml:space="preserve">
<source>Notes &amp; Replies</source>
<target>Notes &amp; Replies</target>
<note>Label for filter for seeing your notes and replies (instead of only your notes).
Label for filter for seeing notes and replies (instead of only notes).</note>
<note>Label for filter for seeing notes and replies (instead of only notes).
Label for filter for seeing your notes and replies (instead of only your notes).</note>
</trans-unit>
<trans-unit id="Notes with the #nsfw tag usually contains adult content or other &quot;Not safe for work&quot; content" xml:space="preserve">
<source>Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content</source>
@ -1342,7 +1383,7 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<source>OK</source>
<target>OK</target>
<note>Button label indicating user wants to proceed.
Button label to dismiss an error dialog</note>
Button label to dismiss an error dialog</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
@ -1392,12 +1433,12 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<trans-unit id="Paid Relay" xml:space="preserve">
<source>Paid Relay</source>
<target>Paid Relay</target>
<note>Section header that indicates the relay server requires payment.</note>
<note>Text indicating that this is a paid relay.</note>
</trans-unit>
<trans-unit id="Paste" xml:space="preserve">
<source>Paste</source>
<target>Paste</target>
<note>Button to paste a Nostr Wallet Connect string to connect the wallet for use in Damus for zaps.</note>
<trans-unit id="Paste NWC Address" xml:space="preserve">
<source>Paste NWC Address</source>
<target>Paste NWC Address</target>
<note>Text for button to connect a lightning wallet.</note>
</trans-unit>
<trans-unit id="Pay" xml:space="preserve">
<source>Pay</source>
@ -1422,8 +1463,7 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<trans-unit id="Permanently Delete Account" xml:space="preserve">
<source>Permanently Delete Account</source>
<target>Permanently Delete Account</target>
<note>Alert for deleting the users account.
Section title for deleting the user</note>
<note>Alert for deleting the users account.</note>
</trans-unit>
<trans-unit id="Plan" xml:space="preserve">
<source>Plan</source>
@ -1520,6 +1560,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Purchased!</target>
<note>User purchased a subscription</note>
</trans-unit>
<trans-unit id="Purchasing" xml:space="preserve">
<source>Purchasing</source>
<target>Purchasing</target>
<note>Loading label indicating the purchase action is in progress</note>
</trans-unit>
<trans-unit id="Purple" xml:space="preserve">
<source>Purple</source>
<target>Purple</target>
@ -1535,6 +1580,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Quote</target>
<note>Button to compose a quoted note</note>
</trans-unit>
<trans-unit id="Quotes" xml:space="preserve">
<source>Quotes</source>
<target>Quotes</target>
<note>Navigation bar title for Quote Reposts view.</note>
</trans-unit>
<trans-unit id="Ran to suspension." xml:space="preserve">
<source>Ran to suspension.</source>
<target>Ran to suspension.</target>
@ -1547,20 +1597,10 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
Section header for reactions settings
Title of emoji reactions view</note>
</trans-unit>
<trans-unit id="Recommended Emojis" xml:space="preserve">
<source>Recommended Emojis</source>
<target>Recommended Emojis</target>
<note>Section title for recommend emojis</note>
</trans-unit>
<trans-unit id="Recommended relays" xml:space="preserve">
<source>Recommended relays</source>
<target>Recommended relays</target>
<note>Title for view of recommended relays.</note>
</trans-unit>
<trans-unit id="Relay" xml:space="preserve">
<source>Relay</source>
<target>Relay</target>
<note>Label to display relay address.</note>
<trans-unit id="Relay Logs" xml:space="preserve">
<source>Relay Logs</source>
<target>Relay Logs</target>
<note>Text label indicating that the text below it are developer mode logs.</note>
</trans-unit>
<trans-unit id="Relays" xml:space="preserve">
<source>Relays</source>
@ -1589,6 +1629,16 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Remove bookmark</target>
<note>Context menu option for removing a note bookmark.</note>
</trans-unit>
<trans-unit id="Renew (1 mo)" xml:space="preserve">
<source>Renew (1 mo)</source>
<target>Renew (1 mo)</target>
<note>Button to take user to renew subscription for one month</note>
</trans-unit>
<trans-unit id="Renew (1 yr)" xml:space="preserve">
<source>Renew (1 yr)</source>
<target>Renew (1 yr)</target>
<note>Button to take user to renew subscription for one year</note>
</trans-unit>
<trans-unit id="Renews on" xml:space="preserve">
<source>Renews on</source>
<target>Renews on</target>
@ -1692,6 +1742,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Runtime error</target>
<note>Indication that a runtime error occurred when running a NostrScript.</note>
</trans-unit>
<trans-unit id="SOFTWARE" xml:space="preserve">
<source>SOFTWARE</source>
<target>SOFTWARE</target>
<note>Text label indicating which relay software is used to run this Nostr relay.</note>
</trans-unit>
<trans-unit id="Satoshi Nakamoto" xml:space="preserve">
<source>Satoshi Nakamoto</source>
<target>Satoshi Nakamoto</target>
@ -1727,6 +1782,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Scan Code</target>
<note>Button to switch to scan QR Code page.</note>
</trans-unit>
<trans-unit id="Scan NWC Address" xml:space="preserve">
<source>Scan NWC Address</source>
<target>Scan NWC Address</target>
<note>Text for button to connect a lightning wallet.</note>
</trans-unit>
<trans-unit id="Scan Your Private Key QR" xml:space="preserve">
<source>Scan Your Private Key QR</source>
<target>Scan Your Private Key QR</target>
@ -1774,6 +1834,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Secret Account Login Key</target>
<note>Section title for user's secret account login key.</note>
</trans-unit>
<trans-unit id="Securely connect your Damus app to your wallet using Nostr Wallet Connect" xml:space="preserve">
<source>Securely connect your Damus app to your wallet using Nostr Wallet Connect</source>
<target>Securely connect your Damus app to your wallet using Nostr Wallet Connect</target>
<note>Text to prompt user to connect their wallet using 'Nostr Wallet Connect'.</note>
</trans-unit>
<trans-unit id="Select a Lightning wallet" xml:space="preserve">
<source>Select a Lightning wallet</source>
<target>Select a Lightning wallet</target>
@ -1845,7 +1910,7 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<trans-unit id="Show" xml:space="preserve">
<source>Show</source>
<target>Show</target>
<note>Button to show a note from a user who has been muted.
<note>Button to show a note which has been muted.
Toggle to show or hide user's secret account login key.</note>
</trans-unit>
<trans-unit id="Show general statuses" xml:space="preserve">
@ -1884,11 +1949,6 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Show profile action sheets</target>
<note>Setting to show profile action sheets when clicking on a user's profile picture</note>
</trans-unit>
<trans-unit id="Show recommended relays" xml:space="preserve">
<source>Show recommended relays</source>
<target>Show recommended relays</target>
<note>Button to show recommended relays.</note>
</trans-unit>
<trans-unit id="Show wallet selector" xml:space="preserve">
<source>Show wallet selector</source>
<target>Show wallet selector</target>
@ -1919,11 +1979,6 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Social media has developed into a key way information flows around the world. Unfortunately, our current social media systems are broken</target>
<note>Description about why Nostr is needed.</note>
</trans-unit>
<trans-unit id="Software" xml:space="preserve">
<source>Software</source>
<target>Software</target>
<note>Label to display relay software.</note>
</trans-unit>
<trans-unit id="Someone posted a note" xml:space="preserve">
<source>Someone posted a note</source>
<target>Someone posted a note</target>
@ -1947,8 +2002,7 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<trans-unit id="Spam" xml:space="preserve">
<source>Spam</source>
<target>Spam</target>
<note>Description of report type for spam.
Section header for Universe/Search spam</note>
<note>Description of report type for spam.</note>
</trans-unit>
<trans-unit id="Staging" xml:space="preserve">
<source>Staging</source>
@ -2000,23 +2054,23 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
<target>Take Photo</target>
<note>Option to take a photo with the camera</note>
</trans-unit>
<trans-unit id="Test (localhost)" xml:space="preserve">
<source>Test (localhost)</source>
<target>Test (localhost)</target>
<note>Label indicating a localhost test environment for Damus Purple functionality (Developer feature)</note>
<trans-unit id="Test (local)" xml:space="preserve">
<source>Test (local)</source>
<target>Test (local)</target>
<note>Label indicating a local test environment for Damus Purple functionality (Developer feature)</note>
</trans-unit>
<trans-unit id="Text Truncation" xml:space="preserve">
<source>Text Truncation</source>
<target>Text Truncation</target>
<note>Section header for damus text truncation user configuration</note>
</trans-unit>
<trans-unit id="Thank you very much for signing up for Damusu{00A0}Purple. Your contribution helps us continue our fight for a more Open and Freeu{00A0}internet.&#10;&#10;You will also get access to premium features, and a star badge on your profile.&#10;&#10;Enjoy!" xml:space="preserve">
<source>Thank you very much for signing up for Damusu{00A0}Purple. Your contribution helps us continue our fight for a more Open and Freeu{00A0}internet.
<trans-unit id="Thank you very much for signing up for Damus Purple. Your contribution helps us continue our fight for a more Open and Free internet.&#10;&#10;You will also get access to premium features, and a star badge on your profile.&#10;&#10;Enjoy!" xml:space="preserve">
<source>Thank you very much for signing up for Damus Purple. Your contribution helps us continue our fight for a more Open and Free internet.
You will also get access to premium features, and a star badge on your profile.
Enjoy!</source>
<target>Thank you very much for signing up for Damusu{00A0}Purple. Your contribution helps us continue our fight for a more Open and Freeu{00A0}internet.
<target>Thank you very much for signing up for Damus Purple. Your contribution helps us continue our fight for a more Open and Free internet.
You will also get access to premium features, and a star badge on your profile.
@ -2045,15 +2099,20 @@ You're all set!</source>
You're all set!</target>
<note>An error message that appears when the user attempts to add a relay that has already been added.</note>
</trans-unit>
<trans-unit id="There has been an unexpected error with the in-app purchase. Please try again later or contact support@damus.io. Error: %@" xml:space="preserve">
<source>There has been an unexpected error with the in-app purchase. Please try again later or contact support@damus.io. Error: %@</source>
<target>There has been an unexpected error with the in-app purchase. Please try again later or contact support@damus.io. Error: %@</target>
<note>In-app purchase error message for the user</note>
</trans-unit>
<trans-unit id="There was an error loading your account. Please try again later. If problem persists, please contact us at support@damus.io" xml:space="preserve">
<source>There was an error loading your account. Please try again later. If problem persists, please contact us at support@damus.io</source>
<target>There was an error loading your account. Please try again later. If problem persists, please contact us at support@damus.io</target>
<note>Error label when Purple account information fails to load</note>
</trans-unit>
<trans-unit id="This is a paid relay, you must pay for notes to be accepted." xml:space="preserve">
<source>This is a paid relay, you must pay for notes to be accepted.</source>
<target>This is a paid relay, you must pay for notes to be accepted.</target>
<note>Footer description that explains that the relay server requires payment to post.</note>
<trans-unit id="This device's in-app purchase is registered to a different Nostr account. Unable to manage this Purple account. If you believe this was a mistake, please contact us via support@damus.io." xml:space="preserve">
<source>This device's in-app purchase is registered to a different Nostr account. Unable to manage this Purple account. If you believe this was a mistake, please contact us via support@damus.io.</source>
<target>This device's in-app purchase is registered to a different Nostr account. Unable to manage this Purple account. If you believe this was a mistake, please contact us via support@damus.io.</target>
<note>Notice label that user cannot manage their In-App purchases</note>
</trans-unit>
<trans-unit id="This is a public key, you will not be able to make notes 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 notes or interact in any way. This is used for viewing accounts from their perspective.</source>
@ -2084,6 +2143,11 @@ Nice to meet you all! #introductions #plebchain </target>
<target>Thread</target>
<note>Navigation bar title for note thread.</note>
</trans-unit>
<trans-unit id="Threads" xml:space="preserve">
<source>Threads</source>
<target>Threads</target>
<note>Section header title for a list of threads that are muted.</note>
</trans-unit>
<trans-unit id="To continue your Purple subscription checkout, please verify your npub by clicking on the button below" xml:space="preserve">
<source>To continue your Purple subscription checkout, please verify your npub by clicking on the button below</source>
<target>To continue your Purple subscription checkout, please verify your npub by clicking on the button below</target>
@ -2148,7 +2212,8 @@ Nice to meet you all! #introductions #plebchain </target>
<trans-unit id="URL" xml:space="preserve">
<source>URL</source>
<target>URL</target>
<note>Example URL to LibreTranslate server</note>
<note>Custom URL host for Damus Purple testing
Example URL to LibreTranslate server</note>
</trans-unit>
<trans-unit id="Unable to find a QR Code" xml:space="preserve">
<source>Unable to find a QR Code</source>
@ -2180,16 +2245,16 @@ Nice to meet you all! #introductions #plebchain </target>
<target>Unmute</target>
<note>Button to unmute a profile.</note>
</trans-unit>
<trans-unit id="Unmute Hashtag" xml:space="preserve">
<source>Unmute Hashtag</source>
<target>Unmute Hashtag</target>
<note>Label represnting a button that the user can tap to unmute a given hashtag so they start seeing it in their feed again.</note>
</trans-unit>
<trans-unit id="Unmute conversation" xml:space="preserve">
<source>Unmute conversation</source>
<target>Unmute conversation</target>
<note>Context menu option for unmuting a conversation.</note>
</trans-unit>
<trans-unit id="Untitled" xml:space="preserve">
<source>Untitled</source>
<target>Untitled</target>
<note>Text indicating that the long-form note title is untitled.</note>
</trans-unit>
<trans-unit id="Upload" xml:space="preserve">
<source>Upload</source>
<target>Upload</target>
@ -2215,6 +2280,16 @@ Nice to meet you all! #introductions #plebchain </target>
<target>Username</target>
<note>Label for Username section of user profile form.</note>
</trans-unit>
<trans-unit id="Users" xml:space="preserve">
<source>Users</source>
<target>Users</target>
<note>Section header title for a list of muted users.</note>
</trans-unit>
<trans-unit id="VERSION" xml:space="preserve">
<source>VERSION</source>
<target>VERSION</target>
<note>Text label indicating which version of the relay software is being run for this Nostr relay.</note>
</trans-unit>
<trans-unit id="Verified!" xml:space="preserve">
<source>Verified!</source>
<target>Verified!</target>
@ -2228,8 +2303,7 @@ Nice to meet you all! #introductions #plebchain </target>
<trans-unit id="Version" xml:space="preserve">
<source>Version</source>
<target>Version</target>
<note>Label to display relay software version.
Section title for displaying the version number of the Damus app.</note>
<note>Section title for displaying the version number of the Damus app.</note>
</trans-unit>
<trans-unit id="View QR Code" xml:space="preserve">
<source>View QR Code</source>
@ -2276,10 +2350,21 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<trans-unit id="Wallet" xml:space="preserve">
<source>Wallet</source>
<target>Wallet</target>
<note>Navigation title for Wallet view
<note>Navigation title for attaching Nostr Wallet Connect lightning wallet.
Navigation title for Wallet view
Sidebar menu label for Wallet view.
Title for section in zap settings that controls the Lightning wallet selection.</note>
</trans-unit>
<trans-unit id="Wallet Address" xml:space="preserve">
<source>Wallet Address</source>
<target>Wallet Address</target>
<note>Label text indicating that below it is the wallet address.</note>
</trans-unit>
<trans-unit id="Wallet Relay" xml:space="preserve">
<source>Wallet Relay</source>
<target>Wallet Relay</target>
<note>Label text indicating that below it is the information about the wallet relay.</note>
</trans-unit>
<trans-unit id="Website" xml:space="preserve">
<source>Website</source>
<target>Website</target>
@ -2335,6 +2420,11 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<target>Why we need Nostr?</target>
<note>Heading text for section describing why Nostr is needed.</note>
</trans-unit>
<trans-unit id="Words" xml:space="preserve">
<source>Words</source>
<target>Words</target>
<note>Section header title for a list of words that are muted.</note>
</trans-unit>
<trans-unit id="Yes" xml:space="preserve">
<source>Yes</source>
<target>Yes</target>
@ -2365,6 +2455,21 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<target>Your Name</target>
<note>Label for Your Name section of user profile form.</note>
</trans-unit>
<trans-unit id="Your Purple subscription expires in %@ days. Renew?" xml:space="preserve">
<source>Your Purple subscription expires in %@ days. Renew?</source>
<target>Your Purple subscription expires in %@ days. Renew?</target>
<note>A notification message explaining to the user that their Damus Purple Subscription is expiring soon, prompting them to renew.</note>
</trans-unit>
<trans-unit id="Your Purple subscription expires in 1 day. Renew?" xml:space="preserve">
<source>Your Purple subscription expires in 1 day. Renew?</source>
<target>Your Purple subscription expires in 1 day. Renew?</target>
<note>A notification message explaining to the user that their Damus Purple Subscription is expiring in one day, prompting them to renew.</note>
</trans-unit>
<trans-unit id="Your Purple subscription has expired. Renew?" xml:space="preserve">
<source>Your Purple subscription has expired. Renew?</source>
<target>Your Purple subscription has expired. Renew?</target>
<note>A notification message explaining to the user that their Damus Purple Subscription has expired, prompting them to renew.</note>
</trans-unit>
<trans-unit id="Your report will be sent to the relays you are connected to" xml:space="preserve">
<source>Your report will be sent to the relays you are connected to</source>
<target>Your report will be sent to the relays you are connected to</target>
@ -2400,8 +2505,7 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<trans-unit id="Zap failed" xml:space="preserve">
<source>Zap failed</source>
<target>Zap failed</target>
<note>Button label indicating that a zap action was unsuccessful (i.e. the user was unable to send a Bitcoin tip via the lightning network to the user shown on-screen)
Title of an alert indicating that a zap action failed</note>
<note>Button label indicating that a zap action was unsuccessful (i.e. the user was unable to send a Bitcoin tip via the lightning network to the user shown on-screen)</note>
</trans-unit>
<trans-unit id="Zap type" xml:space="preserve">
<source>Zap type</source>
@ -2460,7 +2564,13 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<trans-unit id="now" xml:space="preserve">
<source>now</source>
<target>now</target>
<note>String indicating that a given timestamp just occurred</note>
<note>Relative time label that indicates a notification happened now
String indicating that a given timestamp just occurred</note>
</trans-unit>
<trans-unit id="npub, #hashtag, phrase" xml:space="preserve">
<source>npub, #hashtag, phrase</source>
<target>npub, #hashtag, phrase</target>
<note>Placeholder example for relay server address.</note>
</trans-unit>
<trans-unit id="nsec1..." xml:space="preserve">
<source>nsec1...</source>
@ -2587,16 +2697,11 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<target>%@ and %@ zapped you</target>
<note>Notification that 2 users zapped the current user's profile</note>
</trans-unit>
<trans-unit id="⚡" xml:space="preserve">
<source>⚡</source>
<target>⚡</target>
<note>Placeholder example for an emoji reaction</note>
</trans-unit>
</body>
</file>
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.2" build-num="15C500b"/>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.3" build-num="15E204a"/>
</header>
<body>
<trans-unit id="/followed_by_three_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
@ -2659,6 +2764,21 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<target>%#@IMPORTS@</target>
<note/>
</trans-unit>
<trans-unit id="/quoted_reposts_count:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@QUOTE_REPOSTS@</source>
<target>%#@QUOTE_REPOSTS@</target>
<note/>
</trans-unit>
<trans-unit id="/quoted_reposts_count:dict/QUOTE_REPOSTS:dict/one:dict/:string" xml:space="preserve">
<source>Quote</source>
<target>Quote</target>
<note/>
</trans-unit>
<trans-unit id="/quoted_reposts_count:dict/QUOTE_REPOSTS:dict/other:dict/:string" xml:space="preserve">
<source>Quotes</source>
<target>Quotes</target>
<note/>
</trans-unit>
<trans-unit id="/reacted_tagged_in_3:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
<source>%#@REACTED@</source>
<target>%#@REACTED@</target>
@ -2963,7 +3083,7 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
</file>
<file original="DamusNotificationService/InfoPlist.xcstrings" source-language="en-US" target-language="en-US" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.2" build-num="15C500b"/>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.3" build-num="15E204a"/>
</header>
<body>
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
@ -2985,7 +3105,7 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
</file>
<file original="DamusNotificationService/Localizable.xcstrings" source-language="en-US" target-language="en-US" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.2" build-num="15C500b"/>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="15.3" build-num="15E204a"/>
</header>
<body>
<trans-unit id="" xml:space="preserve">
@ -3113,10 +3233,10 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
<target state="new">Staging</target>
<note>Label indicating a staging test environment for Damus Purple functionality (Developer feature)</note>
</trans-unit>
<trans-unit id="Test (localhost)" xml:space="preserve">
<source>Test (localhost)</source>
<target state="new">Test (localhost)</target>
<note>Label indicating a localhost test environment for Damus Purple functionality (Developer feature)</note>
<trans-unit id="Test (local)" xml:space="preserve">
<source>Test (local)</source>
<target state="new">Test (local)</target>
<note>Label indicating a local test environment for Damus Purple functionality (Developer feature)</note>
</trans-unit>
<trans-unit id="This note contains too many items and cannot be rendered" xml:space="preserve">
<source>This note contains too many items and cannot be rendered</source>

View File

@ -96,8 +96,8 @@
"Staging" : {
"comment" : "Label indicating a staging test environment for Damus Purple functionality (Developer feature)"
},
"Test (localhost)" : {
"comment" : "Label indicating a localhost test environment for Damus Purple functionality (Developer feature)"
"Test (local)" : {
"comment" : "Label indicating a local test environment for Damus Purple functionality (Developer feature)"
},
"This note contains too many items and cannot be rendered" : {
"comment" : "Error message indicating that a note is too big and cannot be rendered"

View File

@ -226,6 +226,22 @@
<string>Reposts</string>
</dict>
</dict>
<key>quoted_reposts_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@QUOTE_REPOSTS@</string>
<key>QUOTE_REPOSTS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Quote</string>
<key>other</key>
<string>Quotes</string>
</dict>
</dict>
<key>sats</key>
<dict>
<key>NSStringLocalizedFormatKey</key>

View File

@ -3,10 +3,10 @@
"project" : "damus.xcodeproj",
"targetLocale" : "en-US",
"toolInfo" : {
"toolBuildNumber" : "15C500b",
"toolBuildNumber" : "15E204a",
"toolID" : "com.apple.dt.xcode",
"toolName" : "Xcode",
"toolVersion" : "15.2"
"toolVersion" : "15.3"
},
"version" : "1.0"
}

View File

@ -254,6 +254,24 @@
<string>Republicaciones</string>
</dict>
</dict>
<key>quoted_reposts_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@QUOTE_REPOSTS@</string>
<key>QUOTE_REPOSTS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Cita</string>
<key>many</key>
<string>Citas</string>
<key>other</key>
<string>Citas</string>
</dict>
</dict>
<key>sats</key>
<dict>
<key>NSStringLocalizedFormatKey</key>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -226,6 +226,22 @@
<string>Herplaatsingen</string>
</dict>
</dict>
<key>quoted_reposts_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@QUOTE_REPOSTS@</string>
<key>QUOTE_REPOSTS</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>one</key>
<string>Citaat</string>
<key>other</key>
<string>Citaten</string>
</dict>
</dict>
<key>sats</key>
<dict>
<key>NSStringLocalizedFormatKey</key>