Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
6bddee0354
|
|||
| c6d9e0b3c9 | |||
| 6d5a152c17 | |||
| 76529f69d0 | |||
| 052ea9b727 | |||
| 95cf45073d | |||
| efd3c95c10 | |||
| d198206cc2 | |||
| 0b0bcedb1e | |||
| 5f7855d6d3 | |||
| e3db84778a | |||
| 5363a0313f | |||
| cb116b0f85 | |||
| 62e40d2824 | |||
|
e0b1985df5
|
|||
|
be585e914d
|
|||
|
e515bf7322
|
|||
|
050f38feac
|
|||
|
b94a435a9b
|
|||
| 279854a9fd | |||
| 19ba020bd0 | |||
| 43a5bbd53a | |||
| 43630cbfa6 | |||
| ae2f48484a | |||
| 2c9b280a04 | |||
| ba494f94ab | |||
| 26d2627a1c | |||
| c2918aaf16 | |||
| e332a7f82c | |||
| 8fbc9dc773 |
@@ -36,6 +36,7 @@
|
|||||||
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
||||||
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBCB77295FE5C70037388A /* TimeAgoTests.swift */; };
|
||||||
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
|
3AE45AF6297BB2E700C1D842 /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
|
||||||
|
3AFE89C32BD4156F00AD31EF /* MCEmojiPicker in Frameworks */ = {isa = PBXBuildFile; productRef = 3AFE89C22BD4156F00AD31EF /* MCEmojiPicker */; };
|
||||||
3CCD1E6A2A874C4E0099A953 /* Nip98HTTPAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCD1E692A874C4E0099A953 /* Nip98HTTPAuth.swift */; };
|
3CCD1E6A2A874C4E0099A953 /* Nip98HTTPAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CCD1E692A874C4E0099A953 /* Nip98HTTPAuth.swift */; };
|
||||||
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
4C06670128FC7C5900038D2A /* RelayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670028FC7C5900038D2A /* RelayView.swift */; };
|
||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 4C06670328FC7EC500038D2A /* Kingfisher */; };
|
||||||
@@ -281,7 +282,6 @@
|
|||||||
4CA927632A290EB10098A105 /* EventTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927622A290EB10098A105 /* EventTop.swift */; };
|
4CA927632A290EB10098A105 /* EventTop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927622A290EB10098A105 /* EventTop.swift */; };
|
||||||
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927642A290F1A0098A105 /* TimeDot.swift */; };
|
4CA927652A290F1A0098A105 /* TimeDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927642A290F1A0098A105 /* TimeDot.swift */; };
|
||||||
4CA927672A290F8B0098A105 /* RelativeTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA927662A290F8B0098A105 /* RelativeTime.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 */; };
|
4CA9276C2A2910D10098A105 /* ReplyPart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA9276B2A2910D10098A105 /* ReplyPart.swift */; };
|
||||||
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
|
4CAAD8AD298851D000060CEA /* AccountDeletion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AC298851D000060CEA /* AccountDeletion.swift */; };
|
||||||
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; };
|
4CAAD8B029888AD200060CEA /* RelayConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CAAD8AF29888AD200060CEA /* RelayConfigView.swift */; };
|
||||||
@@ -318,7 +318,6 @@
|
|||||||
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; };
|
4CC7AAF8297F1CEE00430951 /* EventProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF7297F1CEE00430951 /* EventProfile.swift */; };
|
||||||
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; };
|
4CC7AAFA297F64AC00430951 /* EventMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC7AAF9297F64AC00430951 /* EventMenu.swift */; };
|
||||||
4CCEB7AE29B53D260078AA28 /* SearchingEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CCEB7AD29B53D260078AA28 /* SearchingEventView.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 */; };
|
4CD348EF29C3659D00497EB2 /* ImageUploadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD348EE29C3659D00497EB2 /* ImageUploadModel.swift */; };
|
||||||
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
4CD7641B28A1641400B6928F /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
||||||
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128929E9D10C0006FA5A /* SignalView.swift */; };
|
4CDA128A29E9D10C0006FA5A /* SignalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA128929E9D10C0006FA5A /* SignalView.swift */; };
|
||||||
@@ -444,8 +443,6 @@
|
|||||||
BA3759932ABCCEBA0018D73B /* CameraModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759902ABCCEBA0018D73B /* CameraModel.swift */; };
|
BA3759932ABCCEBA0018D73B /* CameraModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759902ABCCEBA0018D73B /* CameraModel.swift */; };
|
||||||
BA3759942ABCCEBA0018D73B /* CameraService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759912ABCCEBA0018D73B /* CameraService.swift */; };
|
BA3759942ABCCEBA0018D73B /* CameraService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759912ABCCEBA0018D73B /* CameraService.swift */; };
|
||||||
BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759962ABCCF360018D73B /* CameraPreview.swift */; };
|
BA3759972ABCCF360018D73B /* CameraPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759962ABCCF360018D73B /* CameraPreview.swift */; };
|
||||||
BA4AB0AE2A63B9270070A32A /* AddEmojiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */; };
|
|
||||||
BA4AB0B02A63B94D0070A32A /* EmojiListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */; };
|
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
|
||||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; };
|
||||||
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; };
|
D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; };
|
||||||
@@ -639,6 +636,7 @@
|
|||||||
D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; };
|
D7EDED332B12ACAE0018B19C /* DamusUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EDED322B12ACAE0018B19C /* DamusUserDefaults.swift */; };
|
||||||
D7EDED342B12ACAE0018B19C /* 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 */; };
|
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 */; };
|
D7FF94002AC7AC5300FD969D /* RelayURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7FF93FF2AC7AC5200FD969D /* RelayURL.swift */; };
|
||||||
E02429952B7E97740088B16C /* CameraController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02429942B7E97740088B16C /* CameraController.swift */; };
|
E02429952B7E97740088B16C /* CameraController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02429942B7E97740088B16C /* CameraController.swift */; };
|
||||||
E02B54182B4DFADA0077FF42 /* Bech32ObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */; };
|
E02B54182B4DFADA0077FF42 /* Bech32ObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */; };
|
||||||
@@ -740,6 +738,9 @@
|
|||||||
3A41E55A299D52BE001FA465 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
3A5CAE1D298DC0DB00B5334F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||||
@@ -1193,7 +1194,6 @@
|
|||||||
4CA927622A290EB10098A105 /* EventTop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTop.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
4CA9276E2A2A5D110098A105 /* wasm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = wasm.c; sourceTree = "<group>"; };
|
||||||
@@ -1237,7 +1237,6 @@
|
|||||||
4CC7AAF7297F1CEE00430951 /* EventProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventProfile.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
4CDA128929E9D10C0006FA5A /* SignalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalView.swift; sourceTree = "<group>"; };
|
||||||
@@ -1366,8 +1365,6 @@
|
|||||||
BA3759902ABCCEBA0018D73B /* CameraModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraModel.swift; sourceTree = "<group>"; };
|
BA3759902ABCCEBA0018D73B /* CameraModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraModel.swift; sourceTree = "<group>"; };
|
||||||
BA3759912ABCCEBA0018D73B /* CameraService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraService.swift; sourceTree = "<group>"; };
|
BA3759912ABCCEBA0018D73B /* CameraService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraService.swift; sourceTree = "<group>"; };
|
||||||
BA3759962ABCCF360018D73B /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = "<group>"; };
|
BA3759962ABCCF360018D73B /* CameraPreview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPreview.swift; sourceTree = "<group>"; };
|
||||||
BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEmojiView.swift; sourceTree = "<group>"; };
|
|
||||||
BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiListItemView.swift; sourceTree = "<group>"; };
|
|
||||||
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||||
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = "<group>"; };
|
||||||
D2277EE92A089BD5006C3807 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
D2277EE92A089BD5006C3807 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
||||||
@@ -1433,6 +1430,7 @@
|
|||||||
D7EDED202B117DCA0018B19C /* SequenceUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceUtils.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
E02B54172B4DFADA0077FF42 /* Bech32ObjectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bech32ObjectTests.swift; sourceTree = "<group>"; };
|
||||||
@@ -1469,6 +1467,7 @@
|
|||||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
||||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
||||||
4C27C9322A64766F007DBC75 /* MarkdownUI in Frameworks */,
|
4C27C9322A64766F007DBC75 /* MarkdownUI in Frameworks */,
|
||||||
|
3AFE89C32BD4156F00AD31EF /* MCEmojiPicker in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1722,8 +1721,7 @@
|
|||||||
4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */,
|
4C1A9A2629DDE31900516EAC /* TranslationSettingsView.swift */,
|
||||||
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */,
|
E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */,
|
||||||
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */,
|
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */,
|
||||||
BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */,
|
D7FD12252BD345A700CF195B /* FirstAidSettingsView.swift */,
|
||||||
BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2288,7 +2286,6 @@
|
|||||||
4CA927622A290EB10098A105 /* EventTop.swift */,
|
4CA927622A290EB10098A105 /* EventTop.swift */,
|
||||||
4CC7AAF3297F18B400430951 /* ReplyDescription.swift */,
|
4CC7AAF3297F18B400430951 /* ReplyDescription.swift */,
|
||||||
4CA927662A290F8B0098A105 /* RelativeTime.swift */,
|
4CA927662A290F8B0098A105 /* RelativeTime.swift */,
|
||||||
4CA927692A290FC00098A105 /* ContextButton.swift */,
|
|
||||||
4CA9276B2A2910D10098A105 /* ReplyPart.swift */,
|
4CA9276B2A2910D10098A105 /* ReplyPart.swift */,
|
||||||
5C7389B02B6EFA7100781E0A /* ProxyView.swift */,
|
5C7389B02B6EFA7100781E0A /* ProxyView.swift */,
|
||||||
);
|
);
|
||||||
@@ -2399,7 +2396,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4CCEB7AD29B53D260078AA28 /* SearchingEventView.swift */,
|
4CCEB7AD29B53D260078AA28 /* SearchingEventView.swift */,
|
||||||
4CCEB7AF29B5415A0078AA28 /* SearchingProfileView.swift */,
|
|
||||||
4C9D6D1A2B1D35D7004E5CD9 /* PullDownSearch.swift */,
|
4C9D6D1A2B1D35D7004E5CD9 /* PullDownSearch.swift */,
|
||||||
);
|
);
|
||||||
path = Search;
|
path = Search;
|
||||||
@@ -2553,6 +2549,7 @@
|
|||||||
E06336A92B75832100A88E6B /* ImageMetadataTest.swift */,
|
E06336A92B75832100A88E6B /* ImageMetadataTest.swift */,
|
||||||
D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */,
|
D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */,
|
||||||
D72927AC2BAB515C00F93E90 /* RelayURLTests.swift */,
|
D72927AC2BAB515C00F93E90 /* RelayURLTests.swift */,
|
||||||
|
D7831AF72BBE11E2005DA780 /* VideoCacheTests.swift */,
|
||||||
);
|
);
|
||||||
path = damusTests;
|
path = damusTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2825,6 +2822,7 @@
|
|||||||
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
4C649880286E0EE300EAE2B3 /* secp256k1 */,
|
||||||
4C06670328FC7EC500038D2A /* Kingfisher */,
|
4C06670328FC7EC500038D2A /* Kingfisher */,
|
||||||
4C27C9312A64766F007DBC75 /* MarkdownUI */,
|
4C27C9312A64766F007DBC75 /* MarkdownUI */,
|
||||||
|
3AFE89C22BD4156F00AD31EF /* MCEmojiPicker */,
|
||||||
);
|
);
|
||||||
productName = damus;
|
productName = damus;
|
||||||
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
||||||
@@ -2934,6 +2932,7 @@
|
|||||||
"es-419",
|
"es-419",
|
||||||
"es-ES",
|
"es-ES",
|
||||||
fa,
|
fa,
|
||||||
|
fi,
|
||||||
fr,
|
fr,
|
||||||
"hu-HU",
|
"hu-HU",
|
||||||
id,
|
id,
|
||||||
@@ -2962,6 +2961,7 @@
|
|||||||
4CCF9AB02A1FE80B00E03CFB /* XCRemoteSwiftPackageReference "GSPlayer" */,
|
4CCF9AB02A1FE80B00E03CFB /* XCRemoteSwiftPackageReference "GSPlayer" */,
|
||||||
4C27C9302A64766F007DBC75 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
|
4C27C9302A64766F007DBC75 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
|
||||||
D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */,
|
D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */,
|
||||||
|
3AFE89C12BD4156F00AD31EF /* XCRemoteSwiftPackageReference "MCEmojiPicker" */,
|
||||||
);
|
);
|
||||||
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -3075,7 +3075,6 @@
|
|||||||
4C32B9572A9AD44700DC3548 /* Root.swift in Sources */,
|
4C32B9572A9AD44700DC3548 /* Root.swift in Sources */,
|
||||||
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
|
4C3EA64428FF558100C48A62 /* sha256.c in Sources */,
|
||||||
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */,
|
504323A72A34915F006AE6DC /* RelayModel.swift in Sources */,
|
||||||
4CA9276A2A290FC00098A105 /* ContextButton.swift in Sources */,
|
|
||||||
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */,
|
4CF0ABF62985CD5500D66079 /* UserSearch.swift in Sources */,
|
||||||
4C32B9542A9AD44700DC3548 /* FlatBuffersUtils.swift in Sources */,
|
4C32B9542A9AD44700DC3548 /* FlatBuffersUtils.swift in Sources */,
|
||||||
D7EDED1C2B1178FE0018B19C /* NoteContent.swift in Sources */,
|
D7EDED1C2B1178FE0018B19C /* NoteContent.swift in Sources */,
|
||||||
@@ -3106,6 +3105,7 @@
|
|||||||
D7EDED1E2B11797D0018B19C /* LongformEvent.swift in Sources */,
|
D7EDED1E2B11797D0018B19C /* LongformEvent.swift in Sources */,
|
||||||
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
|
504323A92A3495B6006AE6DC /* RelayModelCache.swift in Sources */,
|
||||||
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
|
3A8CC6CC2A2CFEF900940F5F /* StringUtil.swift in Sources */,
|
||||||
|
D7FD12262BD345A700CF195B /* FirstAidSettingsView.swift in Sources */,
|
||||||
D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */,
|
D7870BC12AC4750B0080BA88 /* MentionView.swift in Sources */,
|
||||||
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
|
4CB55EF5295E679D007FD187 /* UserRelaysView.swift in Sources */,
|
||||||
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
|
4C363AA228296A7E006E126D /* SearchView.swift in Sources */,
|
||||||
@@ -3131,7 +3131,6 @@
|
|||||||
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */,
|
F75BA12F29A18EF500E10810 /* BookmarksView.swift in Sources */,
|
||||||
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */,
|
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */,
|
||||||
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
||||||
BA4AB0AE2A63B9270070A32A /* AddEmojiView.swift in Sources */,
|
|
||||||
4C32B94D2A9AD44700DC3548 /* Offset.swift in Sources */,
|
4C32B94D2A9AD44700DC3548 /* Offset.swift in Sources */,
|
||||||
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
|
||||||
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
|
||||||
@@ -3174,7 +3173,6 @@
|
|||||||
4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */,
|
4C9BB83429C12D9900FC4E37 /* EventProfileName.swift in Sources */,
|
||||||
4C7D09602A098C5D00943473 /* WalletView.swift in Sources */,
|
4C7D09602A098C5D00943473 /* WalletView.swift in Sources */,
|
||||||
4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */,
|
4CB8838F296F781C00DC99E7 /* ReactionsView.swift in Sources */,
|
||||||
BA4AB0B02A63B94D0070A32A /* EmojiListItemView.swift in Sources */,
|
|
||||||
B5C60C202B530D5100C5ECA7 /* MuteItem.swift in Sources */,
|
B5C60C202B530D5100C5ECA7 /* MuteItem.swift in Sources */,
|
||||||
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
||||||
4C32B9582A9AD44700DC3548 /* VeriferOptions.swift in Sources */,
|
4C32B9582A9AD44700DC3548 /* VeriferOptions.swift in Sources */,
|
||||||
@@ -3251,7 +3249,6 @@
|
|||||||
4C2B7BF22A71B6540049DEE7 /* Id.swift in Sources */,
|
4C2B7BF22A71B6540049DEE7 /* Id.swift in Sources */,
|
||||||
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */,
|
7C95CAEE299DCEF1009DCB67 /* KFOptionSetter+.swift in Sources */,
|
||||||
4C7D09722A0AEF5E00943473 /* DamusGradient.swift in Sources */,
|
4C7D09722A0AEF5E00943473 /* DamusGradient.swift in Sources */,
|
||||||
4CCEB7B029B5415A0078AA28 /* SearchingProfileView.swift in Sources */,
|
|
||||||
4C463CBF2B960B96008A8C36 /* PurpleBackdrop.swift in Sources */,
|
4C463CBF2B960B96008A8C36 /* PurpleBackdrop.swift in Sources */,
|
||||||
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */,
|
||||||
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
3169CAE6294E69C000EE4006 /* EmptyTimelineView.swift in Sources */,
|
||||||
@@ -3763,6 +3760,7 @@
|
|||||||
3AC59CA929CDDB78007E04A6 /* pt-BR */,
|
3AC59CA929CDDB78007E04A6 /* pt-BR */,
|
||||||
3A821C4029E819D500B4BCA7 /* fr */,
|
3A821C4029E819D500B4BCA7 /* fr */,
|
||||||
3ABACEC02A5B3ED10037A847 /* sw */,
|
3ABACEC02A5B3ED10037A847 /* sw */,
|
||||||
|
3A47CB792BDA05A200728A7C /* fi */,
|
||||||
);
|
);
|
||||||
name = Localizable.stringsdict;
|
name = Localizable.stringsdict;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -3798,6 +3796,7 @@
|
|||||||
3AC59CA829CDDB78007E04A6 /* pt-BR */,
|
3AC59CA829CDDB78007E04A6 /* pt-BR */,
|
||||||
3A821C3F29E819D500B4BCA7 /* fr */,
|
3A821C3F29E819D500B4BCA7 /* fr */,
|
||||||
3ABACEBF2A5B3ED10037A847 /* sw */,
|
3ABACEBF2A5B3ED10037A847 /* sw */,
|
||||||
|
3A47CB772BDA05A200728A7C /* fi */,
|
||||||
);
|
);
|
||||||
name = InfoPlist.strings;
|
name = InfoPlist.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -3834,6 +3833,7 @@
|
|||||||
3AC59CA729CDDB78007E04A6 /* pt-BR */,
|
3AC59CA729CDDB78007E04A6 /* pt-BR */,
|
||||||
3A821C3E29E819D500B4BCA7 /* fr */,
|
3A821C3E29E819D500B4BCA7 /* fr */,
|
||||||
3ABACEC12A5B3ED10037A847 /* sw */,
|
3ABACEC12A5B3ED10037A847 /* sw */,
|
||||||
|
3A47CB782BDA05A200728A7C /* fi */,
|
||||||
);
|
);
|
||||||
name = Localizable.strings;
|
name = Localizable.strings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -4260,6 +4260,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
3AFE89C12BD4156F00AD31EF /* XCRemoteSwiftPackageReference "MCEmojiPicker" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/izyumkin/MCEmojiPicker";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.2.3;
|
||||||
|
};
|
||||||
|
};
|
||||||
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/onevcat/Kingfisher";
|
repositoryURL = "https://github.com/onevcat/Kingfisher";
|
||||||
@@ -4303,6 +4311,11 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
3AFE89C22BD4156F00AD31EF /* MCEmojiPicker */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 3AFE89C12BD4156F00AD31EF /* XCRemoteSwiftPackageReference "MCEmojiPicker" */;
|
||||||
|
productName = MCEmojiPicker;
|
||||||
|
};
|
||||||
4C06670328FC7EC500038D2A /* Kingfisher */ = {
|
4C06670328FC7EC500038D2A /* Kingfisher */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
package = 4C06670228FC7EC500038D2A /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
||||||
|
|||||||
@@ -18,6 +18,15 @@
|
|||||||
"version" : "7.6.1"
|
"version" : "7.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "mcemojipicker",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/izyumkin/MCEmojiPicker",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "e0b4903b75ae1cc418d276d84d1cb946b8a1d73c",
|
||||||
|
"version" : "1.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "secp256k1.swift",
|
"identity" : "secp256k1.swift",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ struct SearchHeaderView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var SearchText: Text {
|
var SearchText: Text {
|
||||||
Text(verbatim: described.description)
|
Text(described.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -83,9 +83,9 @@ struct SingleCharacterAvatar: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NonImageAvatar {
|
NonImageAvatar {
|
||||||
Text(verbatim: character)
|
Text(character)
|
||||||
.font(.largeTitle.bold())
|
.font(.largeTitle.bold())
|
||||||
.mask(Text(verbatim: character)
|
.mask(Text(character)
|
||||||
.font(.largeTitle.bold()))
|
.font(.largeTitle.bold()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ struct UserStatusSheet: View {
|
|||||||
|
|
||||||
Picker(NSLocalizedString("Duration", comment: "Label for profile status expiration duration picker."), selection: $duration) {
|
Picker(NSLocalizedString("Duration", comment: "Label for profile status expiration duration picker."), selection: $duration) {
|
||||||
ForEach(StatusDuration.allCases, id: \.self) { d in
|
ForEach(StatusDuration.allCases, id: \.self) { d in
|
||||||
Text(verbatim: d.description)
|
Text(d.description)
|
||||||
.tag(d)
|
.tag(d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ struct SupporterBadge: View {
|
|||||||
.frame(width:size, height:size)
|
.frame(width:size, height:size)
|
||||||
.foregroundStyle(GoldGradient)
|
.foregroundStyle(GoldGradient)
|
||||||
if self.style == .full {
|
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)
|
.foregroundStyle(.secondary)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -679,10 +679,7 @@ struct ContentView: View {
|
|||||||
let relay_filters = RelayFilters(our_pubkey: pubkey)
|
let relay_filters = RelayFilters(our_pubkey: pubkey)
|
||||||
let bootstrap_relays = load_bootstrap_relays(pubkey: pubkey)
|
let bootstrap_relays = load_bootstrap_relays(pubkey: pubkey)
|
||||||
|
|
||||||
// dumb stuff needed for property wrappers
|
let settings = UserSettingsStore.globally_load_for(pubkey: pubkey)
|
||||||
UserSettingsStore.pubkey = pubkey
|
|
||||||
let settings = UserSettingsStore()
|
|
||||||
UserSettingsStore.shared = settings
|
|
||||||
|
|
||||||
let new_relay_filters = load_relay_filters(pubkey) == nil
|
let new_relay_filters = load_relay_filters(pubkey) == nil
|
||||||
for relay in bootstrap_relays {
|
for relay in bootstrap_relays {
|
||||||
@@ -835,6 +832,12 @@ func save_last_event(_ ev: NostrEvent, timeline: Timeline) {
|
|||||||
UserDefaults.standard.set(String(ev.created_at), forKey: "last_\(str)_time")
|
UserDefaults.standard.set(String(ev.created_at), forKey: "last_\(str)_time")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func save_last_event(_ ev_id: NoteId, created_at: UInt32, timeline: Timeline) {
|
||||||
|
let str = timeline.rawValue
|
||||||
|
UserDefaults.standard.set(ev_id.hex(), forKey: "last_\(str)")
|
||||||
|
UserDefaults.standard.set(String(created_at), forKey: "last_\(str)_time")
|
||||||
|
}
|
||||||
|
|
||||||
func update_filters_with_since(last_of_kind: [UInt32: NostrEvent], filters: [NostrFilter]) -> [NostrFilter] {
|
func update_filters_with_since(last_of_kind: [UInt32: NostrEvent], filters: [NostrFilter]) -> [NostrFilter] {
|
||||||
|
|
||||||
return filters.map { filter in
|
return filters.map { filter in
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
class Contacts {
|
class Contacts {
|
||||||
private var friends: Set<Pubkey> = Set()
|
private var friends: Set<Pubkey> = Set()
|
||||||
private var friend_of_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>]()
|
private var pubkey_to_our_friends = [Pubkey : Set<Pubkey>]()
|
||||||
|
|
||||||
let our_pubkey: 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) {
|
init(our_pubkey: Pubkey) {
|
||||||
self.our_pubkey = our_pubkey
|
self.our_pubkey = our_pubkey
|
||||||
@@ -88,3 +93,8 @@ class Contacts {
|
|||||||
return Array((pubkey_to_our_friends[pubkey] ?? Set()))
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,11 +41,15 @@ enum HomeResubFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HomeModel {
|
class HomeModel: ContactsDelegate {
|
||||||
// Don't trigger a user notification for events older than a certain age
|
// 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
|
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
|
// NDBTODO: let's get rid of this entirely, let nostrdb handle it
|
||||||
var has_event: [String: Set<NoteId>] = [:]
|
var has_event: [String: Set<NoteId>] = [:]
|
||||||
@@ -109,6 +113,32 @@ class HomeModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func resubscribe(_ resubbing: Resubscribe) {
|
||||||
if self.should_debounce_dms {
|
if self.should_debounce_dms {
|
||||||
// don't resub on initial load
|
// don't resub on initial load
|
||||||
@@ -279,10 +309,15 @@ class HomeModel {
|
|||||||
@MainActor
|
@MainActor
|
||||||
func handle_damus_app_notification(_ notification: DamusAppNotification) async {
|
func handle_damus_app_notification(_ notification: DamusAppNotification) async {
|
||||||
if self.notifications.insert_app_notification(notification: notification) {
|
if self.notifications.insert_app_notification(notification: notification) {
|
||||||
|
let last_notification = get_last_event(.notifications)
|
||||||
|
if last_notification == nil || last_notification!.created_at < notification.last_event_at {
|
||||||
|
save_last_event(NoteId.empty, created_at: notification.last_event_at, timeline: .notifications)
|
||||||
// If we successfully inserted a new Damus App notification, switch ON the Damus App notification bit on our NewsEventsBits
|
// If we successfully inserted a new Damus App notification, switch ON the Damus App notification bit on our NewsEventsBits
|
||||||
// This will cause the bell icon on the tab bar to display the purple dot indicating there is an unread notification
|
// This will cause the bell icon on the tab bar to display the purple dot indicating there is an unread notification
|
||||||
self.notification_status.new_events = NewEventsBits(rawValue: self.notification_status.new_events.rawValue | NewEventsBits.damus_app_notifications.rawValue)
|
self.notification_status.new_events = NewEventsBits(rawValue: self.notification_status.new_events.rawValue | NewEventsBits.damus_app_notifications.rawValue)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func filter_events() {
|
func filter_events() {
|
||||||
|
|||||||
@@ -49,6 +49,32 @@ enum MediaUpload {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mime_type: String {
|
||||||
|
switch self.file_extension {
|
||||||
|
case "jpg", "jpeg":
|
||||||
|
return "image/jpg"
|
||||||
|
case "png":
|
||||||
|
return "image/png"
|
||||||
|
case "gif":
|
||||||
|
return "image/gif"
|
||||||
|
case "tiff", "tif":
|
||||||
|
return "image/tiff"
|
||||||
|
case "mp4":
|
||||||
|
return "video/mp4"
|
||||||
|
case "ogg":
|
||||||
|
return "video/ogg"
|
||||||
|
case "webm":
|
||||||
|
return "video/webm"
|
||||||
|
default:
|
||||||
|
switch self {
|
||||||
|
case .image:
|
||||||
|
return "image/jpg"
|
||||||
|
case .video:
|
||||||
|
return "video/mp4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageUploadModel: NSObject, URLSessionTaskDelegate, ObservableObject {
|
class ImageUploadModel: NSObject, URLSessionTaskDelegate, ObservableObject {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ struct LongformEvent {
|
|||||||
var image: URL? = nil
|
var image: URL? = nil
|
||||||
var summary: String? = nil
|
var summary: String? = nil
|
||||||
var published_at: Date? = nil
|
var published_at: Date? = nil
|
||||||
|
var labels: [String]? = nil
|
||||||
|
|
||||||
static func parse(from ev: NostrEvent) -> LongformEvent {
|
static func parse(from ev: NostrEvent) -> LongformEvent {
|
||||||
var longform = LongformEvent(event: ev)
|
var longform = LongformEvent(event: ev)
|
||||||
@@ -26,6 +27,10 @@ struct LongformEvent {
|
|||||||
case "summary": longform.summary = tag[1].string()
|
case "summary": longform.summary = tag[1].string()
|
||||||
case "published_at":
|
case "published_at":
|
||||||
longform.published_at = Double(tag[1].string()).map { d in Date(timeIntervalSince1970: d) }
|
longform.published_at = Double(tag[1].string()).map { d in Date(timeIntervalSince1970: d) }
|
||||||
|
case "t":
|
||||||
|
if (longform.labels?.append(tag[1].string())) == nil {
|
||||||
|
longform.labels = [tag[1].string()]
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,14 @@ class UserSettingsStore: ObservableObject {
|
|||||||
static var shared: UserSettingsStore? = nil
|
static var shared: UserSettingsStore? = nil
|
||||||
static var bool_options = Set<String>()
|
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)
|
@StringSetting(key: "default_wallet", default_value: .system_default_wallet)
|
||||||
var default_wallet: Wallet
|
var default_wallet: Wallet
|
||||||
|
|
||||||
@@ -312,6 +320,12 @@ class UserSettingsStore: ObservableObject {
|
|||||||
return internal_winetranslate_api_key != nil
|
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 {
|
func pk_setting_key(_ pubkey: Pubkey, key: String) -> String {
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
//
|
||||||
|
// VideoCache.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Daniel D'Aquino on 2024-04-01.
|
||||||
|
//
|
||||||
|
import Foundation
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
|
// Default expiry time of only 1 day to prevent using too much storage
|
||||||
|
fileprivate let DEFAULT_EXPIRY_TIME: TimeInterval = 60*60*24
|
||||||
|
// Default cache directory is in the system-provided caches directory, so that the operating system can delete files when it needs storage space
|
||||||
|
// (https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html)
|
||||||
|
fileprivate let DEFAULT_CACHE_DIRECTORY_PATH: URL? = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("video_cache")
|
||||||
|
|
||||||
|
struct VideoCache {
|
||||||
|
private let cache_url: URL
|
||||||
|
private let expiry_time: TimeInterval
|
||||||
|
static let standard: VideoCache? = try? VideoCache()
|
||||||
|
|
||||||
|
init?(cache_url: URL? = nil, expiry_time: TimeInterval = DEFAULT_EXPIRY_TIME) throws {
|
||||||
|
guard let cache_url_to_apply = cache_url ?? DEFAULT_CACHE_DIRECTORY_PATH else { return nil }
|
||||||
|
self.cache_url = cache_url_to_apply
|
||||||
|
self.expiry_time = expiry_time
|
||||||
|
|
||||||
|
// Create the cache directory if it doesn't exist
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(at: self.cache_url, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch {
|
||||||
|
Log.error("Could not create cache directory: %s", for: .storage, error.localizedDescription)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for a cached video and returns its URL if available, otherwise downloads and caches the video.
|
||||||
|
func maybe_cached_url_for(video_url: URL) throws -> URL {
|
||||||
|
let cached_url = url_to_cached_url(url: video_url)
|
||||||
|
|
||||||
|
if FileManager.default.fileExists(atPath: cached_url.path) {
|
||||||
|
// Check if the cached video has expired
|
||||||
|
let file_attributes = try FileManager.default.attributesOfItem(atPath: cached_url.path)
|
||||||
|
if let modification_date = file_attributes[.modificationDate] as? Date, Date().timeIntervalSince(modification_date) <= expiry_time {
|
||||||
|
// Video is not expired
|
||||||
|
return cached_url
|
||||||
|
} else {
|
||||||
|
Task {
|
||||||
|
// Video is expired, delete and re-download on the background
|
||||||
|
try FileManager.default.removeItem(at: cached_url)
|
||||||
|
return try await download_and_cache_video(from: video_url)
|
||||||
|
}
|
||||||
|
return video_url
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Task {
|
||||||
|
// Video is not cached, download and cache on the background
|
||||||
|
return try await download_and_cache_video(from: video_url)
|
||||||
|
}
|
||||||
|
return video_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downloads video content using URLSession and caches it to disk.
|
||||||
|
private func download_and_cache_video(from url: URL) async throws -> URL {
|
||||||
|
let (data, response) = try await URLSession.shared.data(from: url)
|
||||||
|
|
||||||
|
guard let http_response = response as? HTTPURLResponse,
|
||||||
|
200..<300 ~= http_response.statusCode else {
|
||||||
|
throw URLError(.badServerResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
let destination_url = url_to_cached_url(url: url)
|
||||||
|
|
||||||
|
try data.write(to: destination_url)
|
||||||
|
return destination_url
|
||||||
|
}
|
||||||
|
|
||||||
|
func url_to_cached_url(url: URL) -> URL {
|
||||||
|
let hashed_url = hash_url(url)
|
||||||
|
let file_extension = url.pathExtension
|
||||||
|
return self.cache_url.appendingPathComponent(hashed_url + "." + file_extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes all cached videos older than the expiry time.
|
||||||
|
func periodic_purge(completion: ((Error?) -> Void)? = nil) {
|
||||||
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
Log.info("Starting periodic video cache purge", for: .storage)
|
||||||
|
let file_manager = FileManager.default
|
||||||
|
do {
|
||||||
|
let cached_files = try file_manager.contentsOfDirectory(at: self.cache_url, includingPropertiesForKeys: [.contentModificationDateKey], options: .skipsHiddenFiles)
|
||||||
|
|
||||||
|
for file in cached_files {
|
||||||
|
let attributes = try file.resourceValues(forKeys: [.contentModificationDateKey])
|
||||||
|
if let modification_date = attributes.contentModificationDate, Date().timeIntervalSince(modification_date) > self.expiry_time {
|
||||||
|
try file_manager.removeItem(at: file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion?(nil)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion?(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hashes the URL using SHA-256
|
||||||
|
private func hash_url(_ url: URL) -> String {
|
||||||
|
let data = Data(url.absoluteString.utf8)
|
||||||
|
let hashed_data = SHA256.hash(data: data)
|
||||||
|
return hashed_data.compactMap { String(format: "%02x", $0) }.joined()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -227,9 +227,7 @@ class RelayPool {
|
|||||||
request_queue.append(QueuedRequest(req: r, relay: relay, skip_ephemeral: skip_ephemeral))
|
request_queue.append(QueuedRequest(req: r, relay: relay, skip_ephemeral: skip_ephemeral))
|
||||||
}
|
}
|
||||||
|
|
||||||
func send_raw(_ req: NostrRequestType, to: [RelayURL]? = nil, skip_ephemeral: Bool = true) {
|
func send_raw_to_local_ndb(_ req: NostrRequestType) {
|
||||||
let relays = to.map{ get_relays($0) } ?? self.relays
|
|
||||||
|
|
||||||
// send to local relay (nostrdb)
|
// send to local relay (nostrdb)
|
||||||
switch req {
|
switch req {
|
||||||
case .typical(let r):
|
case .typical(let r):
|
||||||
@@ -239,6 +237,12 @@ class RelayPool {
|
|||||||
case .custom(let string):
|
case .custom(let string):
|
||||||
let _ = ndb.process_client_event(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
|
||||||
|
|
||||||
|
self.send_raw_to_local_ndb(req)
|
||||||
|
|
||||||
for relay in relays {
|
for relay in relays {
|
||||||
if req.is_read && !(relay.descriptor.info.read ?? true) {
|
if req.is_read && !(relay.descriptor.info.read ?? true) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class CompatibleText: Equatable {
|
|||||||
return AnyView(
|
return AnyView(
|
||||||
VStack {
|
VStack {
|
||||||
Image("warning")
|
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)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|||||||
@@ -240,6 +240,11 @@ func should_translate(event: NostrEvent, our_keypair: Keypair, settings: UserSet
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't translate reposts, longform, etc
|
||||||
|
if event.kind != 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not translate self-authored notes if logged in with a private key
|
// Do not translate self-authored notes if logged in with a private key
|
||||||
// as we can assume the user can understand their own notes.
|
// as we can assume the user can understand their own notes.
|
||||||
// The detected language prediction could be incorrect and not in the list of preferred languages.
|
// The detected language prediction could be incorrect and not in the list of preferred languages.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func processImage(image: UIImage) -> URL? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func processImage(source: CGImageSource, fileExtension: String) -> URL? {
|
fileprivate func processImage(source: CGImageSource, fileExtension: String) -> URL? {
|
||||||
let destinationURL = createMediaURL(fileExtension: fileExtension)
|
let destinationURL = generateUniqueTemporaryMediaURL(fileExtension: fileExtension)
|
||||||
|
|
||||||
guard let destination = removeGPSDataFromImage(source: source, url: destinationURL) else { return nil }
|
guard let destination = removeGPSDataFromImage(source: source, url: destinationURL) else { return nil }
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ func processVideo(videoURL: URL) -> URL? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func saveVideoToTemporaryFolder(videoURL: URL) -> URL? {
|
fileprivate func saveVideoToTemporaryFolder(videoURL: URL) -> URL? {
|
||||||
let destinationURL = createMediaURL(fileExtension: videoURL.pathExtension)
|
let destinationURL = generateUniqueTemporaryMediaURL(fileExtension: videoURL.pathExtension)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try FileManager.default.copyItem(at: videoURL, to: destinationURL)
|
try FileManager.default.copyItem(at: videoURL, to: destinationURL)
|
||||||
@@ -57,7 +57,7 @@ fileprivate func saveVideoToTemporaryFolder(videoURL: URL) -> URL? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a temporary URL with a unique filename
|
/// Generate a temporary URL with a unique filename
|
||||||
fileprivate func createMediaURL(fileExtension: String) -> URL {
|
func generateUniqueTemporaryMediaURL(fileExtension: String) -> URL {
|
||||||
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
||||||
let uniqueMediaName = "\(UUID().uuidString).\(fileExtension)"
|
let uniqueMediaName = "\(UUID().uuidString).\(fileExtension)"
|
||||||
let temporaryMediaURL = temporaryDirectoryURL.appendingPathComponent(uniqueMediaName)
|
let temporaryMediaURL = temporaryDirectoryURL.appendingPathComponent(uniqueMediaName)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ enum LogCategory: String {
|
|||||||
case storage
|
case storage
|
||||||
case push_notifications
|
case push_notifications
|
||||||
case damus_purple
|
case damus_purple
|
||||||
|
case image_uploading
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Damus structured logger
|
/// Damus structured logger
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ enum Route: Hashable {
|
|||||||
case ReactionsSettings(settings: UserSettingsStore)
|
case ReactionsSettings(settings: UserSettingsStore)
|
||||||
case SearchSettings(settings: UserSettingsStore)
|
case SearchSettings(settings: UserSettingsStore)
|
||||||
case DeveloperSettings(settings: UserSettingsStore)
|
case DeveloperSettings(settings: UserSettingsStore)
|
||||||
|
case FirstAidSettings(settings: UserSettingsStore)
|
||||||
case Thread(thread: ThreadModel)
|
case Thread(thread: ThreadModel)
|
||||||
case Reposts(reposts: EventsModel)
|
case Reposts(reposts: EventsModel)
|
||||||
case QuoteReposts(quotes: EventsModel)
|
case QuoteReposts(quotes: EventsModel)
|
||||||
@@ -89,6 +90,8 @@ enum Route: Hashable {
|
|||||||
SearchSettingsView(settings: settings)
|
SearchSettingsView(settings: settings)
|
||||||
case .DeveloperSettings(let settings):
|
case .DeveloperSettings(let settings):
|
||||||
DeveloperSettingsView(settings: settings)
|
DeveloperSettingsView(settings: settings)
|
||||||
|
case .FirstAidSettings(settings: let settings):
|
||||||
|
FirstAidSettingsView(damus_state: damusState, settings: settings)
|
||||||
case .Thread(let thread):
|
case .Thread(let thread):
|
||||||
ThreadView(state: damusState, thread: thread)
|
ThreadView(state: damusState, thread: thread)
|
||||||
case .Reposts(let reposts):
|
case .Reposts(let reposts):
|
||||||
@@ -175,6 +178,8 @@ enum Route: Hashable {
|
|||||||
hasher.combine("searchSettings")
|
hasher.combine("searchSettings")
|
||||||
case .DeveloperSettings:
|
case .DeveloperSettings:
|
||||||
hasher.combine("developerSettings")
|
hasher.combine("developerSettings")
|
||||||
|
case .FirstAidSettings:
|
||||||
|
hasher.combine("firstAidSettings")
|
||||||
case .Thread(let threadModel):
|
case .Thread(let threadModel):
|
||||||
hasher.combine("thread")
|
hasher.combine("thread")
|
||||||
hasher.combine(threadModel.event.id)
|
hasher.combine(threadModel.event.id)
|
||||||
|
|||||||
@@ -411,9 +411,6 @@ func get_zap_description(_ ev: NostrEvent, inv_desc: InvoiceDescription) -> Stri
|
|||||||
guard let data = desc.data(using: .utf8) else {
|
guard let data = desc.data(using: .utf8) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard sha256(data) == deschash else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return desc
|
return desc
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import MCEmojiPicker
|
||||||
|
|
||||||
|
|
||||||
struct EventActionBar: View {
|
struct EventActionBar: View {
|
||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
@@ -20,6 +19,8 @@ struct EventActionBar: View {
|
|||||||
@State var show_share_action: Bool = false
|
@State var show_share_action: Bool = false
|
||||||
@State var show_repost_action: Bool = false
|
@State var show_repost_action: Bool = false
|
||||||
|
|
||||||
|
@State private var isOnTopHalfOfScreen: Bool = false
|
||||||
|
|
||||||
@ObservedObject var bar: ActionBarModel
|
@ObservedObject var bar: ActionBarModel
|
||||||
|
|
||||||
init(damus_state: DamusState, event: NostrEvent, bar: ActionBarModel? = nil) {
|
init(damus_state: DamusState, event: NostrEvent, bar: ActionBarModel? = nil) {
|
||||||
@@ -72,7 +73,7 @@ struct EventActionBar: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
LikeButton(damus_state: damus_state, liked: bar.liked, liked_emoji: bar.our_like != nil ? to_reaction_emoji(ev: bar.our_like!) : nil) { emoji in
|
LikeButton(damus_state: damus_state, liked: bar.liked, liked_emoji: bar.our_like != nil ? to_reaction_emoji(ev: bar.our_like!) : nil, isOnTopHalfOfScreen: $isOnTopHalfOfScreen) { emoji in
|
||||||
if bar.liked {
|
if bar.liked {
|
||||||
//notify(.delete, bar.our_like)
|
//notify(.delete, bar.our_like)
|
||||||
} else {
|
} else {
|
||||||
@@ -135,6 +136,20 @@ struct EventActionBar: View {
|
|||||||
self.bar.our_like = liked.event
|
self.bar.our_like = liked.event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.background(
|
||||||
|
GeometryReader { geometry in
|
||||||
|
EmptyView()
|
||||||
|
.onAppear {
|
||||||
|
let eventActionBarY = geometry.frame(in: .global).midY
|
||||||
|
let screenMidY = UIScreen.main.bounds.midY
|
||||||
|
self.isOnTopHalfOfScreen = eventActionBarY > screenMidY
|
||||||
|
}
|
||||||
|
.onChange(of: geometry.frame(in: .global).midY) { newY in
|
||||||
|
let screenMidY = UIScreen.main.bounds.midY
|
||||||
|
self.isOnTopHalfOfScreen = newY > screenMidY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func send_like(emoji: String) {
|
func send_like(emoji: String) {
|
||||||
@@ -168,15 +183,17 @@ struct LikeButton: View {
|
|||||||
let damus_state: DamusState
|
let damus_state: DamusState
|
||||||
let liked: Bool
|
let liked: Bool
|
||||||
let liked_emoji: String?
|
let liked_emoji: String?
|
||||||
|
@Binding var isOnTopHalfOfScreen: Bool
|
||||||
let action: (_ emoji: String) -> Void
|
let action: (_ emoji: String) -> Void
|
||||||
|
|
||||||
// For reactions background
|
// For reactions background
|
||||||
@State private var showReactionsBG = 0
|
@State private var showReactionsBG = 0
|
||||||
@State private var showEmojis: [Int] = []
|
|
||||||
@State private var rotateThumb = -45
|
@State private var rotateThumb = -45
|
||||||
|
|
||||||
@State private var isReactionsVisible = false
|
@State private var isReactionsVisible = false
|
||||||
|
|
||||||
|
@State private var selectedEmoji: String = ""
|
||||||
|
|
||||||
// Following four are Shaka animation properties
|
// Following four are Shaka animation properties
|
||||||
let timer = Timer.publish(every: 0.10, on: .main, in: .common).autoconnect()
|
let timer = Timer.publish(every: 0.10, on: .main, in: .common).autoconnect()
|
||||||
@State private var shouldAnimate = false
|
@State private var shouldAnimate = false
|
||||||
@@ -228,7 +245,15 @@ struct LikeButton: View {
|
|||||||
amountOfAngleIncrease = 20.0
|
amountOfAngleIncrease = 20.0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.overlay(reactionsOverlay())
|
.emojiPicker(
|
||||||
|
isPresented: $isReactionsVisible,
|
||||||
|
selectedEmoji: $selectedEmoji,
|
||||||
|
arrowDirection: isOnTopHalfOfScreen ? .down : .up,
|
||||||
|
isDismissAfterChoosing: true
|
||||||
|
)
|
||||||
|
.onChange(of: selectedEmoji) { newSelectedEmoji in
|
||||||
|
self.action(newSelectedEmoji)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shakaAnimationLogic() {
|
func shakaAnimationLogic() {
|
||||||
@@ -251,110 +276,11 @@ struct LikeButton: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reactionsOverlay() -> some View {
|
|
||||||
Group {
|
|
||||||
if isReactionsVisible {
|
|
||||||
ZStack {
|
|
||||||
RoundedRectangle(cornerRadius: 20)
|
|
||||||
.frame(width: calculateOverlayWidth(), height: 50)
|
|
||||||
.foregroundColor(DamusColors.black)
|
|
||||||
.scaleEffect(Double(showReactionsBG), anchor: .topTrailing)
|
|
||||||
.animation(
|
|
||||||
.interpolatingSpring(stiffness: 170, damping: 15).delay(0.05),
|
|
||||||
value: showReactionsBG
|
|
||||||
)
|
|
||||||
.overlay(
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(Color.white.opacity(0.2))
|
|
||||||
.frame(width: calculateOverlayWidth(), height: 50)
|
|
||||||
.clipShape(
|
|
||||||
RoundedRectangle(cornerRadius: 20)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.overlay(reactions())
|
|
||||||
}
|
|
||||||
.offset(y: -40)
|
|
||||||
.onTapGesture {
|
|
||||||
withAnimation(.easeOut(duration: 0.2)) {
|
|
||||||
isReactionsVisible = false
|
|
||||||
showReactionsBG = 0
|
|
||||||
}
|
|
||||||
showEmojis = []
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateOverlayWidth() -> CGFloat {
|
|
||||||
let maxWidth: CGFloat = 250
|
|
||||||
let numberOfEmojis = emojis.count
|
|
||||||
let minimumWidth: CGFloat = 75
|
|
||||||
|
|
||||||
if numberOfEmojis > 0 {
|
|
||||||
let emojiWidth: CGFloat = 25
|
|
||||||
let padding: CGFloat = 15
|
|
||||||
let buttonWidth: CGFloat = 18
|
|
||||||
let buttonPadding: CGFloat = 20
|
|
||||||
|
|
||||||
let totalWidth = CGFloat(numberOfEmojis) * (emojiWidth + padding) + buttonWidth + buttonPadding
|
|
||||||
return min(maxWidth, max(minimumWidth, totalWidth))
|
|
||||||
} else {
|
|
||||||
return minimumWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func reactions() -> some View {
|
|
||||||
HStack {
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
|
||||||
HStack(spacing: 15) {
|
|
||||||
ForEach(emojis, id: \.self) { emoji in
|
|
||||||
if let index = emojis.firstIndex(of: emoji) {
|
|
||||||
let scale = index < showEmojis.count ? showEmojis[index] : 0
|
|
||||||
Text(emoji)
|
|
||||||
.font(.system(size: 25))
|
|
||||||
.scaleEffect(Double(scale))
|
|
||||||
.onTapGesture {
|
|
||||||
emojiTapped(emoji)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.leading, 10)
|
|
||||||
}
|
|
||||||
Button(action: {
|
|
||||||
withAnimation(.easeOut(duration: 0.2)) {
|
|
||||||
isReactionsVisible = false
|
|
||||||
showReactionsBG = 0
|
|
||||||
}
|
|
||||||
showEmojis = []
|
|
||||||
}) {
|
|
||||||
Image(systemName: "xmark.circle.fill")
|
|
||||||
.font(.system(size: 18))
|
|
||||||
.foregroundColor(.gray)
|
|
||||||
}
|
|
||||||
.padding(.trailing, 7.5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When reaction button is long pressed, it displays the multiple emojis overlay and displays the user's selected emojis with an animation
|
// When reaction button is long pressed, it displays the multiple emojis overlay and displays the user's selected emojis with an animation
|
||||||
private func reactionLongPressed() {
|
private func reactionLongPressed() {
|
||||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||||
showEmojis = Array(repeating: 0, count: emojis.count) // Initialize the showEmojis array
|
|
||||||
|
|
||||||
for (index, _) in emojis.enumerated() {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
|
|
||||||
withAnimation(.interpolatingSpring(stiffness: 170, damping: 8)) {
|
|
||||||
if index < showEmojis.count {
|
|
||||||
showEmojis[index] = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isReactionsVisible = true
|
isReactionsVisible = true
|
||||||
showReactionsBG = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func emojiTapped(_ emoji: String) {
|
private func emojiTapped(_ emoji: String) {
|
||||||
@@ -364,9 +290,7 @@ struct LikeButton: View {
|
|||||||
|
|
||||||
withAnimation(.easeOut(duration: 0.2)) {
|
withAnimation(.easeOut(duration: 0.2)) {
|
||||||
isReactionsVisible = false
|
isReactionsVisible = false
|
||||||
showReactionsBG = 0
|
|
||||||
}
|
}
|
||||||
showEmojis = []
|
|
||||||
|
|
||||||
withAnimation(Animation.easeOut(duration: 0.15)) {
|
withAnimation(Animation.easeOut(duration: 0.15)) {
|
||||||
shouldAnimate = true
|
shouldAnimate = true
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ struct ShareActionButton: View {
|
|||||||
.frame(width: 55.0, height: 55.0)
|
.frame(width: 55.0, height: 55.0)
|
||||||
}
|
}
|
||||||
.frame(height: 25)
|
.frame(height: 25)
|
||||||
Text(verbatim: text)
|
Text(text)
|
||||||
.foregroundColor(col)
|
.foregroundColor(col)
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ struct AddRelayView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text(verbatim: "Add relay")
|
Text("Add relay", comment: "Button to add a relay.")
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ enum ImageUploadResult {
|
|||||||
|
|
||||||
fileprivate func create_upload_body(mediaData: Data, boundary: String, mediaUploader: MediaUploader, mediaToUpload: MediaUpload) -> Data {
|
fileprivate func create_upload_body(mediaData: Data, boundary: String, mediaUploader: MediaUploader, mediaToUpload: MediaUpload) -> Data {
|
||||||
let body = NSMutableData();
|
let body = NSMutableData();
|
||||||
let contentType = mediaToUpload.is_image ? "image/jpg" : "video/mp4"
|
let contentType = mediaToUpload.mime_type
|
||||||
body.appendString(string: "Content-Type: multipart/form-data; boundary=\(boundary)\r\n\r\n")
|
body.appendString(string: "Content-Type: multipart/form-data; boundary=\(boundary)\r\n\r\n")
|
||||||
body.appendString(string: "--\(boundary)\r\n")
|
body.appendString(string: "--\(boundary)\r\n")
|
||||||
body.appendString(string: "Content-Disposition: form-data; name=\(mediaUploader.nameParam); filename=\(mediaToUpload.genericFileName)\r\n")
|
body.appendString(string: "Content-Disposition: form-data; name=\(mediaUploader.nameParam); filename=\(mediaToUpload.genericFileName)\r\n")
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ struct BookmarksView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.frame(width: 32.0, height: 32.0)
|
.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 {
|
} else {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ struct GradientFollowButton: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
follow_state = perform_follow_btn_action(follow_state, target: target)
|
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)
|
.foregroundColor(follow_state == .unfollows ? .white : grayTextColor)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
.fontWeight(.medium)
|
.fontWeight(.medium)
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ struct ConfigView: View {
|
|||||||
NavigationLink(value: Route.DeveloperSettings(settings: settings)) {
|
NavigationLink(value: Route.DeveloperSettings(settings: settings)) {
|
||||||
IconLabel(NSLocalizedString("Developer", comment: "Section header for developer settings"), img_name: "magic-stick2.fill", color: DamusColors.adaptableBlack)
|
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")) {
|
Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) {
|
||||||
@@ -84,7 +88,7 @@ struct ConfigView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if state.is_privkey_user {
|
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: {
|
Button(action: {
|
||||||
delete_account_warning = true
|
delete_account_warning = true
|
||||||
}, label: {
|
}, label: {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct CreateAccountView: View {
|
|||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
EditPictureControl(uploader: .nostrBuild, pubkey: account.pubkey, image_url: $account.profile_image , uploadObserver: profileUploadObserver, callback: uploadedProfilePicture)
|
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()
|
.bold()
|
||||||
.padding()
|
.padding()
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,7 @@ struct ProxyView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
let protocolLogo = get_protocol_image(protocolName: proxy.protocolName)
|
let protocolLogo = get_protocol_image(protocolName: proxy.protocolName)
|
||||||
if protocolLogo.isEmpty {
|
if protocolLogo.isEmpty {
|
||||||
Text("\(proxy.protocolName)")
|
Text(proxy.protocolName)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
} else {
|
} else {
|
||||||
Image(protocolLogo)
|
Image(protocolLogo)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct EventBody: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if event.known_kind == .longform {
|
if event.known_kind == .longform {
|
||||||
LongformPreviewBody(state: damus_state, ev: event, options: options)
|
LongformPreviewBody(state: damus_state, ev: event, options: options, header: true)
|
||||||
|
|
||||||
// truncated longform bodies are just the preview
|
// truncated longform bodies are just the preview
|
||||||
if !options.contains(.truncate_content) {
|
if !options.contains(.truncate_content) {
|
||||||
|
|||||||
@@ -6,25 +6,31 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
struct LongformPreviewBody: View {
|
struct LongformPreviewBody: View {
|
||||||
let state: DamusState
|
let state: DamusState
|
||||||
let event: LongformEvent
|
let event: LongformEvent
|
||||||
let options: EventViewOptions
|
let options: EventViewOptions
|
||||||
|
let header: Bool
|
||||||
|
@State var blur_images: Bool = true
|
||||||
|
|
||||||
@ObservedObject var artifacts: NoteArtifactsModel
|
@ObservedObject var artifacts: NoteArtifactsModel
|
||||||
|
|
||||||
init(state: DamusState, ev: LongformEvent, options: EventViewOptions) {
|
init(state: DamusState, ev: LongformEvent, options: EventViewOptions, header: Bool) {
|
||||||
self.state = state
|
self.state = state
|
||||||
self.event = ev
|
self.event = ev
|
||||||
self.options = options
|
self.options = options
|
||||||
|
self.header = header
|
||||||
|
|
||||||
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.event.id).artifacts_model)
|
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.event.id).artifacts_model)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(state: DamusState, ev: NostrEvent, options: EventViewOptions) {
|
init(state: DamusState, ev: NostrEvent, options: EventViewOptions, header: Bool) {
|
||||||
self.state = state
|
self.state = state
|
||||||
self.event = LongformEvent.parse(from: ev)
|
self.event = LongformEvent.parse(from: ev)
|
||||||
self.options = options
|
self.options = options
|
||||||
|
self.header = header
|
||||||
|
|
||||||
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.id).artifacts_model)
|
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.id).artifacts_model)
|
||||||
}
|
}
|
||||||
@@ -34,6 +40,67 @@ struct LongformPreviewBody: View {
|
|||||||
return Text(wordCount)
|
return Text(wordCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var truncate: Bool {
|
||||||
|
return options.contains(.truncate_content)
|
||||||
|
}
|
||||||
|
|
||||||
|
var truncate_very_short: Bool {
|
||||||
|
return options.contains(.truncate_content_very_short)
|
||||||
|
}
|
||||||
|
|
||||||
|
func truncatedText(content: CompatibleText) -> some View {
|
||||||
|
Group {
|
||||||
|
if truncate_very_short {
|
||||||
|
TruncatedText(text: content, maxChars: 140)
|
||||||
|
.font(header ? .body : .caption)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
}
|
||||||
|
else if truncate {
|
||||||
|
TruncatedText(text: content)
|
||||||
|
.font(header ? .body : .caption)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
} else {
|
||||||
|
content.text
|
||||||
|
.font(header ? .body : .caption)
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Placeholder(url: URL) -> some View {
|
||||||
|
Group {
|
||||||
|
if let meta = state.events.lookup_img_metadata(url: url),
|
||||||
|
case .processed(let blurhash) = meta.state {
|
||||||
|
Image(uiImage: blurhash)
|
||||||
|
.resizable()
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: header ? .infinity : 150)
|
||||||
|
} else {
|
||||||
|
DamusColors.adaptableWhite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func titleImage(url: URL) -> some View {
|
||||||
|
KFAnimatedImage(url)
|
||||||
|
.callbackQueue(.dispatch(.global(qos:.background)))
|
||||||
|
.backgroundDecode(true)
|
||||||
|
.imageContext(.note, disable_animation: state.settings.disable_animation)
|
||||||
|
.image_fade(duration: 0.25)
|
||||||
|
.cancelOnDisappear(true)
|
||||||
|
.configure { view in
|
||||||
|
view.framePreloadCount = 3
|
||||||
|
}
|
||||||
|
.background {
|
||||||
|
Placeholder(url: url)
|
||||||
|
}
|
||||||
|
.aspectRatio(contentMode: .fill)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: header ? .infinity : 150)
|
||||||
|
.cornerRadius(1)
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if options.contains(.wide) {
|
if options.contains(.wide) {
|
||||||
@@ -46,23 +113,71 @@ struct LongformPreviewBody: View {
|
|||||||
|
|
||||||
var Main: some View {
|
var Main: some View {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
if let title = event.title {
|
if let url = event.image {
|
||||||
Text(title)
|
if (self.options.contains(.no_media)) {
|
||||||
.font(.title)
|
EmptyView()
|
||||||
} else {
|
} else if !blur_images || (!blur_images && !state.settings.media_previews) {
|
||||||
Text("Untitled", comment: "Text indicating that the long-form note title is untitled.")
|
titleImage(url: url)
|
||||||
.font(.title)
|
} else if blur_images || (blur_images && !state.settings.media_previews) {
|
||||||
|
ZStack {
|
||||||
|
titleImage(url: url)
|
||||||
|
Blur()
|
||||||
|
.onTapGesture {
|
||||||
|
blur_images = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(event.summary ?? "")
|
Text(event.title ?? "Untitled")
|
||||||
|
.font(header ? .title : .headline)
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
.padding(.top, 5)
|
||||||
|
|
||||||
|
if let summary = event.summary {
|
||||||
|
truncatedText(content: CompatibleText(stringLiteral: summary))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let labels = event.labels {
|
||||||
|
ScrollView(.horizontal) {
|
||||||
|
HStack {
|
||||||
|
ForEach(labels, id: \.self) { label in
|
||||||
|
Text(label)
|
||||||
|
.font(.caption)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
|
.padding(EdgeInsets(top: 5, leading: 15, bottom: 5, trailing: 15))
|
||||||
|
.background(DamusColors.neutral1)
|
||||||
|
.cornerRadius(20)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 20)
|
||||||
|
.stroke(DamusColors.neutral3, lineWidth: 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.scrollIndicators(.hidden)
|
||||||
|
.padding(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if case .loaded(let arts) = artifacts.state,
|
if case .loaded(let arts) = artifacts.state,
|
||||||
case .longform(let longform) = arts
|
case .longform(let longform) = arts
|
||||||
{
|
{
|
||||||
Words(longform.words).font(.footnote)
|
Words(longform.words).font(.footnote)
|
||||||
|
.padding([.horizontal, .bottom], 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(DamusColors.neutral3)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.stroke(DamusColors.neutral1, lineWidth: 1)
|
||||||
|
)
|
||||||
|
.padding(.top, 10)
|
||||||
|
.onAppear {
|
||||||
|
blur_images = should_blur_images(settings: state.settings, contacts: state.contacts, ev: event.event, our_pubkey: state.pubkey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +194,7 @@ struct LongformPreview: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
EventShell(state: state, event: event.event, options: options) {
|
EventShell(state: state, event: event.event, options: options) {
|
||||||
LongformPreviewBody(state: state, ev: event, options: options)
|
LongformPreviewBody(state: state, ev: event, options: options, header: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ struct FullScreenCarouselView_Previews: PreviewProvider {
|
|||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text("Some content")
|
Text(verbatim: "Some content")
|
||||||
.padding()
|
.padding()
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,29 @@ struct MediaPicker: UIViewControllerRepresentable {
|
|||||||
result.itemProvider.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { (item, error) in
|
result.itemProvider.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { (item, error) in
|
||||||
guard let url = item as? URL else { return }
|
guard let url = item as? URL else { return }
|
||||||
|
|
||||||
if canGetSourceTypeFromUrl(url: url) {
|
if(url.pathExtension == "gif") {
|
||||||
|
// GIFs do not natively support location metadata (See https://superuser.com/a/556320 and https://www.w3.org/Graphics/GIF/spec-gif89a.txt)
|
||||||
|
// It is better to avoid any GPS data processing at all, as it can cause the image to be converted to JPEG.
|
||||||
|
// Therefore, we should load the file directtly and deliver it as "already processed".
|
||||||
|
|
||||||
|
// Load the data for the GIF image
|
||||||
|
// - Don't load it as an UIImage since that can only get exported into JPEG/PNG
|
||||||
|
// - Don't load it as a file representation because it gets deleted before the upload can occur
|
||||||
|
_ = result.itemProvider.loadDataRepresentation(for: .gif, completionHandler: { imageData, error in
|
||||||
|
guard let imageData else { return }
|
||||||
|
let destinationURL = generateUniqueTemporaryMediaURL(fileExtension: "gif")
|
||||||
|
do {
|
||||||
|
try imageData.write(to: destinationURL)
|
||||||
|
Task {
|
||||||
|
await self.chooseMedia(.processed_image(destinationURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Log.error("Failed to write GIF image data from Photo picker into a local copy", for: .image_uploading)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if canGetSourceTypeFromUrl(url: url) {
|
||||||
// Media was not taken from camera
|
// Media was not taken from camera
|
||||||
self.attemptAcquireResourceAndChooseMedia(
|
self.attemptAcquireResourceAndChooseMedia(
|
||||||
url: url,
|
url: url,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ struct MuteDurationMenu<T: View>: View {
|
|||||||
Button {
|
Button {
|
||||||
action(duration)
|
action(duration)
|
||||||
} label: {
|
} label: {
|
||||||
Text("\(duration.title)")
|
Text(duration.title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
@@ -30,6 +30,6 @@ struct MuteDurationMenu<T: View>: View {
|
|||||||
MuteDurationMenu { _ in
|
MuteDurationMenu { _ in
|
||||||
|
|
||||||
} label: {
|
} label: {
|
||||||
Text("Mute hashtag")
|
Text(verbatim: "Mute hashtag")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ struct MutelistView: View {
|
|||||||
Section(NSLocalizedString("Hashtags", comment: "Section header title for a list of hashtags that are muted.")) {
|
Section(NSLocalizedString("Hashtags", comment: "Section header title for a list of hashtags that are muted.")) {
|
||||||
ForEach(hashtags, id: \.self) { item in
|
ForEach(hashtags, id: \.self) { item in
|
||||||
if case let MuteItem.hashtag(hashtag, _) = item {
|
if case let MuteItem.hashtag(hashtag, _) = item {
|
||||||
Text("#\(hashtag.hashtag)")
|
Text(verbatim: "#\(hashtag.hashtag)")
|
||||||
.id(hashtag.hashtag)
|
.id(hashtag.hashtag)
|
||||||
.swipeActions {
|
.swipeActions {
|
||||||
RemoveAction(item: .hashtag(hashtag, nil))
|
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.")) {
|
Section(NSLocalizedString("Words", comment: "Section header title for a list of words that are muted.")) {
|
||||||
ForEach(words, id: \.self) { item in
|
ForEach(words, id: \.self) { item in
|
||||||
if case let MuteItem.word(word, _) = item {
|
if case let MuteItem.word(word, _) = item {
|
||||||
Text("\(word)")
|
Text(word)
|
||||||
.id(word)
|
.id(word)
|
||||||
.swipeActions {
|
.swipeActions {
|
||||||
RemoveAction(item: .word(word, nil))
|
RemoveAction(item: .word(word, nil))
|
||||||
@@ -94,7 +94,7 @@ struct MutelistView: View {
|
|||||||
RemoveAction(item: .thread(note_id, nil))
|
RemoveAction(item: .thread(note_id, nil))
|
||||||
}
|
}
|
||||||
} else {
|
} 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.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ struct DamusAppNotificationView: View {
|
|||||||
.shadow(radius: 5, y: 5)
|
.shadow(radius: 5, y: 5)
|
||||||
VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
HStack(alignment: .center, spacing: 3) {
|
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))
|
.font(.body.weight(.bold))
|
||||||
Text("·")
|
Text(verbatim: "·")
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
Text(relative_date)
|
Text(relative_date)
|
||||||
.font(.system(size: 16))
|
.font(.system(size: 16))
|
||||||
@@ -49,7 +49,7 @@ struct DamusAppNotificationView: View {
|
|||||||
Image("check-circle.fill")
|
Image("check-circle.fill")
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 15, height: 15)
|
.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)
|
.font(.caption2)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ struct EventGroupView: View {
|
|||||||
return VStack(alignment: .center) {
|
return VStack(alignment: .center) {
|
||||||
Image("zap.fill")
|
Image("zap.fill")
|
||||||
.foregroundColor(.orange)
|
.foregroundColor(.orange)
|
||||||
Text(verbatim: fmt)
|
Text(fmt)
|
||||||
.foregroundColor(Color.orange)
|
.foregroundColor(Color.orange)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ struct OnboardingSuggestionsView: View {
|
|||||||
.navigationBarItems(leading: Button(action: {
|
.navigationBarItems(leading: Button(action: {
|
||||||
self.next_page()
|
self.next_page()
|
||||||
}, label: {
|
}, 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))
|
.font(.subheadline.weight(.semibold))
|
||||||
}))
|
}))
|
||||||
.tag(0)
|
.tag(0)
|
||||||
@@ -48,7 +48,7 @@ struct OnboardingSuggestionsView: View {
|
|||||||
AnyView(
|
AnyView(
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "sparkles")
|
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)
|
.foregroundColor(.secondary)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
@@ -97,7 +97,7 @@ fileprivate struct SuggestedUsersPageView: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
self.next_page()
|
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)
|
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
||||||
}
|
}
|
||||||
.buttonStyle(GradientButtonStyle())
|
.buttonStyle(GradientButtonStyle())
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ struct PostView: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
self.cancel()
|
self.cancel()
|
||||||
}, label: {
|
}, 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)
|
.padding(10)
|
||||||
})
|
})
|
||||||
.buttonStyle(NeutralButtonStyle())
|
.buttonStyle(NeutralButtonStyle())
|
||||||
|
|||||||
@@ -18,17 +18,7 @@ struct UserSearch: View {
|
|||||||
|
|
||||||
var users: [Pubkey] {
|
var users: [Pubkey] {
|
||||||
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return [] }
|
guard let txn = NdbTxn(ndb: damus_state.ndb) else { return [] }
|
||||||
return search_profiles(profiles: damus_state.profiles, search: search, txn: txn).sorted { a, b in
|
return search_profiles(profiles: damus_state.profiles, contacts: damus_state.contacts, search: search, txn: txn)
|
||||||
let aFriendTypePriority = get_friend_type(contacts: damus_state.contacts, pubkey: a)?.priority ?? 0
|
|
||||||
let bFriendTypePriority = get_friend_type(contacts: damus_state.contacts, pubkey: b)?.priority ?? 0
|
|
||||||
|
|
||||||
if aFriendTypePriority > bFriendTypePriority {
|
|
||||||
// `a` should be sorted before `b`
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func on_user_tapped(pk: Pubkey) {
|
func on_user_tapped(pk: Pubkey) {
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ struct ProfileView: View {
|
|||||||
MuteDurationMenu { duration in
|
MuteDurationMenu { duration in
|
||||||
notify(.mute(.user(profile.pubkey, duration?.date_from_now)))
|
notify(.mute(.user(profile.pubkey, duration?.date_from_now)))
|
||||||
} label: {
|
} label: {
|
||||||
Text(NSLocalizedString("Mute", comment: "Button to mute a profile."))
|
Text("Mute", comment: "Button to mute a profile.")
|
||||||
.foregroundStyle(.red)
|
.foregroundStyle(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ struct ProfileActionSheetView: View {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.buttonStyle(NeutralButtonShape.circle.style)
|
.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)
|
.foregroundStyle(.secondary)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ struct ProfileActionSheetView: View {
|
|||||||
label: {
|
label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
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")
|
Image(systemName: "arrow.up.right")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
@@ -305,9 +305,9 @@ fileprivate struct ProfileActionSheetZapButton: View {
|
|||||||
})
|
})
|
||||||
.alert(isPresented: $show_error_alert) {
|
.alert(isPresented: $show_error_alert) {
|
||||||
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() ?? ""),
|
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
|
.onChange(of: zap_state) { new_zap_state in
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ struct PubkeyView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.foregroundColor(DamusColors.green)
|
.foregroundColor(DamusColors.green)
|
||||||
.frame(width: 20, height: 20)
|
.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)
|
.font(.footnote)
|
||||||
.layoutPriority(1)
|
.layoutPriority(1)
|
||||||
.foregroundColor(DamusColors.green)
|
.foregroundColor(DamusColors.green)
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ struct DamusPurpleAccountView: View {
|
|||||||
// TODO: Generalize this view instead of setting up dividers and paddings manually
|
// TODO: Generalize this view instead of setting up dividers and paddings manually
|
||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
Text(NSLocalizedString("Expiry date", comment: "Label for Purple subscription expiry date"))
|
Text("Expiry date", comment: "Label for Purple subscription expiry date")
|
||||||
Spacer()
|
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(.horizontal)
|
||||||
.padding(.top, 20)
|
.padding(.top, 20)
|
||||||
@@ -43,9 +44,10 @@ struct DamusPurpleAccountView: View {
|
|||||||
.padding(.vertical, 10)
|
.padding(.vertical, 10)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Text(NSLocalizedString("Account creation", comment: "Label for Purple account creation date"))
|
Text("Account creation", comment: "Label for Purple account creation date")
|
||||||
Spacer()
|
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)
|
.padding(.horizontal)
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ struct DamusPurpleAccountView: View {
|
|||||||
.padding(.vertical, 10)
|
.padding(.vertical, 10)
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Text(NSLocalizedString("Subscriber number", comment: "Label for Purple account subscriber number"))
|
Text("Subscriber number", comment: "Label for Purple account subscriber number")
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(verbatim: "#\(account.subscriber_number)")
|
Text(verbatim: "#\(account.subscriber_number)")
|
||||||
}
|
}
|
||||||
@@ -90,7 +92,7 @@ struct DamusPurpleAccountView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 15, height: 15)
|
.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)
|
.font(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
@@ -107,7 +109,7 @@ struct DamusPurpleAccountView: View {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 15, height: 15)
|
.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)
|
.font(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ struct DamusPurpleTranslationSetupView: View {
|
|||||||
.opacity(start ? 1.0 : 0.0)
|
.opacity(start ? 1.0 : 0.0)
|
||||||
.animation(.content(), value: start)
|
.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)
|
.font(.largeTitle)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
@@ -95,7 +95,7 @@ struct DamusPurpleTranslationSetupView: View {
|
|||||||
.opacity(start ? 1.0 : 0.0)
|
.opacity(start ? 1.0 : 0.0)
|
||||||
.animation(Animation.snappy(duration: 2).delay(0), value: start)
|
.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)
|
.font(.headline)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
@@ -110,7 +110,7 @@ struct DamusPurpleTranslationSetupView: View {
|
|||||||
.animation(.content(), value: start)
|
.animation(.content(), value: start)
|
||||||
.padding(.top, 10)
|
.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)
|
.lineSpacing(5)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(.white.opacity(0.8))
|
.foregroundStyle(.white.opacity(0.8))
|
||||||
@@ -125,7 +125,7 @@ struct DamusPurpleTranslationSetupView: View {
|
|||||||
}, label: {
|
}, label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -139,7 +139,7 @@ struct DamusPurpleTranslationSetupView: View {
|
|||||||
}, label: {
|
}, label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ struct DamusPurpleVerifyNpubView: View {
|
|||||||
}, label: {
|
}, label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -61,7 +61,7 @@ struct DamusPurpleVerifyNpubView: View {
|
|||||||
.buttonStyle(GradientButtonStyle())
|
.buttonStyle(GradientButtonStyle())
|
||||||
}
|
}
|
||||||
else {
|
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)
|
.frame(height: subtitle_height)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundColor(.green)
|
.foregroundColor(.green)
|
||||||
@@ -71,7 +71,7 @@ struct DamusPurpleVerifyNpubView: View {
|
|||||||
}, label: {
|
}, label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(NSLocalizedString("Continue", comment: "Prompt to user to continue"))
|
Text("Continue", comment: "Prompt to user to continue")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ struct DamusPurpleView: View, DamusPurpleStoreKitManagerDelegate {
|
|||||||
if let account_uuid {
|
if let account_uuid {
|
||||||
DamusPurpleView.IAPProductStateView(products: products, purchased: purchased, account_uuid: account_uuid, subscribe: subscribe)
|
DamusPurpleView.IAPProductStateView(products: products, purchased: purchased, account_uuid: account_uuid, subscribe: subscribe)
|
||||||
if let iap_error {
|
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)
|
.foregroundStyle(.red)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
@@ -158,7 +158,7 @@ struct DamusPurpleView: View, DamusPurpleStoreKitManagerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ManageOnWebsiteNote: some View {
|
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)
|
.font(.caption)
|
||||||
.foregroundColor(.white.opacity(0.6))
|
.foregroundColor(.white.opacity(0.6))
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct DamusPurpleWelcomeView: View {
|
|||||||
.opacity(start ? 1.0 : 0.0)
|
.opacity(start ? 1.0 : 0.0)
|
||||||
.animation(.content(), value: start)
|
.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)
|
.font(.largeTitle)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
@@ -70,7 +70,7 @@ struct DamusPurpleWelcomeView: View {
|
|||||||
.opacity(start ? 1.0 : 0.0)
|
.opacity(start ? 1.0 : 0.0)
|
||||||
.animation(Animation.snappy(duration: 2).delay(0), value: start)
|
.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)
|
.lineSpacing(5)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.foregroundStyle(.white.opacity(0.8))
|
.foregroundStyle(.white.opacity(0.8))
|
||||||
@@ -85,7 +85,7 @@ struct DamusPurpleWelcomeView: View {
|
|||||||
}, label: {
|
}, label: {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(NSLocalizedString("Continue", comment: "Prompt to user to continue"))
|
Text("Continue", comment: "Prompt to user to continue")
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ extension DamusPurpleView {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
if subscription_purchase_loading {
|
if subscription_purchase_loading {
|
||||||
HStack(spacing: 10) {
|
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)
|
.foregroundStyle(.white)
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.progressViewStyle(.circular)
|
.progressViewStyle(.circular)
|
||||||
@@ -66,7 +66,7 @@ extension DamusPurpleView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PurchasedUnmanageableView(_ purchased: PurchasedProduct) -> some View {
|
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)
|
.font(.caption)
|
||||||
.foregroundColor(.white.opacity(0.6))
|
.foregroundColor(.white.opacity(0.6))
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -76,21 +76,21 @@ extension DamusPurpleView {
|
|||||||
func PurchasedManageView(_ purchased: PurchasedProduct) -> some View {
|
func PurchasedManageView(_ purchased: PurchasedProduct) -> some View {
|
||||||
VStack(spacing: 10) {
|
VStack(spacing: 10) {
|
||||||
if SHOW_IAP_DEBUG_INFO == true {
|
if SHOW_IAP_DEBUG_INFO == true {
|
||||||
Text(NSLocalizedString("Purchased!", comment: "User purchased a subscription"))
|
Text("Purchased!", comment: "User purchased a subscription")
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
price_description(product: purchased.product)
|
price_description(product: purchased.product)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.opacity(0.65)
|
.opacity(0.65)
|
||||||
.frame(width: 200)
|
.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)
|
.font(.title2)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
Text(format_date(date: purchased.tx.purchaseDate))
|
Text(format_date(date: purchased.tx.purchaseDate))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.opacity(0.65)
|
.opacity(0.65)
|
||||||
if let expiry = purchased.tx.expirationDate {
|
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)
|
.font(.title2)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
Text(format_date(date: expiry))
|
Text(format_date(date: expiry))
|
||||||
@@ -101,7 +101,7 @@ extension DamusPurpleView {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
show_manage_subscriptions = true
|
show_manage_subscriptions = true
|
||||||
}, label: {
|
}, label: {
|
||||||
Text(NSLocalizedString("Manage", comment: "Manage the damus subscription"))
|
Text("Manage", comment: "Manage the damus subscription")
|
||||||
.padding(.horizontal, 20)
|
.padding(.horizontal, 20)
|
||||||
})
|
})
|
||||||
.buttonStyle(GradientButtonStyle())
|
.buttonStyle(GradientButtonStyle())
|
||||||
@@ -112,7 +112,7 @@ extension DamusPurpleView {
|
|||||||
|
|
||||||
func ProductsView(_ products: [Product]) -> some View {
|
func ProductsView(_ products: [Product]) -> some View {
|
||||||
VStack(spacing: 10) {
|
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())
|
.font(.callout.bold())
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
ForEach(products) { product in
|
ForEach(products) { product in
|
||||||
@@ -132,7 +132,7 @@ extension DamusPurpleView {
|
|||||||
.buttonStyle(GradientButtonStyle())
|
.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))
|
.foregroundColor(.white.opacity(0.6))
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.padding()
|
.padding()
|
||||||
@@ -148,11 +148,11 @@ extension DamusPurpleView {
|
|||||||
Text(purple_type?.label() ?? product.displayName)
|
Text(purple_type?.label() ?? product.displayName)
|
||||||
Spacer()
|
Spacer()
|
||||||
if let non_discounted_price = purple_type?.non_discounted_price(product: product) {
|
if let non_discounted_price = purple_type?.non_discounted_price(product: product) {
|
||||||
Text(verbatim: non_discounted_price)
|
Text(non_discounted_price)
|
||||||
.strikethrough()
|
.strikethrough()
|
||||||
.foregroundColor(DamusColors.white.opacity(0.5))
|
.foregroundColor(DamusColors.white.opacity(0.5))
|
||||||
}
|
}
|
||||||
Text(verbatim: product.displayPrice)
|
Text(product.displayPrice)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ extension DamusPurpleView {
|
|||||||
.shadow(radius: 5)
|
.shadow(radius: 5)
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text(NSLocalizedString("Purple", comment: "Subscription service name"))
|
Text("Purple", comment: "Subscription service name")
|
||||||
.font(.system(size: 60.0).weight(.bold))
|
.font(.system(size: 60.0).weight(.bold))
|
||||||
.foregroundStyle(
|
.foregroundStyle(
|
||||||
LinearGradient(
|
LinearGradient(
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ extension DamusPurpleView {
|
|||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 15, height: 15)
|
.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)
|
.font(.caption)
|
||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ struct PurpleBackdrop<T: View>: View {
|
|||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
PurpleBackdrop {
|
PurpleBackdrop {
|
||||||
Text("Hello, World")
|
Text(verbatim: "Hello, World")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,14 +67,14 @@ struct PurpleViewPrimitives {
|
|||||||
|
|
||||||
struct ProductLoadErrorView: View {
|
struct ProductLoadErrorView: View {
|
||||||
var body: some 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)
|
.foregroundColor(.white)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SaveTextView: View {
|
struct SaveTextView: View {
|
||||||
var body: some 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)
|
.font(.callout)
|
||||||
.italic()
|
.italic()
|
||||||
.foregroundColor(DamusColors.green)
|
.foregroundColor(DamusColors.green)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ struct RelayAdminDetail: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 15) {
|
HStack(spacing: 15) {
|
||||||
VStack(spacing: 10) {
|
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)
|
.font(.caption)
|
||||||
.fontWeight(.heavy)
|
.fontWeight(.heavy)
|
||||||
.foregroundColor(DamusColors.mediumGrey)
|
.foregroundColor(DamusColors.mediumGrey)
|
||||||
@@ -36,18 +36,18 @@ struct RelayAdminDetail: View {
|
|||||||
Divider().frame(width: 1)
|
Divider().frame(width: 1)
|
||||||
|
|
||||||
VStack {
|
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)
|
.font(.caption)
|
||||||
.fontWeight(.heavy)
|
.fontWeight(.heavy)
|
||||||
.foregroundColor(DamusColors.mediumGrey)
|
.foregroundColor(DamusColors.mediumGrey)
|
||||||
Image("messages")
|
Image("messages")
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
if nip11?.contact == "" {
|
if let contact = nip11?.contact, !contact.isEmpty {
|
||||||
Text("N/A")
|
Text(contact)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
} else {
|
} 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)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ struct RelayAuthenticationDetail: View {
|
|||||||
case .none:
|
case .none:
|
||||||
EmptyView()
|
EmptyView()
|
||||||
case .pending:
|
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)
|
.font(.caption)
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
@@ -27,7 +27,7 @@ struct RelayAuthenticationDetail: View {
|
|||||||
.stroke(DamusColors.warningBorder, lineWidth: 1)
|
.stroke(DamusColors.warningBorder, lineWidth: 1)
|
||||||
)
|
)
|
||||||
case .verified:
|
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)
|
.font(.caption)
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
@@ -39,7 +39,7 @@ struct RelayAuthenticationDetail: View {
|
|||||||
.stroke(DamusColors.successBorder, lineWidth: 1)
|
.stroke(DamusColors.successBorder, lineWidth: 1)
|
||||||
)
|
)
|
||||||
case .error:
|
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)
|
.font(.caption)
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ struct RelayNipList: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
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)
|
.font(.callout)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundColor(DamusColors.mediumGrey)
|
.foregroundColor(DamusColors.mediumGrey)
|
||||||
|
|||||||
@@ -20,17 +20,20 @@ struct RelayPaidDetail: View {
|
|||||||
return formattedString
|
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 {
|
func Amount(unit: String, amount: Int64) -> some View {
|
||||||
HStack {
|
HStack {
|
||||||
if unit == "msats" {
|
let displayString = displayAmount(unit: unit, amount: amount)
|
||||||
Text("\(format_msats(amount))")
|
Text(displayString)
|
||||||
.font(.system(size: 13, weight: .heavy))
|
.font(.system(size: 13, weight: .heavy))
|
||||||
.foregroundColor(DamusColors.white)
|
.foregroundColor(DamusColors.white)
|
||||||
} else {
|
|
||||||
Text("\(amount) \(unit)")
|
|
||||||
.font(.system(size: 13, weight: .heavy))
|
|
||||||
.foregroundColor(DamusColors.white)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,26 +51,24 @@ struct RelayPaidDetail: View {
|
|||||||
if !admission.isEmpty {
|
if !admission.isEmpty {
|
||||||
Amount(unit: admission[0].unit, amount: admission[0].amount)
|
Amount(unit: admission[0].unit, amount: admission[0].amount)
|
||||||
} else {
|
} else {
|
||||||
Text(verbatim: "Paid Relay")
|
Text("Paid Relay", comment: "Text indicating that this is a paid relay.")
|
||||||
.font(.system(size: 13, weight: .heavy))
|
.font(.system(size: 13, weight: .heavy))
|
||||||
.foregroundColor(DamusColors.white)
|
.foregroundColor(DamusColors.white)
|
||||||
}
|
}
|
||||||
} else if let subscription = fees?.subscription {
|
} else if let subscription = fees?.subscription {
|
||||||
if !subscription.isEmpty {
|
if !subscription.isEmpty {
|
||||||
Amount(unit: subscription[0].unit, amount: subscription[0].amount)
|
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.")
|
||||||
Text("/ \(timeString(time: subscription[0].period))")
|
|
||||||
.font(.system(size: 13, weight: .heavy))
|
.font(.system(size: 13, weight: .heavy))
|
||||||
.foregroundColor(DamusColors.white)
|
.foregroundColor(DamusColors.white)
|
||||||
}
|
}
|
||||||
} else if let publication = fees?.publication {
|
} else if let publication = fees?.publication {
|
||||||
if !publication.isEmpty {
|
if !publication.isEmpty {
|
||||||
Amount(unit: publication[0].unit, amount: publication[0].amount)
|
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.")
|
||||||
Text("/ event")
|
|
||||||
.font(.system(size: 13, weight: .heavy))
|
.font(.system(size: 13, weight: .heavy))
|
||||||
.foregroundColor(DamusColors.white)
|
.foregroundColor(DamusColors.white)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text(verbatim: "Paid Relay")
|
Text("Paid Relay", comment: "Text indicating that this is a paid relay.")
|
||||||
.font(.system(size: 13, weight: .heavy))
|
.font(.system(size: 13, weight: .heavy))
|
||||||
.foregroundColor(DamusColors.white)
|
.foregroundColor(DamusColors.white)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct RelaySoftwareDetail: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 15) {
|
HStack(spacing: 15) {
|
||||||
VStack {
|
VStack {
|
||||||
Text("SOFTWARE")
|
Text("SOFTWARE", comment: "Text label indicating which relay software is used to run this Nostr relay.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.fontWeight(.heavy)
|
.fontWeight(.heavy)
|
||||||
.foregroundColor(DamusColors.mediumGrey)
|
.foregroundColor(DamusColors.mediumGrey)
|
||||||
@@ -24,16 +24,21 @@ struct RelaySoftwareDetail: View {
|
|||||||
|
|
||||||
let software = nip11?.software
|
let software = nip11?.software
|
||||||
let softwareSeparated = software?.components(separatedBy: "/")
|
let softwareSeparated = software?.components(separatedBy: "/")
|
||||||
let softwareShortened = softwareSeparated?.last
|
if let softwareShortened = softwareSeparated?.last {
|
||||||
Text(softwareShortened ?? "N/A")
|
Text(softwareShortened)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.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)
|
Divider().frame(width: 1)
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
Text("VERSION")
|
Text("VERSION", comment: "Text label indicating which version of the relay software is being run for this Nostr relay.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.fontWeight(.heavy)
|
.fontWeight(.heavy)
|
||||||
.foregroundColor(DamusColors.mediumGrey)
|
.foregroundColor(DamusColors.mediumGrey)
|
||||||
@@ -41,9 +46,15 @@ struct RelaySoftwareDetail: View {
|
|||||||
Image("branches")
|
Image("branches")
|
||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
|
|
||||||
Text(nip11?.version ?? "N/A")
|
if let version = nip11?.version, !version.isEmpty {
|
||||||
|
Text(version)
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(.gray)
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ struct RelayConfigView: View {
|
|||||||
func RelayList(title: String, relayList: [RelayDescriptor], recommended: Bool) -> some View {
|
func RelayList(title: String, relayList: [RelayDescriptor], recommended: Bool) -> some View {
|
||||||
ScrollView(showsIndicators: false) {
|
ScrollView(showsIndicators: false) {
|
||||||
HStack {
|
HStack {
|
||||||
Text(NSLocalizedString(title, comment: "Section title for type of relay server list"))
|
Text(title)
|
||||||
.font(.system(size: 32, weight: .bold))
|
.font(.system(size: 32, weight: .bold))
|
||||||
|
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ struct RelayConfigView: View {
|
|||||||
show_add_relay.toggle()
|
show_add_relay.toggle()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text(verbatim: "Add relay")
|
Text("Add relay", comment: "Button text to add a relay")
|
||||||
.padding(10)
|
.padding(10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,12 +144,17 @@ struct RelayDetailView: View {
|
|||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
Text("Description")
|
Text("Description", comment: "Description of the specific Nostr relay server.")
|
||||||
.font(.subheadline)
|
.font(.subheadline)
|
||||||
.foregroundColor(DamusColors.mediumGrey)
|
.foregroundColor(DamusColors.mediumGrey)
|
||||||
|
|
||||||
Text(nip11?.description ?? "N/A")
|
if let description = nip11?.description, !description.isEmpty {
|
||||||
|
Text(description)
|
||||||
.font(.subheadline)
|
.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()
|
Divider()
|
||||||
|
|
||||||
@@ -175,7 +180,7 @@ struct RelayDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if state.settings.developer_mode {
|
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)
|
.padding(.top)
|
||||||
Divider()
|
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"))
|
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"))
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ struct RelayView: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
remove_action(privkey: keypair.privkey)
|
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)
|
.font(.caption)
|
||||||
}
|
}
|
||||||
.buttonStyle(NeutralButtonShape.capsule.style)
|
.buttonStyle(NeutralButtonShape.capsule.style)
|
||||||
@@ -147,7 +147,7 @@ struct RelayView: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
add_action(keypair: keypair)
|
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)
|
.font(.caption)
|
||||||
}
|
}
|
||||||
.buttonStyle(NeutralButtonShape.capsule.style)
|
.buttonStyle(NeutralButtonShape.capsule.style)
|
||||||
@@ -166,7 +166,7 @@ struct RelayView: View {
|
|||||||
remove_action(privkey: privkey)
|
remove_action(privkey: privkey)
|
||||||
}) {
|
}) {
|
||||||
if showText {
|
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")
|
Image("minus-circle")
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ struct SaveKeysView: View {
|
|||||||
@FocusState var pubkey_focused: Bool
|
@FocusState var pubkey_focused: Bool
|
||||||
@FocusState var privkey_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 {
|
var body: some View {
|
||||||
ZStack(alignment: .top) {
|
ZStack(alignment: .top) {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
@@ -102,6 +109,13 @@ struct SaveKeysView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func complete_account_creation(_ account: CreateAccountModel) {
|
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)
|
let bootstrap_relays = load_bootstrap_relays(pubkey: account.pubkey)
|
||||||
for relay in bootstrap_relays {
|
for relay in bootstrap_relays {
|
||||||
add_rw_relay(self.pool, relay)
|
add_rw_relay(self.pool, relay)
|
||||||
@@ -116,21 +130,29 @@ struct SaveKeysView: View {
|
|||||||
self.pool.connect()
|
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) {
|
func handle_event(relay: RelayURL, ev: NostrConnectionEvent) {
|
||||||
switch ev {
|
switch ev {
|
||||||
case .ws_event(let wsev):
|
case .ws_event(let wsev):
|
||||||
switch wsev {
|
switch wsev {
|
||||||
case .connected:
|
case .connected:
|
||||||
let metadata = create_account_to_metadata(account)
|
let metadata = create_account_to_metadata(account)
|
||||||
let contacts_ev = make_first_contact_event(keypair: account.keypair)
|
|
||||||
|
|
||||||
if let keypair = account.keypair.to_full(),
|
if let keypair = account.keypair.to_full(),
|
||||||
let metadata_ev = make_metadata_event(keypair: keypair, metadata: metadata) {
|
let metadata_ev = make_metadata_event(keypair: keypair, metadata: metadata) {
|
||||||
self.pool.send(.event(metadata_ev))
|
self.pool.send(.event(metadata_ev))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let contacts_ev {
|
if let first_contact_event {
|
||||||
self.pool.send(.event(contacts_ev))
|
self.pool.send(.event(first_contact_event))
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ struct PullDownSearchView: View {
|
|||||||
if results.count > 0 {
|
if results.count > 0 {
|
||||||
HStack {
|
HStack {
|
||||||
Image("search")
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
@@ -101,7 +101,7 @@ struct PullDownSearchView: View {
|
|||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Image("notes.fill")
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
@@ -109,7 +109,7 @@ struct PullDownSearchView: View {
|
|||||||
} else if results.count == 0 && !search_text.isEmpty {
|
} else if results.count == 0 && !search_text.isEmpty {
|
||||||
HStack {
|
HStack {
|
||||||
Image("search")
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -85,7 +85,7 @@ struct SearchHomeView: View {
|
|||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Image("notes.fill")
|
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()
|
Spacer()
|
||||||
}
|
}
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
|
|||||||
@@ -113,11 +113,11 @@ struct SearchResultsView: View {
|
|||||||
.frame(maxHeight: .infinity)
|
.frame(maxHeight: .infinity)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
guard let txn = NdbTxn.init(ndb: damus_state.ndb) else { return }
|
guard let txn = NdbTxn.init(ndb: damus_state.ndb) else { return }
|
||||||
self.result = search_for_string(profiles: damus_state.profiles, search: search, txn: txn)
|
self.result = search_for_string(profiles: damus_state.profiles, contacts: damus_state.contacts, search: search, txn: txn)
|
||||||
}
|
}
|
||||||
.onChange(of: search) { new in
|
.onChange(of: search) { new in
|
||||||
guard let txn = NdbTxn.init(ndb: damus_state.ndb) else { return }
|
guard let txn = NdbTxn.init(ndb: damus_state.ndb) else { return }
|
||||||
self.result = search_for_string(profiles: damus_state.profiles, search: search, txn: txn)
|
self.result = search_for_string(profiles: damus_state.profiles, contacts: damus_state.contacts, search: search, txn: txn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ struct SearchResultsView_Previews: PreviewProvider {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
func search_for_string<Y>(profiles: Profiles, search new: String, txn: NdbTxn<Y>) -> Search? {
|
func search_for_string<Y>(profiles: Profiles, contacts: Contacts, search new: String, txn: NdbTxn<Y>) -> Search? {
|
||||||
guard new.count != 0 else {
|
guard new.count != 0 else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ func search_for_string<Y>(profiles: Profiles, search new: String, txn: NdbTxn<Y>
|
|||||||
return .naddr(naddr)
|
return .naddr(naddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
let multisearch = MultiSearch(hashtag: make_hashtagable(searchQuery), profiles: search_profiles(profiles: profiles, search: new, txn: txn))
|
let multisearch = MultiSearch(hashtag: make_hashtagable(searchQuery), profiles: search_profiles(profiles: profiles, contacts: contacts, search: new, txn: txn))
|
||||||
return .multi(multisearch)
|
return .multi(multisearch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ func make_hashtagable(_ str: String) -> String {
|
|||||||
return String(new.filter{$0 != " "})
|
return String(new.filter{$0 != " "})
|
||||||
}
|
}
|
||||||
|
|
||||||
func search_profiles<Y>(profiles: Profiles, search: String, txn: NdbTxn<Y>) -> [Pubkey] {
|
func search_profiles<Y>(profiles: Profiles, contacts: Contacts, search: String, txn: NdbTxn<Y>) -> [Pubkey] {
|
||||||
// Search by hex pubkey.
|
// Search by hex pubkey.
|
||||||
if let pubkey = hex_decode_pubkey(search),
|
if let pubkey = hex_decode_pubkey(search),
|
||||||
profiles.lookup_key_by_pubkey(pubkey) != nil
|
profiles.lookup_key_by_pubkey(pubkey) != nil
|
||||||
@@ -208,8 +208,16 @@ func search_profiles<Y>(profiles: Profiles, search: String, txn: NdbTxn<Y>) -> [
|
|||||||
return [pk]
|
return [pk]
|
||||||
}
|
}
|
||||||
|
|
||||||
let new = search.lowercased()
|
return profiles.search(search, limit: 10, txn: txn).sorted { a, b in
|
||||||
|
let aFriendTypePriority = get_friend_type(contacts: contacts, pubkey: a)?.priority ?? 0
|
||||||
|
let bFriendTypePriority = get_friend_type(contacts: contacts, pubkey: b)?.priority ?? 0
|
||||||
|
|
||||||
return profiles.search(search, limit: 10, txn: txn)
|
if aFriendTypePriority > bFriendTypePriority {
|
||||||
|
// `a` should be sorted before `b`
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
//
|
|
||||||
// AddEmojiView.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Suhail Saqan on 7/16/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct AddEmojiView: View {
|
|
||||||
@Binding var emoji: String
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack(alignment: .leading) {
|
|
||||||
HStack{
|
|
||||||
TextField(NSLocalizedString("⚡", comment: "Placeholder example for an emoji reaction"), text: $emoji)
|
|
||||||
.padding(2)
|
|
||||||
.padding(.leading, 25)
|
|
||||||
.opacity(emoji == "" ? 0.5 : 1)
|
|
||||||
.autocorrectionDisabled(true)
|
|
||||||
.textInputAutocapitalization(.never)
|
|
||||||
.onChange(of: emoji) { newEmoji in
|
|
||||||
if let lastEmoji = newEmoji.last.map(String.init), isValidEmoji(lastEmoji) {
|
|
||||||
self.emoji = lastEmoji
|
|
||||||
} else {
|
|
||||||
self.emoji = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label("", image: "close-circle")
|
|
||||||
.foregroundColor(.accentColor)
|
|
||||||
.padding(.trailing, -25.0)
|
|
||||||
.opacity((emoji == "") ? 0.0 : 1.0)
|
|
||||||
.onTapGesture {
|
|
||||||
self.emoji = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label("", image: "copy2")
|
|
||||||
.padding(.leading, -10)
|
|
||||||
.onTapGesture {
|
|
||||||
if let pastedEmoji = UIPasteboard.general.string {
|
|
||||||
self.emoji = pastedEmoji
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -54,7 +54,7 @@ struct AppearanceSettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Text Truncation
|
// 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)
|
Toggle(NSLocalizedString("Truncate timeline text", comment: "Setting to truncate text in timeline"), isOn: $settings.truncate_timeline_text)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
Toggle(NSLocalizedString("Truncate notification mention text", comment: "Setting to truncate text in mention notifications"), isOn: $settings.truncate_mention_text)
|
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
|
// 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)
|
Toggle(NSLocalizedString("Left Handed", comment: "Moves the post button to the left side of the screen"), isOn: $settings.left_handed)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
}
|
}
|
||||||
@@ -97,8 +97,8 @@ struct AppearanceSettingsView: View {
|
|||||||
|
|
||||||
// MARK: - Content filters and moderation
|
// MARK: - Content filters and moderation
|
||||||
Section(
|
Section(
|
||||||
header: Text(NSLocalizedString("Content filters", comment: "Section title for content filtering/moderation configuration.")),
|
header: Text("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"))
|
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)
|
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)
|
.toggleStyle(.switch)
|
||||||
@@ -106,8 +106,8 @@ struct AppearanceSettingsView: View {
|
|||||||
|
|
||||||
// MARK: - Profiles
|
// MARK: - Profiles
|
||||||
Section(
|
Section(
|
||||||
header: Text(NSLocalizedString("Profiles", comment: "Section title for profile view configuration.")),
|
header: Text("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"))
|
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)
|
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)
|
.toggleStyle(.switch)
|
||||||
@@ -157,9 +157,9 @@ struct AppearanceSettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.alert(isPresented: $showing_enable_animation_alert) {
|
.alert(isPresented: $showing_enable_animation_alert) {
|
||||||
Alert(title: Text(NSLocalizedString("Confirmation", comment: "Confirmation dialog title")),
|
Alert(title: Text("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")),
|
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(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) {
|
primaryButton: .default(Text("OK", comment: "Button label indicating user wants to proceed.")) {
|
||||||
self.clear_cache_button_action()
|
self.clear_cache_button_action()
|
||||||
},
|
},
|
||||||
secondaryButton: .cancel() {
|
secondaryButton: .cancel() {
|
||||||
@@ -176,22 +176,22 @@ struct AppearanceSettingsView: View {
|
|||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
switch cache_clearing_state {
|
switch cache_clearing_state {
|
||||||
case .not_cleared:
|
case .not_cleared:
|
||||||
Text(NSLocalizedString("Clear Cache", comment: "Button to clear image cache."))
|
Text("Clear Cache", comment: "Button to clear image cache.")
|
||||||
case .clearing:
|
case .clearing:
|
||||||
ProgressView()
|
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:
|
case .cleared:
|
||||||
Image(systemName: "checkmark.circle.fill")
|
Image(systemName: "checkmark.circle.fill")
|
||||||
.foregroundColor(.green)
|
.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)
|
.disabled(self.cache_clearing_state != .not_cleared)
|
||||||
.alert(isPresented: $showing_cache_clear_alert) {
|
.alert(isPresented: $showing_cache_clear_alert) {
|
||||||
Alert(title: Text(NSLocalizedString("Confirmation", comment: "Confirmation dialog title")),
|
Alert(title: Text("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.")),
|
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(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) {
|
primaryButton: .default(Text("OK", comment: "Button label indicating user wants to proceed.")) {
|
||||||
self.clear_cache_button_action()
|
self.clear_cache_button_action()
|
||||||
},
|
},
|
||||||
secondaryButton: .cancel())
|
secondaryButton: .cancel())
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ struct DeveloperSettingsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
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)
|
Toggle(NSLocalizedString("Developer Mode", comment: "Setting to enable developer mode"), isOn: $settings.developer_mode)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
if settings.developer_mode {
|
if settings.developer_mode {
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
//
|
|
||||||
// EmojiListItemView.swift
|
|
||||||
// damus
|
|
||||||
//
|
|
||||||
// Created by Suhail Saqan on 7/16/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct EmojiListItemView: View {
|
|
||||||
@ObservedObject var settings: UserSettingsStore
|
|
||||||
|
|
||||||
let emoji: String
|
|
||||||
let recommended: Bool
|
|
||||||
|
|
||||||
@Binding var showActionButtons: Bool
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Group {
|
|
||||||
HStack {
|
|
||||||
if showActionButtons {
|
|
||||||
if recommended {
|
|
||||||
AddButton()
|
|
||||||
} else {
|
|
||||||
RemoveButton()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(emoji)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.swipeActions {
|
|
||||||
if !recommended {
|
|
||||||
RemoveButton()
|
|
||||||
.tint(.red)
|
|
||||||
} else {
|
|
||||||
AddButton()
|
|
||||||
.tint(.green)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.contextMenu {
|
|
||||||
if !showActionButtons {
|
|
||||||
CopyAction(emoji: emoji)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyAction(emoji: String) -> some View {
|
|
||||||
Button {
|
|
||||||
UIPasteboard.general.setValue(emoji, forPasteboardType: "public.plain-text")
|
|
||||||
} label: {
|
|
||||||
Label(NSLocalizedString("Copy", comment: "Button to copy an emoji reaction"), image: "copy2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveButton() -> some View {
|
|
||||||
Button(action: {
|
|
||||||
if let index = settings.emoji_reactions.firstIndex(of: emoji) {
|
|
||||||
settings.emoji_reactions.remove(at: index)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
Image(systemName: "minus.circle")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
.foregroundColor(.red)
|
|
||||||
.padding(.leading, -5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddButton() -> some View {
|
|
||||||
Button(action: {
|
|
||||||
settings.emoji_reactions.append(emoji)
|
|
||||||
}) {
|
|
||||||
Image(systemName: "plus.circle")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 20, height: 20)
|
|
||||||
.foregroundColor(.green)
|
|
||||||
.padding(.leading, -5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// FirstAidSettingsView.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Daniel D’Aquino 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)
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ struct NotificationSettingsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
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)
|
Toggle(NSLocalizedString("Zaps", comment: "Setting to enable Zap Local Notification"), isOn: $settings.zap_notification)
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
Toggle(NSLocalizedString("Mentions", comment: "Setting to enable Mention Local Notification"), isOn: $settings.mention_notification)
|
Toggle(NSLocalizedString("Mentions", comment: "Setting to enable Mention Local Notification"), isOn: $settings.mention_notification)
|
||||||
@@ -39,12 +39,12 @@ struct NotificationSettingsView: View {
|
|||||||
.toggleStyle(.switch)
|
.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)
|
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)
|
.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))
|
Toggle(NSLocalizedString("Zaps", comment: "Setting to enable Zap Local Notification"), isOn: indicator_binding(.zaps))
|
||||||
.toggleStyle(.switch)
|
.toggleStyle(.switch)
|
||||||
Toggle(NSLocalizedString("Mentions", comment: "Setting to enable Mention Local Notification"), isOn: indicator_binding(.mentions))
|
Toggle(NSLocalizedString("Mentions", comment: "Setting to enable Mention Local Notification"), isOn: indicator_binding(.mentions))
|
||||||
|
|||||||
@@ -6,114 +6,31 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Combine
|
import MCEmojiPicker
|
||||||
|
|
||||||
struct ReactionsSettingsView: View {
|
struct ReactionsSettingsView: View {
|
||||||
@ObservedObject var settings: UserSettingsStore
|
@ObservedObject var settings: UserSettingsStore
|
||||||
|
@State private var isReactionsVisible: Bool = false
|
||||||
@State var new_emoji: String = ""
|
|
||||||
@State private var showActionButtons = false
|
|
||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
|
||||||
|
|
||||||
var recommended: [String] {
|
|
||||||
return getMissingRecommendedEmojis(added: settings.emoji_reactions)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
AddEmojiView(emoji: $new_emoji)
|
Text(settings.default_emoji_reaction)
|
||||||
} header: {
|
.emojiPicker(
|
||||||
Text(NSLocalizedString("Add Emoji", comment: "Label for section for adding an emoji to the reactions list."))
|
isPresented: $isReactionsVisible,
|
||||||
.font(.system(size: 18, weight: .heavy))
|
selectedEmoji: $settings.default_emoji_reaction,
|
||||||
.padding(.bottom, 5)
|
arrowDirection: .up,
|
||||||
} footer: {
|
isDismissAfterChoosing: true
|
||||||
HStack {
|
)
|
||||||
Spacer()
|
.onTapGesture {
|
||||||
if !new_emoji.isEmpty {
|
isReactionsVisible = true
|
||||||
Button(NSLocalizedString("Cancel", comment: "Button to cancel out of view adding user inputted emoji.")) {
|
|
||||||
new_emoji = ""
|
|
||||||
}
|
|
||||||
.font(.system(size: 14, weight: .bold))
|
|
||||||
.frame(width: 80, height: 30)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.background(LINEAR_GRADIENT)
|
|
||||||
.clipShape(Capsule())
|
|
||||||
.padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
|
|
||||||
|
|
||||||
Button(NSLocalizedString("Add", comment: "Button to confirm adding user inputted emoji.")) {
|
|
||||||
if isValidEmoji(new_emoji) {
|
|
||||||
settings.emoji_reactions.append(new_emoji)
|
|
||||||
new_emoji = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.font(.system(size: 14, weight: .bold))
|
|
||||||
.frame(width: 80, height: 30)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.background(LINEAR_GRADIENT)
|
|
||||||
.clipShape(Capsule())
|
|
||||||
.padding(EdgeInsets(top: 15, leading: 0, bottom: 0, trailing: 0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Picker(NSLocalizedString("Select default emoji", comment: "Prompt selection of user's default emoji reaction"),
|
|
||||||
selection: $settings.default_emoji_reaction) {
|
|
||||||
ForEach(settings.emoji_reactions, id: \.self) { emoji in
|
|
||||||
Text(emoji)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
|
||||||
List {
|
|
||||||
ForEach(Array(zip(settings.emoji_reactions, 1...)), id: \.1) { tup in
|
|
||||||
EmojiListItemView(settings: settings, emoji: tup.0, recommended: false, showActionButtons: $showActionButtons)
|
|
||||||
}
|
|
||||||
.onMove(perform: showActionButtons ? move: nil)
|
|
||||||
}
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Emoji Reactions", comment: "Section title for emoji reactions that are currently added.")
|
Text("Select default emoji", comment: "Prompt selection of user's default emoji reaction")
|
||||||
.font(.system(size: 18, weight: .heavy))
|
|
||||||
.padding(.bottom, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
if recommended.count > 0 {
|
|
||||||
Section {
|
|
||||||
List(Array(zip(recommended, 1...)), id: \.1) { tup in
|
|
||||||
EmojiListItemView(settings: settings, emoji: tup.0, recommended: true, showActionButtons: $showActionButtons)
|
|
||||||
}
|
|
||||||
} header: {
|
|
||||||
Text("Recommended Emojis", comment: "Section title for recommend emojis")
|
|
||||||
.font(.system(size: 18, weight: .heavy))
|
|
||||||
.padding(.bottom, 5)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
|
.navigationTitle(NSLocalizedString("Reactions", comment: "Title of emoji reactions view"))
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
.toolbar {
|
|
||||||
if showActionButtons {
|
|
||||||
Button("Done") {
|
|
||||||
showActionButtons.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Button("Edit") {
|
|
||||||
showActionButtons.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func move(from: IndexSet, to: Int) {
|
|
||||||
settings.emoji_reactions.move(fromOffsets: from, toOffset: to)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the emojis that are in the recommended list but the user has not added yet
|
|
||||||
func getMissingRecommendedEmojis(added: [String], recommended: [String] = default_emoji_reactions) -> [String] {
|
|
||||||
let addedSet = Set(added)
|
|
||||||
let missingEmojis = recommended.filter { !addedSet.contains($0) }
|
|
||||||
return missingEmojis
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ struct SearchSettingsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
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)
|
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)
|
.toggleStyle(.switch)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct TranslationSettingsView: View {
|
|||||||
|
|
||||||
if settings.translation_service == .purple && damus_state.purple.enable_purple {
|
if settings.translation_service == .purple && damus_state.purple.enable_purple {
|
||||||
NavigationLink(destination: DamusPurpleView(damus_state: damus_state)) {
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ struct ZapSettingsView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section(
|
Section(
|
||||||
header: Text(NSLocalizedString("OnlyZaps", comment: "Section header for enabling OnlyZaps mode (hide reactions)")),
|
header: Text("OnlyZaps", comment: "Section header for enabling OnlyZaps mode (hide reactions)"),
|
||||||
footer: Text(NSLocalizedString("Hide all 🤙's", comment: "Section footer describing OnlyZaps mode"))
|
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)
|
Toggle(NSLocalizedString("OnlyZaps mode", comment: "Setting toggle to hide reactions."), isOn: $settings.onlyzaps_mode)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ struct SuggestedHashtagsView: View {
|
|||||||
VStack {
|
VStack {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "sparkles")
|
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()
|
Spacer()
|
||||||
Button(action: {
|
Button(action: {
|
||||||
withAnimation(.easeOut(duration: 0.2)) {
|
withAnimation(.easeOut(duration: 0.2)) {
|
||||||
@@ -105,7 +105,8 @@ struct SuggestedHashtagsView: View {
|
|||||||
Text(verbatim: "#\(hashtag)")
|
Text(verbatim: "#\(hashtag)")
|
||||||
.bold()
|
.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)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ struct ConnectWalletView: View {
|
|||||||
}
|
}
|
||||||
.alert(isPresented: $showAlert) {
|
.alert(isPresented: $showAlert) {
|
||||||
Alert(
|
Alert(
|
||||||
title: Text(NSLocalizedString("Invalid Nostr wallet connection string", comment: "Error message when an invalid Nostr wallet connection string is provided.")),
|
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."),
|
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(NSLocalizedString("OK", comment: "Button label indicating user wants to proceed."))) {
|
dismissButton: .default(Text("OK", comment: "Button label indicating user wants to proceed.")) {
|
||||||
wallet_scan_result = .scanning
|
wallet_scan_result = .scanning
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -80,7 +80,7 @@ struct ConnectWalletView: View {
|
|||||||
model.cancel()
|
model.cancel()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
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()
|
.padding()
|
||||||
}
|
}
|
||||||
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
||||||
@@ -96,9 +96,16 @@ struct ConnectWalletView: View {
|
|||||||
openURL(URL(string:"https://nwc.getalby.com/apps/new?c=Damus")!)
|
openURL(URL(string:"https://nwc.getalby.com/apps/new?c=Damus")!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mutiny Wallet NWC is way too advanced to recommend for normal
|
||||||
|
// users until they have a way to do async receive.
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
MutinyButton() {
|
MutinyButton() {
|
||||||
openURL(URL(string:"https://app.mutinywallet.com/settings/connections?callbackUri=nostr%2bwalletconnect&name=Damus")!)
|
openURL(URL(string:"https://app.mutinywallet.com/settings/connections?callbackUri=nostr%2bwalletconnect&name=Damus")!)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Button(action: {
|
Button(action: {
|
||||||
if let pasted_nwc = UIPasteboard.general.string {
|
if let pasted_nwc = UIPasteboard.general.string {
|
||||||
@@ -168,10 +175,10 @@ struct ConnectWalletView: View {
|
|||||||
|
|
||||||
var TitleSection: some View {
|
var TitleSection: some View {
|
||||||
VStack(spacing: 25) {
|
VStack(spacing: 25) {
|
||||||
Text("Damus Wallet")
|
Text("Damus Wallet", comment: "Title text for Damus Wallet view.")
|
||||||
.fontWeight(.bold)
|
.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)
|
.font(.caption)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct WalletView: View {
|
|||||||
|
|
||||||
VStack(spacing: 5) {
|
VStack(spacing: 5) {
|
||||||
VStack(spacing: 10) {
|
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)
|
.fontWeight(.semibold)
|
||||||
.padding(.top)
|
.padding(.top)
|
||||||
|
|
||||||
@@ -47,12 +47,12 @@ struct WalletView: View {
|
|||||||
|
|
||||||
if let lud16 = nwc.lud16 {
|
if let lud16 = nwc.lud16 {
|
||||||
VStack(spacing: 10) {
|
VStack(spacing: 10) {
|
||||||
Text("Wallet Address")
|
Text("Wallet Address", comment: "Label text indicating that below it is the wallet address.")
|
||||||
.fontWeight(.semibold)
|
.fontWeight(.semibold)
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
Text(verbatim: lud16)
|
Text(lud16)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, minHeight: 75, alignment: .center)
|
.frame(maxWidth: .infinity, minHeight: 75, alignment: .center)
|
||||||
.padding(.horizontal, 10)
|
.padding(.horizontal, 10)
|
||||||
@@ -69,7 +69,7 @@ struct WalletView: View {
|
|||||||
self.model.disconnect()
|
self.model.disconnect()
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
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)
|
.frame(minWidth: 300, maxWidth: .infinity, maxHeight: 18, alignment: .center)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ struct CustomizeZapView: View {
|
|||||||
model.zapping = true
|
model.zapping = true
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
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))
|
.font(.system(size: 20, weight: .bold))
|
||||||
}
|
}
|
||||||
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
||||||
|
|||||||
Binary file not shown.
@@ -226,6 +226,22 @@
|
|||||||
<string>geteilte Beiträge</string>
|
<string>geteilte Beiträge</string>
|
||||||
</dict>
|
</dict>
|
||||||
</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>
|
<key>sats</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
@@ -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">
|
<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">
|
<file original="damus/en-US.lproj/InfoPlist.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||||
@@ -44,15 +44,25 @@
|
|||||||
</file>
|
</file>
|
||||||
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
<file original="damus/en-US.lproj/Localizable.strings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="%@ %@" xml:space="preserve">
|
<trans-unit id="%@ %@" xml:space="preserve">
|
||||||
<source>%@ %@</source>
|
<source>%@ %@</source>
|
||||||
<target>%@ %@</target>
|
<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 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>
|
||||||
<trans-unit id="%@ has been muted" xml:space="preserve">
|
<trans-unit id="%@ has been muted" xml:space="preserve">
|
||||||
<source>%@ has been muted</source>
|
<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>
|
<target>(Contents are encrypted)</target>
|
||||||
<note>Label on push notification indicating that the contents of the message are encrypted</note>
|
<note>Label on push notification indicating that the contents of the message are encrypted</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="API Key (optional)" xml:space="preserve">
|
||||||
<source>API Key (optional)</source>
|
<source>API Key (optional)</source>
|
||||||
<target>API Key (optional)</target>
|
<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">
|
<trans-unit id="Add" xml:space="preserve">
|
||||||
<source>Add</source>
|
<source>Add</source>
|
||||||
<target>Add</target>
|
<target>Add</target>
|
||||||
<note>Button to add relay server to list.
|
<note>Button to add relay server to list.</note>
|
||||||
Button to confirm adding user inputted emoji.</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Add Bookmark" xml:space="preserve">
|
<trans-unit id="Add Bookmark" xml:space="preserve">
|
||||||
<source>Add Bookmark</source>
|
<source>Add Bookmark</source>
|
||||||
<target>Add Bookmark</target>
|
<target>Add Bookmark</target>
|
||||||
<note>Button text to add bookmark to a note.</note>
|
<note>Button text to add bookmark to a note.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Add all" xml:space="preserve">
|
||||||
<source>Add all</source>
|
<source>Add all</source>
|
||||||
<target>Add all</target>
|
<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>
|
<target>Add bookmark</target>
|
||||||
<note>Context menu option for adding a note bookmark.</note>
|
<note>Context menu option for adding a note bookmark.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Add relay" xml:space="preserve">
|
||||||
<source>Add relay</source>
|
<source>Add relay</source>
|
||||||
<target>Add relay</target>
|
<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>
|
||||||
<trans-unit id="Add your first post" xml:space="preserve">
|
<trans-unit id="Add your first post" xml:space="preserve">
|
||||||
<source>Add your first post</source>
|
<source>Add your first post</source>
|
||||||
<target>Add your first post</target>
|
<target>Add your first post</target>
|
||||||
<note>Prompt given to the user during onboarding, suggesting them to write their first post</note>
|
<note>Prompt given to the user during onboarding, suggesting them to write their first post</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Additional information" xml:space="preserve">
|
||||||
<source>Additional information</source>
|
<source>Additional information</source>
|
||||||
<target>Additional information</target>
|
<target>Additional information</target>
|
||||||
<note>Header text to prompt user to optionally provide additional information when reporting a user or note.</note>
|
<note>Header text to prompt user to optionally provide additional information when reporting a user or note.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="All" xml:space="preserve">
|
||||||
<source>All</source>
|
<source>All</source>
|
||||||
<target>All</target>
|
<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>
|
<target>Are you lost?</target>
|
||||||
<note>Text asking the user if they are lost in the app.</note>
|
<note>Text asking the user if they are lost in the app.</note>
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<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>
|
<note>Message explaining what it means to clear the cache, asking if user wants to proceed.</note>
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<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>
|
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>
|
<note>Message notifying the user that they get auto-translations as part of their service</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Authenticated" xml:space="preserve">
|
||||||
<source>Authenticated</source>
|
<source>Authenticated</source>
|
||||||
<target>Authenticated</target>
|
<target>Authenticated</target>
|
||||||
<note>Label to display that authentication to a server has succeeded.</note>
|
<note>Label to display that authentication to a server has succeeded.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Automatic translations" xml:space="preserve">
|
||||||
<source>Automatic translations</source>
|
<source>Automatic translations</source>
|
||||||
<target>Automatic translations</target>
|
<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>
|
<target>By signing up, you agree to our </target>
|
||||||
<note>Ask the user if they already have an account on Nostr</note>
|
<note>Ask the user if they already have an account on Nostr</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Cache has been cleared" xml:space="preserve">
|
||||||
<source>Cache has been cleared</source>
|
<source>Cache has been cleared</source>
|
||||||
<target>Cache has been cleared</target>
|
<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 a repost.
|
||||||
Button to cancel any interaction with the QRCode link.
|
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 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.
|
Button to cancel the upload.
|
||||||
Cancel deleting bookmarks.
|
Cancel deleting bookmarks.
|
||||||
Cancel deleting the user.
|
Cancel deleting the user.
|
||||||
Cancel out of logging out the user.
|
Cancel out of logging out the user.</note>
|
||||||
Text for button to cancel out of connecting Nostr Wallet Connect lightning ewallet.</note>
|
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<target>Confirmation</target>
|
||||||
<note>Confirmation dialog title</note>
|
<note>Confirmation dialog title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connect To Relay" xml:space="preserve">
|
<trans-unit id="Connect" xml:space="preserve">
|
||||||
<source>Connect To Relay</source>
|
<source>Connect</source>
|
||||||
<target>Connect To Relay</target>
|
<target>Connect</target>
|
||||||
<note>Button to connect to the relay.</note>
|
<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>
|
||||||
<trans-unit id="Connecting" xml:space="preserve">
|
<trans-unit id="Connecting" xml:space="preserve">
|
||||||
<source>Connecting</source>
|
<source>Connecting</source>
|
||||||
<target>Connecting</target>
|
<target>Connecting</target>
|
||||||
<note>Relay status label that indicates a relay is connecting.</note>
|
<note>Relay status label that indicates a relay is connecting.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Content filters" xml:space="preserve">
|
||||||
<source>Content filters</source>
|
<source>Content filters</source>
|
||||||
<target>Content filters</target>
|
<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">
|
<trans-unit id="Continue" xml:space="preserve">
|
||||||
<source>Continue</source>
|
<source>Continue</source>
|
||||||
<target>Continue</target>
|
<target>Continue</target>
|
||||||
<note>Button to dismiss suggested users view and continue to the main app
|
<note>Continue with bookmarks.
|
||||||
Continue with bookmarks.
|
Continue with deleting the user.</note>
|
||||||
Continue with deleting the user.
|
|
||||||
Prompt to user to continue</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Copied" xml:space="preserve">
|
<trans-unit id="Copied" xml:space="preserve">
|
||||||
<source>Copied</source>
|
<source>Copied</source>
|
||||||
@@ -490,7 +506,6 @@ Tip: You can always change this later in Settings → Translations</target>
|
|||||||
<source>Copy</source>
|
<source>Copy</source>
|
||||||
<target>Copy</target>
|
<target>Copy</target>
|
||||||
<note>Button to copy a relay server address.
|
<note>Button to copy a relay server address.
|
||||||
Button to copy an emoji reaction
|
|
||||||
Button to copy the value found.
|
Button to copy the value found.
|
||||||
Context menu option for copying the version of damus.</note>
|
Context menu option for copying the version of damus.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@@ -597,6 +612,11 @@ Tip: You can always change this later in Settings → Translations</target>
|
|||||||
Setting to enable DM Local Notification
|
Setting to enable DM Local Notification
|
||||||
Toolbar label for DMs view, where DM is the English abbreviation for Direct Message.</note>
|
Toolbar label for DMs view, where DM is the English abbreviation for Direct Message.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Damus Purple" xml:space="preserve">
|
||||||
<source>Damus Purple</source>
|
<source>Damus Purple</source>
|
||||||
<target>Damus Purple</target>
|
<target>Damus Purple</target>
|
||||||
@@ -607,6 +627,11 @@ Tip: You can always change this later in Settings → Translations</target>
|
|||||||
<target>Damus Purple environment</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>
|
<note>Prompt selection of the Damus purple environment (Developer feature to switch between real/production mode to test modes).</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="DeepL (Proprietary, Higher Accuracy)" xml:space="preserve">
|
||||||
<source>DeepL (Proprietary, Higher Accuracy)</source>
|
<source>DeepL (Proprietary, Higher Accuracy)</source>
|
||||||
<target>DeepL (Proprietary, Higher Accuracy)</target>
|
<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">
|
<trans-unit id="Description" xml:space="preserve">
|
||||||
<source>Description</source>
|
<source>Description</source>
|
||||||
<target>Description</target>
|
<target>Description</target>
|
||||||
<note>Label to display relay description.</note>
|
<note>Description of the specific Nostr relay server.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Developer" xml:space="preserve">
|
<trans-unit id="Developer" xml:space="preserve">
|
||||||
<source>Developer</source>
|
<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">
|
<trans-unit id="Disconnect" xml:space="preserve">
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<target>Disconnect</target>
|
<target>Disconnect</target>
|
||||||
<note>Button to disconnect from a relay server.</note>
|
<note>Button to disconnect from the relay.
|
||||||
</trans-unit>
|
Button to disconnect from a relay server.</note>
|
||||||
<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>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Disconnect Wallet" xml:space="preserve">
|
<trans-unit id="Disconnect Wallet" xml:space="preserve">
|
||||||
<source>Disconnect Wallet</source>
|
<source>Disconnect Wallet</source>
|
||||||
@@ -709,11 +730,6 @@ Tip: You can always change this later in Settings → Translations</target>
|
|||||||
<target>Edit</target>
|
<target>Edit</target>
|
||||||
<note>Button to edit user's profile.</note>
|
<note>Button to edit user's profile.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Enable Purple auto-translations" xml:space="preserve">
|
||||||
<source>Enable Purple auto-translations</source>
|
<source>Enable Purple auto-translations</source>
|
||||||
<target>Enable Purple auto-translations</target>
|
<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">
|
<trans-unit id="Error" xml:space="preserve">
|
||||||
<source>Error</source>
|
<source>Error</source>
|
||||||
<target>Error</target>
|
<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>
|
||||||
<trans-unit id="Error fetching lightning invoice" xml:space="preserve">
|
<trans-unit id="Error fetching lightning invoice" xml:space="preserve">
|
||||||
<source>Error fetching lightning invoice</source>
|
<source>Error fetching lightning invoice</source>
|
||||||
<target>Error fetching lightning invoice</target>
|
<target>Error fetching lightning invoice</target>
|
||||||
<note>Message to display when there was an error fetching a lightning invoice while attempting to zap.</note>
|
<note>Message to display when there was an error fetching a lightning invoice while attempting to zap.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Error: %@" xml:space="preserve">
|
||||||
<source>Error: %@</source>
|
<source>Error: %@</source>
|
||||||
<target>Error: %@</target>
|
<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">
|
<trans-unit id="Hashtags" xml:space="preserve">
|
||||||
<source>Hashtags</source>
|
<source>Hashtags</source>
|
||||||
<target>Hashtags</target>
|
<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>
|
||||||
<trans-unit id="Hello everybody! This is my first post on Damus, I am happy to meet you all 🤙. What’s up? #introductions" xml:space="preserve">
|
<trans-unit id="Hello everybody! This is my first post on Damus, I am happy to meet you all 🤙. What’s up? #introductions" xml:space="preserve">
|
||||||
<source>Hello everybody!
|
<source>Hello everybody!
|
||||||
@@ -937,7 +959,7 @@ This is my first post on Damus, I am happy to meet you all 🤙. What’s up?
|
|||||||
<trans-unit id="Hide" xml:space="preserve">
|
<trans-unit id="Hide" xml:space="preserve">
|
||||||
<source>Hide</source>
|
<source>Hide</source>
|
||||||
<target>Hide</target>
|
<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>
|
||||||
<trans-unit id="Hide all 🤙's" xml:space="preserve">
|
<trans-unit id="Hide all 🤙's" xml:space="preserve">
|
||||||
<source>Hide all 🤙's</source>
|
<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>
|
<target>Impersonation</target>
|
||||||
<note>Description of report type for impersonation.</note>
|
<note>Description of report type for impersonation.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Invalid Nostr wallet connection string" xml:space="preserve">
|
||||||
<source>Invalid Nostr wallet connection string</source>
|
<source>Invalid Nostr wallet connection string</source>
|
||||||
<target>Invalid Nostr wallet connection string</target>
|
<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>
|
<target>Local default</target>
|
||||||
<note>Dropdown option label for system default for Lightning wallet.</note>
|
<note>Dropdown option label for system default for Lightning wallet.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Login" xml:space="preserve">
|
||||||
<source>Login</source>
|
<source>Login</source>
|
||||||
<target>Login</target>
|
<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>
|
<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>
|
<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>
|
||||||
|
<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">
|
<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>
|
<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>
|
<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>
|
<target>Manage</target>
|
||||||
<note>Manage the damus subscription</note>
|
<note>Manage the damus subscription</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Media previews" xml:space="preserve">
|
||||||
<source>Media previews</source>
|
<source>Media previews</source>
|
||||||
<target>Media previews</target>
|
<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">
|
<trans-unit id="Mute" xml:space="preserve">
|
||||||
<source>Mute</source>
|
<source>Mute</source>
|
||||||
<target>Mute</target>
|
<target>Mute</target>
|
||||||
<note>Alert button to mute a user.
|
<note>Alert button to mute a user.</note>
|
||||||
Button to mute a profile.</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Mute %@?" xml:space="preserve">
|
<trans-unit id="Mute %@?" xml:space="preserve">
|
||||||
<source>Mute %@?</source>
|
<source>Mute %@?</source>
|
||||||
<target>Mute %@?</target>
|
<target>Mute %@?</target>
|
||||||
<note>Alert message prompt to ask if a user should be muted.</note>
|
<note>Alert message prompt to ask if a user should be muted.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Mute User" xml:space="preserve">
|
||||||
<source>Mute User</source>
|
<source>Mute User</source>
|
||||||
<target>Mute User</target>
|
<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">
|
<trans-unit id="Muted" xml:space="preserve">
|
||||||
<source>Muted</source>
|
<source>Muted</source>
|
||||||
<target>Muted</target>
|
<target>Muted</target>
|
||||||
<note>Sidebar menu label for muted users view.</note>
|
<note>Navigation title of view to see list of muted users & phrases.
|
||||||
|
Sidebar menu label for muted users view.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Muted Users" xml:space="preserve">
|
<trans-unit id="N/A" xml:space="preserve">
|
||||||
<source>Muted Users</source>
|
<source>N/A</source>
|
||||||
<target>Muted Users</target>
|
<target>N/A</target>
|
||||||
<note>Navigation title of view to see list of muted users.</note>
|
<note>Text label indicating that there is no NIP-11 relay admin contact information found. In English, N/A stands for not applicable.
|
||||||
</trans-unit>
|
Text label indicating that there is no NIP-11 relay description information found. In English, N/A stands for not applicable.
|
||||||
<trans-unit id="My Relays" xml:space="preserve">
|
Text label indicating that there is no NIP-11 relay software information found. In English, N/A stands for not applicable.</note>
|
||||||
<source>My Relays</source>
|
|
||||||
<target>My Relays</target>
|
|
||||||
<note>Section title for relay servers that the user is connected to.</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Never" xml:space="preserve">
|
<trans-unit id="Never" xml:space="preserve">
|
||||||
<source>Never</source>
|
<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>
|
<target>No</target>
|
||||||
<note>User confirm No</note>
|
<note>User confirm No</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="No logs to display" xml:space="preserve">
|
||||||
<source>No logs to display</source>
|
<source>No logs to display</source>
|
||||||
<target>No logs to display</target>
|
<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>
|
<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>
|
<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>
|
||||||
<trans-unit id="Note from a user you've muted" xml:space="preserve">
|
<trans-unit id="Note from a %@ you've muted" xml:space="preserve">
|
||||||
<source>Note from a user you've muted</source>
|
<source>Note from a %@ you've muted</source>
|
||||||
<target>Note from a user you've muted</target>
|
<target>Note from a %@ you've muted</target>
|
||||||
<note>Text to indicate that what is being shown is a note from a user who has been muted.</note>
|
<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>
|
||||||
<trans-unit id="Notes" xml:space="preserve">
|
<trans-unit id="Notes" xml:space="preserve">
|
||||||
<source>Notes</source>
|
<source>Notes</source>
|
||||||
<target>Notes</target>
|
<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>
|
||||||
<trans-unit id="Notes & Replies" xml:space="preserve">
|
<trans-unit id="Notes & Replies" xml:space="preserve">
|
||||||
<source>Notes & Replies</source>
|
<source>Notes & Replies</source>
|
||||||
<target>Notes & Replies</target>
|
<target>Notes & Replies</target>
|
||||||
<note>Label for filter for seeing your notes and replies (instead of only your notes).
|
<note>Label for filter for seeing notes and replies (instead of only notes).
|
||||||
Label for filter for seeing notes and replies (instead of only notes).</note>
|
Label for filter for seeing your notes and replies (instead of only your notes).</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content" xml:space="preserve">
|
<trans-unit id="Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content" xml:space="preserve">
|
||||||
<source>Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content</source>
|
<source>Notes with the #nsfw tag usually contains adult content or other "Not safe for work" content</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">
|
<trans-unit id="Paid Relay" xml:space="preserve">
|
||||||
<source>Paid Relay</source>
|
<source>Paid Relay</source>
|
||||||
<target>Paid Relay</target>
|
<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>
|
||||||
<trans-unit id="Paste" xml:space="preserve">
|
<trans-unit id="Paste NWC Address" xml:space="preserve">
|
||||||
<source>Paste</source>
|
<source>Paste NWC Address</source>
|
||||||
<target>Paste</target>
|
<target>Paste NWC Address</target>
|
||||||
<note>Button to paste a Nostr Wallet Connect string to connect the wallet for use in Damus for zaps.</note>
|
<note>Text for button to connect a lightning wallet.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Pay" xml:space="preserve">
|
<trans-unit id="Pay" xml:space="preserve">
|
||||||
<source>Pay</source>
|
<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">
|
<trans-unit id="Permanently Delete Account" xml:space="preserve">
|
||||||
<source>Permanently Delete Account</source>
|
<source>Permanently Delete Account</source>
|
||||||
<target>Permanently Delete Account</target>
|
<target>Permanently Delete Account</target>
|
||||||
<note>Alert for deleting the users account.
|
<note>Alert for deleting the users account.</note>
|
||||||
Section title for deleting the user</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Plan" xml:space="preserve">
|
<trans-unit id="Plan" xml:space="preserve">
|
||||||
<source>Plan</source>
|
<source>Plan</source>
|
||||||
@@ -1520,6 +1560,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
|
|||||||
<target>Purchased!</target>
|
<target>Purchased!</target>
|
||||||
<note>User purchased a subscription</note>
|
<note>User purchased a subscription</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Purple" xml:space="preserve">
|
||||||
<source>Purple</source>
|
<source>Purple</source>
|
||||||
<target>Purple</target>
|
<target>Purple</target>
|
||||||
@@ -1535,6 +1580,11 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
|
|||||||
<target>Quote</target>
|
<target>Quote</target>
|
||||||
<note>Button to compose a quoted note</note>
|
<note>Button to compose a quoted note</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Ran to suspension." xml:space="preserve">
|
||||||
<source>Ran to suspension.</source>
|
<source>Ran to suspension.</source>
|
||||||
<target>Ran to suspension.</target>
|
<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
|
Section header for reactions settings
|
||||||
Title of emoji reactions view</note>
|
Title of emoji reactions view</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Recommended Emojis" xml:space="preserve">
|
<trans-unit id="Relay Logs" xml:space="preserve">
|
||||||
<source>Recommended Emojis</source>
|
<source>Relay Logs</source>
|
||||||
<target>Recommended Emojis</target>
|
<target>Relay Logs</target>
|
||||||
<note>Section title for recommend emojis</note>
|
<note>Text label indicating that the text below it are developer mode logs.</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>
|
</trans-unit>
|
||||||
<trans-unit id="Relays" xml:space="preserve">
|
<trans-unit id="Relays" xml:space="preserve">
|
||||||
<source>Relays</source>
|
<source>Relays</source>
|
||||||
@@ -1589,6 +1629,16 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
|
|||||||
<target>Remove bookmark</target>
|
<target>Remove bookmark</target>
|
||||||
<note>Context menu option for removing a note bookmark.</note>
|
<note>Context menu option for removing a note bookmark.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Renews on" xml:space="preserve">
|
||||||
<source>Renews on</source>
|
<source>Renews on</source>
|
||||||
<target>Renews on</target>
|
<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>
|
<target>Runtime error</target>
|
||||||
<note>Indication that a runtime error occurred when running a NostrScript.</note>
|
<note>Indication that a runtime error occurred when running a NostrScript.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Satoshi Nakamoto" xml:space="preserve">
|
||||||
<source>Satoshi Nakamoto</source>
|
<source>Satoshi Nakamoto</source>
|
||||||
<target>Satoshi Nakamoto</target>
|
<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>
|
<target>Scan Code</target>
|
||||||
<note>Button to switch to scan QR Code page.</note>
|
<note>Button to switch to scan QR Code page.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Scan Your Private Key QR" xml:space="preserve">
|
||||||
<source>Scan Your Private Key QR</source>
|
<source>Scan Your Private Key QR</source>
|
||||||
<target>Scan Your Private Key QR</target>
|
<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>
|
<target>Secret Account Login Key</target>
|
||||||
<note>Section title for user's secret account login key.</note>
|
<note>Section title for user's secret account login key.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Select a Lightning wallet" xml:space="preserve">
|
||||||
<source>Select a Lightning wallet</source>
|
<source>Select a Lightning wallet</source>
|
||||||
<target>Select a Lightning wallet</target>
|
<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">
|
<trans-unit id="Show" xml:space="preserve">
|
||||||
<source>Show</source>
|
<source>Show</source>
|
||||||
<target>Show</target>
|
<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>
|
Toggle to show or hide user's secret account login key.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Show general statuses" xml:space="preserve">
|
<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>
|
<target>Show profile action sheets</target>
|
||||||
<note>Setting to show profile action sheets when clicking on a user's profile picture</note>
|
<note>Setting to show profile action sheets when clicking on a user's profile picture</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Show wallet selector" xml:space="preserve">
|
||||||
<source>Show wallet selector</source>
|
<source>Show wallet selector</source>
|
||||||
<target>Show wallet selector</target>
|
<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>
|
<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>
|
<note>Description about why Nostr is needed.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Someone posted a note" xml:space="preserve">
|
||||||
<source>Someone posted a note</source>
|
<source>Someone posted a note</source>
|
||||||
<target>Someone posted a note</target>
|
<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">
|
<trans-unit id="Spam" xml:space="preserve">
|
||||||
<source>Spam</source>
|
<source>Spam</source>
|
||||||
<target>Spam</target>
|
<target>Spam</target>
|
||||||
<note>Description of report type for spam.
|
<note>Description of report type for spam.</note>
|
||||||
Section header for Universe/Search spam</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Staging" xml:space="preserve">
|
<trans-unit id="Staging" xml:space="preserve">
|
||||||
<source>Staging</source>
|
<source>Staging</source>
|
||||||
@@ -2000,23 +2054,23 @@ Label for filter for seeing notes and replies (instead of only notes).</note>
|
|||||||
<target>Take Photo</target>
|
<target>Take Photo</target>
|
||||||
<note>Option to take a photo with the camera</note>
|
<note>Option to take a photo with the camera</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Test (localhost)" xml:space="preserve">
|
<trans-unit id="Test (local)" xml:space="preserve">
|
||||||
<source>Test (localhost)</source>
|
<source>Test (local)</source>
|
||||||
<target>Test (localhost)</target>
|
<target>Test (local)</target>
|
||||||
<note>Label indicating a localhost test environment for Damus Purple functionality (Developer feature)</note>
|
<note>Label indicating a local test environment for Damus Purple functionality (Developer feature)</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Text Truncation" xml:space="preserve">
|
<trans-unit id="Text Truncation" xml:space="preserve">
|
||||||
<source>Text Truncation</source>
|
<source>Text Truncation</source>
|
||||||
<target>Text Truncation</target>
|
<target>Text Truncation</target>
|
||||||
<note>Section header for damus text truncation user configuration</note>
|
<note>Section header for damus text truncation user configuration</note>
|
||||||
</trans-unit>
|
</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. You will also get access to premium features, and a star badge on your profile. Enjoy!" xml:space="preserve">
|
<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. You will also get access to premium features, and a star badge on your profile. 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.
|
<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.
|
You will also get access to premium features, and a star badge on your profile.
|
||||||
|
|
||||||
Enjoy!</source>
|
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.
|
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>
|
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>
|
<note>An error message that appears when the user attempts to add a relay that has already been added.</note>
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<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>
|
<note>Error label when Purple account information fails to load</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="This is a paid relay, you must pay for notes to be accepted." xml:space="preserve">
|
<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 is a paid relay, you must pay for notes to be accepted.</source>
|
<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 is a paid relay, you must pay for notes to be accepted.</target>
|
<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>Footer description that explains that the relay server requires payment to post.</note>
|
<note>Notice label that user cannot manage their In-App purchases</note>
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<target>Thread</target>
|
||||||
<note>Navigation bar title for note thread.</note>
|
<note>Navigation bar title for note thread.</note>
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<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">
|
<trans-unit id="URL" xml:space="preserve">
|
||||||
<source>URL</source>
|
<source>URL</source>
|
||||||
<target>URL</target>
|
<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>
|
||||||
<trans-unit id="Unable to find a QR Code" xml:space="preserve">
|
<trans-unit id="Unable to find a QR Code" xml:space="preserve">
|
||||||
<source>Unable to find a QR Code</source>
|
<source>Unable to find a QR Code</source>
|
||||||
@@ -2180,16 +2245,16 @@ Nice to meet you all! #introductions #plebchain </target>
|
|||||||
<target>Unmute</target>
|
<target>Unmute</target>
|
||||||
<note>Button to unmute a profile.</note>
|
<note>Button to unmute a profile.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Unmute conversation" xml:space="preserve">
|
||||||
<source>Unmute conversation</source>
|
<source>Unmute conversation</source>
|
||||||
<target>Unmute conversation</target>
|
<target>Unmute conversation</target>
|
||||||
<note>Context menu option for unmuting a conversation.</note>
|
<note>Context menu option for unmuting a conversation.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Upload" xml:space="preserve">
|
||||||
<source>Upload</source>
|
<source>Upload</source>
|
||||||
<target>Upload</target>
|
<target>Upload</target>
|
||||||
@@ -2215,6 +2280,16 @@ Nice to meet you all! #introductions #plebchain </target>
|
|||||||
<target>Username</target>
|
<target>Username</target>
|
||||||
<note>Label for Username section of user profile form.</note>
|
<note>Label for Username section of user profile form.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Verified!" xml:space="preserve">
|
||||||
<source>Verified!</source>
|
<source>Verified!</source>
|
||||||
<target>Verified!</target>
|
<target>Verified!</target>
|
||||||
@@ -2228,8 +2303,7 @@ Nice to meet you all! #introductions #plebchain </target>
|
|||||||
<trans-unit id="Version" xml:space="preserve">
|
<trans-unit id="Version" xml:space="preserve">
|
||||||
<source>Version</source>
|
<source>Version</source>
|
||||||
<target>Version</target>
|
<target>Version</target>
|
||||||
<note>Label to display relay software version.
|
<note>Section title for displaying the version number of the Damus app.</note>
|
||||||
Section title for displaying the version number of the Damus app.</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="View QR Code" xml:space="preserve">
|
<trans-unit id="View QR Code" xml:space="preserve">
|
||||||
<source>View QR Code</source>
|
<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">
|
<trans-unit id="Wallet" xml:space="preserve">
|
||||||
<source>Wallet</source>
|
<source>Wallet</source>
|
||||||
<target>Wallet</target>
|
<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.
|
Sidebar menu label for Wallet view.
|
||||||
Title for section in zap settings that controls the Lightning wallet selection.</note>
|
Title for section in zap settings that controls the Lightning wallet selection.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Website" xml:space="preserve">
|
||||||
<source>Website</source>
|
<source>Website</source>
|
||||||
<target>Website</target>
|
<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>
|
<target>Why we need Nostr?</target>
|
||||||
<note>Heading text for section describing why Nostr is needed.</note>
|
<note>Heading text for section describing why Nostr is needed.</note>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="Yes" xml:space="preserve">
|
||||||
<source>Yes</source>
|
<source>Yes</source>
|
||||||
<target>Yes</target>
|
<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>
|
<target>Your Name</target>
|
||||||
<note>Label for Your Name section of user profile form.</note>
|
<note>Label for Your Name section of user profile form.</note>
|
||||||
</trans-unit>
|
</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">
|
<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>
|
<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>
|
<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">
|
<trans-unit id="Zap failed" xml:space="preserve">
|
||||||
<source>Zap failed</source>
|
<source>Zap failed</source>
|
||||||
<target>Zap failed</target>
|
<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)
|
<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>
|
||||||
Title of an alert indicating that a zap action failed</note>
|
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Zap type" xml:space="preserve">
|
<trans-unit id="Zap type" xml:space="preserve">
|
||||||
<source>Zap type</source>
|
<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">
|
<trans-unit id="now" xml:space="preserve">
|
||||||
<source>now</source>
|
<source>now</source>
|
||||||
<target>now</target>
|
<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>
|
||||||
<trans-unit id="nsec1..." xml:space="preserve">
|
<trans-unit id="nsec1..." xml:space="preserve">
|
||||||
<source>nsec1...</source>
|
<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>
|
<target>%@ and %@ zapped you</target>
|
||||||
<note>Notification that 2 users zapped the current user's profile</note>
|
<note>Notification that 2 users zapped the current user's profile</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="⚡" xml:space="preserve">
|
|
||||||
<source>⚡</source>
|
|
||||||
<target>⚡</target>
|
|
||||||
<note>Placeholder example for an emoji reaction</note>
|
|
||||||
</trans-unit>
|
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext">
|
<file original="damus/en-US.lproj/Localizable.stringsdict" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="/followed_by_three_and_others:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
<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>
|
<target>%#@IMPORTS@</target>
|
||||||
<note/>
|
<note/>
|
||||||
</trans-unit>
|
</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">
|
<trans-unit id="/reacted_tagged_in_3:dict/NSStringLocalizedFormatKey:dict/:string" xml:space="preserve">
|
||||||
<source>%#@REACTED@</source>
|
<source>%#@REACTED@</source>
|
||||||
<target>%#@REACTED@</target>
|
<target>%#@REACTED@</target>
|
||||||
@@ -2963,7 +3083,7 @@ YOU WILL NO LONGER BE ABLE TO LOG INTO DAMUS USING THIS ACCOUNT KEY.
|
|||||||
</file>
|
</file>
|
||||||
<file original="DamusNotificationService/InfoPlist.xcstrings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
<file original="DamusNotificationService/InfoPlist.xcstrings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
<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>
|
||||||
<file original="DamusNotificationService/Localizable.xcstrings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
<file original="DamusNotificationService/Localizable.xcstrings" source-language="en-US" target-language="en-US" datatype="plaintext">
|
||||||
<header>
|
<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>
|
</header>
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="" xml:space="preserve">
|
<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>
|
<target state="new">Staging</target>
|
||||||
<note>Label indicating a staging test environment for Damus Purple functionality (Developer feature)</note>
|
<note>Label indicating a staging test environment for Damus Purple functionality (Developer feature)</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Test (localhost)" xml:space="preserve">
|
<trans-unit id="Test (local)" xml:space="preserve">
|
||||||
<source>Test (localhost)</source>
|
<source>Test (local)</source>
|
||||||
<target state="new">Test (localhost)</target>
|
<target state="new">Test (local)</target>
|
||||||
<note>Label indicating a localhost test environment for Damus Purple functionality (Developer feature)</note>
|
<note>Label indicating a local test environment for Damus Purple functionality (Developer feature)</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="This note contains too many items and cannot be rendered" xml:space="preserve">
|
<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>
|
<source>This note contains too many items and cannot be rendered</source>
|
||||||
|
|||||||
@@ -96,8 +96,8 @@
|
|||||||
"Staging" : {
|
"Staging" : {
|
||||||
"comment" : "Label indicating a staging test environment for Damus Purple functionality (Developer feature)"
|
"comment" : "Label indicating a staging test environment for Damus Purple functionality (Developer feature)"
|
||||||
},
|
},
|
||||||
"Test (localhost)" : {
|
"Test (local)" : {
|
||||||
"comment" : "Label indicating a localhost test environment for Damus Purple functionality (Developer feature)"
|
"comment" : "Label indicating a local test environment for Damus Purple functionality (Developer feature)"
|
||||||
},
|
},
|
||||||
"This note contains too many items and cannot be rendered" : {
|
"This note contains too many items and cannot be rendered" : {
|
||||||
"comment" : "Error message indicating that a note is too big and cannot be rendered"
|
"comment" : "Error message indicating that a note is too big and cannot be rendered"
|
||||||
|
|||||||
Binary file not shown.
@@ -226,6 +226,22 @@
|
|||||||
<string>Reposts</string>
|
<string>Reposts</string>
|
||||||
</dict>
|
</dict>
|
||||||
</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>
|
<key>sats</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
"project" : "damus.xcodeproj",
|
"project" : "damus.xcodeproj",
|
||||||
"targetLocale" : "en-US",
|
"targetLocale" : "en-US",
|
||||||
"toolInfo" : {
|
"toolInfo" : {
|
||||||
"toolBuildNumber" : "15C500b",
|
"toolBuildNumber" : "15E204a",
|
||||||
"toolID" : "com.apple.dt.xcode",
|
"toolID" : "com.apple.dt.xcode",
|
||||||
"toolName" : "Xcode",
|
"toolName" : "Xcode",
|
||||||
"toolVersion" : "15.2"
|
"toolVersion" : "15.3"
|
||||||
},
|
},
|
||||||
"version" : "1.0"
|
"version" : "1.0"
|
||||||
}
|
}
|
||||||
@@ -254,6 +254,24 @@
|
|||||||
<string>Republicaciones</string>
|
<string>Republicaciones</string>
|
||||||
</dict>
|
</dict>
|
||||||
</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>
|
<key>sats</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -226,6 +226,22 @@
|
|||||||
<string>Herplaatsingen</string>
|
<string>Herplaatsingen</string>
|
||||||
</dict>
|
</dict>
|
||||||
</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>
|
<key>sats</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
|||||||
Binary file not shown.
@@ -99,12 +99,4 @@ final class WalletConnectTests: XCTestCase {
|
|||||||
XCTAssertEqual(ev.remaining.count, 1)
|
XCTAssertEqual(ev.remaining.count, 1)
|
||||||
XCTAssertEqual(ev.remaining[0].relay.url.absoluteString, "ws://127.0.0.1")
|
XCTAssertEqual(ev.remaining[0].relay.url.absoluteString, "ws://127.0.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPerformanceExample() throws {
|
|
||||||
// This is an example of a performance test case.
|
|
||||||
self.measure {
|
|
||||||
// Put the code you want to measure the time of here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,6 @@ class damusTests: XCTestCase {
|
|||||||
XCTAssertEqual(pubkey, pubkey_same)
|
XCTAssertEqual(pubkey, pubkey_same)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPerformanceExample() throws {
|
|
||||||
// This is an example of a performance test case.
|
|
||||||
self.measure {
|
|
||||||
// Put the code you want to measure the time of here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRandomBytes() {
|
func testRandomBytes() {
|
||||||
let bytes = random_bytes(count: 32)
|
let bytes = random_bytes(count: 32)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user