Compare commits
58 Commits
localizati
...
preferred-
| Author | SHA1 | Date | |
|---|---|---|---|
|
8e852ed742
|
|||
|
7b4fc79030
|
|||
|
|
7a4af31859 | ||
|
|
e106be1412 | ||
|
|
282bf80daa | ||
|
|
bcb861a61b | ||
|
|
bb0ad18913 | ||
|
|
81830c7540 | ||
|
|
68128b5ff1 | ||
|
|
aebeb26bc6 | ||
|
|
79cf3db279 | ||
|
|
dcae0d2cc7 | ||
|
|
2b12dc5920 | ||
|
|
51930e7a12 | ||
|
|
b04e09d2e0 | ||
| b6c4213515 | |||
|
|
8230c6eded | ||
|
|
e79590f795 | ||
|
|
79bced1246 | ||
|
|
896f4b55e3 | ||
|
|
52e65f9429 | ||
|
|
a22cc532e2 | ||
|
|
823227920c | ||
|
|
3e2bbce25e | ||
|
|
e05b2d9ecf | ||
|
|
d7b31a1cd8 | ||
|
|
70f01c0880 | ||
|
|
2cf5f21f78 | ||
|
|
96e8f8b6b2 | ||
|
|
370cfd1b08 | ||
|
|
046af15734 | ||
|
|
9e4ab2d54c | ||
|
|
7cf12e2e0d | ||
|
|
a63a81b387 | ||
|
|
d994cd13dc | ||
|
|
95e985cfce | ||
|
|
3a69de9274 | ||
|
|
64f5acf98c | ||
|
|
5167ab264d | ||
|
|
e02895b29f | ||
|
|
0009d11025 | ||
|
|
afc317bb52 | ||
|
|
629212ea23 | ||
|
|
ec1252200f | ||
|
|
54ea1ab803 | ||
|
|
4cf8097de4 | ||
|
|
2c7384b0a9 | ||
|
|
19e312a8fb | ||
|
|
3986308638 | ||
| fa7740948b | |||
| 892a1420f3 | |||
| ee4cbf7363 | |||
| a1b1ce949b | |||
| 902e8c3950 | |||
| b776788b38 | |||
|
|
78066773f4 | ||
|
|
0bac284eee | ||
|
|
07c95d1003 |
@@ -406,9 +406,10 @@
|
||||
5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6E1DAC2A193EC2008FC15A /* GradientButtonStyle.swift */; };
|
||||
5C6E1DAF2A194075008FC15A /* PinkGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6E1DAE2A194075008FC15A /* PinkGradient.swift */; };
|
||||
5C7389B12B6EFA7100781E0A /* ProxyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B02B6EFA7100781E0A /* ProxyView.swift */; };
|
||||
5C7389B72B9E692E00781E0A /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; };
|
||||
5C7389B92B9E69ED00781E0A /* MutinyGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */; };
|
||||
5C8711DE2C460C06007879C2 /* PostingTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C8711DD2C460C06007879C2 /* PostingTimelineView.swift */; };
|
||||
5CB017212D2D985E00A9ED05 /* CoinosButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB017202D2D985800A9ED05 /* CoinosButton.swift */; };
|
||||
5CB017222D2D985E00A9ED05 /* CoinosButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB017202D2D985800A9ED05 /* CoinosButton.swift */; };
|
||||
5CB017232D2D985E00A9ED05 /* CoinosButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB017202D2D985800A9ED05 /* CoinosButton.swift */; };
|
||||
5CC8529D2BD741CD0039FFC5 /* HighlightEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC8529C2BD741CD0039FFC5 /* HighlightEvent.swift */; };
|
||||
5CC8529F2BD744F60039FFC5 /* HighlightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC8529E2BD744F60039FFC5 /* HighlightView.swift */; };
|
||||
5CC852A22BDED9B90039FFC5 /* HighlightDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC852A12BDED9B90039FFC5 /* HighlightDescription.swift */; };
|
||||
@@ -535,7 +536,6 @@
|
||||
82D6FB0F2CD99F7900C925F4 /* DamusLogoGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */; };
|
||||
82D6FB102CD99F7900C925F4 /* DamusBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C202A5F7ED00092C550 /* DamusBackground.swift */; };
|
||||
82D6FB112CD99F7900C925F4 /* DamusLightGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */; };
|
||||
82D6FB122CD99F7900C925F4 /* MutinyGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */; };
|
||||
82D6FB132CD99F7900C925F4 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||
82D6FB142CD99F7900C925F4 /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
||||
82D6FB152CD99F7900C925F4 /* ImageCarousel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C06670528FCB08600038D2A /* ImageCarousel.swift */; };
|
||||
@@ -733,7 +733,6 @@
|
||||
82D6FBD82CD99F7900C925F4 /* FriendsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8D1A6E29F31E5000ACDF75 /* FriendsButton.swift */; };
|
||||
82D6FBD92CD99F7900C925F4 /* GradientFollowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F71694F32A6732B7001F4053 /* GradientFollowButton.swift */; };
|
||||
82D6FBDA2CD99F7900C925F4 /* AlbyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7D09652A0AE62100943473 /* AlbyButton.swift */; };
|
||||
82D6FBDB2CD99F7900C925F4 /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; };
|
||||
82D6FBDC2CD99F7900C925F4 /* DamusVideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A2929DDF54400516EAC /* DamusVideoPlayerView.swift */; };
|
||||
82D6FBDD2CD99F7900C925F4 /* DamusVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayer.swift */; };
|
||||
82D6FBDE2CD99F7900C925F4 /* DamusVideoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */; };
|
||||
@@ -1127,7 +1126,6 @@
|
||||
D73E5E442C6A97F4007EB227 /* DamusLogoGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */; };
|
||||
D73E5E452C6A97F4007EB227 /* DamusBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C687C202A5F7ED00092C550 /* DamusBackground.swift */; };
|
||||
D73E5E462C6A97F4007EB227 /* DamusLightGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */; };
|
||||
D73E5E472C6A97F4007EB227 /* MutinyGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */; };
|
||||
D73E5E482C6A97F4007EB227 /* Shimmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D2E846295218AF006D67F8 /* Shimmer.swift */; };
|
||||
D73E5E492C6A97F4007EB227 /* EndBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD7641A28A1641400B6928F /* EndBlock.swift */; };
|
||||
D73E5E4D2C6A97F4007EB227 /* NIP05Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB8838A296F6E1E00DC99E7 /* NIP05Badge.swift */; };
|
||||
@@ -1255,7 +1253,6 @@
|
||||
D73E5ED42C6A97F4007EB227 /* FriendsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8D1A6E29F31E5000ACDF75 /* FriendsButton.swift */; };
|
||||
D73E5ED52C6A97F4007EB227 /* GradientFollowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F71694F32A6732B7001F4053 /* GradientFollowButton.swift */; };
|
||||
D73E5ED62C6A97F4007EB227 /* AlbyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7D09652A0AE62100943473 /* AlbyButton.swift */; };
|
||||
D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7389B62B9E692E00781E0A /* MutinyButton.swift */; };
|
||||
D73E5ED82C6A97F4007EB227 /* DamusVideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A9A2929DDF54400516EAC /* DamusVideoPlayerView.swift */; };
|
||||
D73E5ED92C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFC2AA7525700DFEC1F /* DamusVideoPlayer.swift */; };
|
||||
D73E5EDA2C6A97F4007EB227 /* DamusVideoCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A16FFE2AA76A0900DFEC1F /* DamusVideoCoordinator.swift */; };
|
||||
@@ -1485,6 +1482,7 @@
|
||||
D798D22E2B086E4800234419 /* NostrResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB028049D510006080F /* NostrResponse.swift */; };
|
||||
D79C4C172AFEB061003A41B4 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D79C4C162AFEB061003A41B4 /* NotificationService.swift */; };
|
||||
D79C4C1B2AFEB061003A41B4 /* DamusNotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D79C4C142AFEB061003A41B4 /* DamusNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
D7A0D8752D1FE67900DCBE59 /* EditPictureControlTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A0D8742D1FE66A00DCBE59 /* EditPictureControlTests.swift */; };
|
||||
D7A343EE2AD0D77C00CED48B /* InlineSnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = D7A343ED2AD0D77C00CED48B /* InlineSnapshotTesting */; };
|
||||
D7A343F02AD0D77C00CED48B /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = D7A343EF2AD0D77C00CED48B /* SnapshotTesting */; };
|
||||
D7ADD3DE2B53854300F104C4 /* DamusPurpleURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3DD2B53854300F104C4 /* DamusPurpleURL.swift */; };
|
||||
@@ -1492,6 +1490,9 @@
|
||||
D7ADD3E22B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3E12B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift */; };
|
||||
D7B76C902C825042003A16CB /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; };
|
||||
D7B76C912C82507F003A16CB /* NIP98AuthenticatedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6787D2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift */; };
|
||||
D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */; };
|
||||
D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0C2D12E34900A3BACF /* SwiftyCrop */; };
|
||||
D7C48C0F2D12E35600A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0E2D12E35600A3BACF /* SwiftyCrop */; };
|
||||
D7C6787E2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6787D2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift */; };
|
||||
D7C9701E2C890FC500C56602 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
|
||||
D7C9701F2C890FEB00C56602 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 3A4325AA2961E11400BFCD9D /* Localizable.stringsdict */; };
|
||||
@@ -2335,9 +2336,8 @@
|
||||
5C6E1DAC2A193EC2008FC15A /* GradientButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientButtonStyle.swift; sourceTree = "<group>"; };
|
||||
5C6E1DAE2A194075008FC15A /* PinkGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinkGradient.swift; sourceTree = "<group>"; };
|
||||
5C7389B02B6EFA7100781E0A /* ProxyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyView.swift; sourceTree = "<group>"; };
|
||||
5C7389B62B9E692E00781E0A /* MutinyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutinyButton.swift; sourceTree = "<group>"; };
|
||||
5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutinyGradient.swift; sourceTree = "<group>"; };
|
||||
5C8711DD2C460C06007879C2 /* PostingTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostingTimelineView.swift; sourceTree = "<group>"; };
|
||||
5CB017202D2D985800A9ED05 /* CoinosButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoinosButton.swift; sourceTree = "<group>"; };
|
||||
5CC8529C2BD741CD0039FFC5 /* HighlightEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightEvent.swift; sourceTree = "<group>"; };
|
||||
5CC8529E2BD744F60039FFC5 /* HighlightView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightView.swift; sourceTree = "<group>"; };
|
||||
5CC852A12BDED9B90039FFC5 /* HighlightDescription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightDescription.swift; sourceTree = "<group>"; };
|
||||
@@ -2446,6 +2446,7 @@
|
||||
D79C4C162AFEB061003A41B4 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
D79C4C182AFEB061003A41B4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D79C4C1C2AFEB061003A41B4 /* DamusNotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DamusNotificationService.entitlements; sourceTree = "<group>"; };
|
||||
D7A0D8742D1FE66A00DCBE59 /* EditPictureControlTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditPictureControlTests.swift; sourceTree = "<group>"; };
|
||||
D7ADD3DD2B53854300F104C4 /* DamusPurpleURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleURL.swift; sourceTree = "<group>"; };
|
||||
D7ADD3DF2B538D4200F104C4 /* DamusPurpleURLSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleURLSheetView.swift; sourceTree = "<group>"; };
|
||||
D7ADD3E12B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleVerifyNpubView.swift; sourceTree = "<group>"; };
|
||||
@@ -2508,6 +2509,7 @@
|
||||
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
|
||||
3A0A30BB2C21397A00F8C9BC /* EmojiPicker in Frameworks */,
|
||||
D70D90982CDED61800CD0534 /* CodeScanner in Frameworks */,
|
||||
D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */,
|
||||
D78DB8592C1CE9CA00F0AB12 /* SwipeActions in Frameworks */,
|
||||
4C649881286E0EE300EAE2B3 /* secp256k1 in Frameworks */,
|
||||
4C27C9322A64766F007DBC75 /* MarkdownUI in Frameworks */,
|
||||
@@ -2537,6 +2539,7 @@
|
||||
82D6FC862CD9A4A600C925F4 /* MarkdownUI in Frameworks */,
|
||||
82D6FC8A2CD9A54600C925F4 /* SwipeActions in Frameworks */,
|
||||
D7F360292CEBBE34009D34DA /* CodeScanner in Frameworks */,
|
||||
D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */,
|
||||
82D6FC882CD9A4DE00C925F4 /* EmojiPicker in Frameworks */,
|
||||
82D6FC842CD9A48500C925F4 /* Kingfisher in Frameworks */,
|
||||
82D6FC812CD99FC500C925F4 /* secp256k1 in Frameworks */,
|
||||
@@ -2551,6 +2554,7 @@
|
||||
D73E5F9D2C6AA8E3007EB227 /* SwipeActions in Frameworks */,
|
||||
D73E5F762C6A997E007EB227 /* EmojiPicker in Frameworks */,
|
||||
D703D7192C66E47100A400EA /* UniformTypeIdentifiers.framework in Frameworks */,
|
||||
D7C48C0F2D12E35600A3BACF /* SwiftyCrop in Frameworks */,
|
||||
D703D7492C6709B100A400EA /* secp256k1 in Frameworks */,
|
||||
D70D909C2CDED7B200CD0534 /* CodeScanner in Frameworks */,
|
||||
D73E5F9B2C6AA8B0007EB227 /* Kingfisher in Frameworks */,
|
||||
@@ -3184,7 +3188,6 @@
|
||||
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */,
|
||||
4C687C202A5F7ED00092C550 /* DamusBackground.swift */,
|
||||
5CF2DCCD2AABE1A500984B8D /* DamusLightGradient.swift */,
|
||||
5C7389B82B9E69ED00781E0A /* MutinyGradient.swift */,
|
||||
);
|
||||
path = Gradients;
|
||||
sourceTree = "<group>";
|
||||
@@ -3253,10 +3256,10 @@
|
||||
4C8D1A6D29F31E4100ACDF75 /* Buttons */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CB017202D2D985800A9ED05 /* CoinosButton.swift */,
|
||||
4C8D1A6E29F31E5000ACDF75 /* FriendsButton.swift */,
|
||||
F71694F32A6732B7001F4053 /* GradientFollowButton.swift */,
|
||||
4C7D09652A0AE62100943473 /* AlbyButton.swift */,
|
||||
5C7389B62B9E692E00781E0A /* MutinyButton.swift */,
|
||||
);
|
||||
path = Buttons;
|
||||
sourceTree = "<group>";
|
||||
@@ -3590,6 +3593,7 @@
|
||||
4CE6DEF627F7A08200C66700 /* damusTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D7A0D8742D1FE66A00DCBE59 /* EditPictureControlTests.swift */,
|
||||
E06336A72B7582D600A88E6B /* Assets */,
|
||||
D72A2D032AD9C165002AFF62 /* Mocking */,
|
||||
4C9B0DEC2A65A74000CBDA21 /* Util */,
|
||||
@@ -3967,6 +3971,7 @@
|
||||
3A0A30BA2C21397A00F8C9BC /* EmojiPicker */,
|
||||
D78DB8582C1CE9CA00F0AB12 /* SwipeActions */,
|
||||
D70D90972CDED61800CD0534 /* CodeScanner */,
|
||||
D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */,
|
||||
);
|
||||
productName = damus;
|
||||
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
|
||||
@@ -4032,6 +4037,7 @@
|
||||
82D6FC872CD9A4DE00C925F4 /* EmojiPicker */,
|
||||
82D6FC892CD9A54600C925F4 /* SwipeActions */,
|
||||
D7F360282CEBBE34009D34DA /* CodeScanner */,
|
||||
D7C48C0C2D12E34900A3BACF /* SwiftyCrop */,
|
||||
);
|
||||
productName = "share extension";
|
||||
productReference = 82D6FA972CD9820500C925F4 /* ShareExtension.appex */;
|
||||
@@ -4059,6 +4065,7 @@
|
||||
D73E5F9A2C6AA8B0007EB227 /* Kingfisher */,
|
||||
D73E5F9C2C6AA8E3007EB227 /* SwipeActions */,
|
||||
D70D909B2CDED7B200CD0534 /* CodeScanner */,
|
||||
D7C48C0E2D12E35600A3BACF /* SwiftyCrop */,
|
||||
);
|
||||
productName = "highlighter action extension";
|
||||
productReference = D703D7172C66E47100A400EA /* HighlighterActionExtension.appex */;
|
||||
@@ -4167,6 +4174,7 @@
|
||||
3A0A30B92C21397A00F8C9BC /* XCRemoteSwiftPackageReference "EmojiPicker" */,
|
||||
D78DB8572C1CE9CA00F0AB12 /* XCRemoteSwiftPackageReference "SwipeActions" */,
|
||||
D70D90962CDED61800CD0534 /* XCRemoteSwiftPackageReference "CodeScanner" */,
|
||||
D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */,
|
||||
);
|
||||
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -4299,7 +4307,6 @@
|
||||
4C30AC7829A577AB00E2BD5A /* EventCache.swift in Sources */,
|
||||
4C285C8428385690008A31F1 /* CreateAccountView.swift in Sources */,
|
||||
4CDD1AE22A6B3074001CD4DF /* NdbTagsIterator.swift in Sources */,
|
||||
5C7389B72B9E692E00781E0A /* MutinyButton.swift in Sources */,
|
||||
4C216F34286F5ACD00040376 /* DMView.swift in Sources */,
|
||||
D7CB5D512B1174D100AD4105 /* FriendFilter.swift in Sources */,
|
||||
D7CBD1D42B8D21DC00BFD889 /* DamusPurpleNotificationManagement.swift in Sources */,
|
||||
@@ -4604,7 +4611,6 @@
|
||||
4CF0ABD82981980C00D66079 /* Lists.swift in Sources */,
|
||||
F71694EA2A662232001F4053 /* OnboardingSuggestionsView.swift in Sources */,
|
||||
4C12536A2A76D3850004F4B8 /* RelaysChangedNotify.swift in Sources */,
|
||||
5C7389B92B9E69ED00781E0A /* MutinyGradient.swift in Sources */,
|
||||
4C30AC8029A6A53F00E2BD5A /* ProfilePicturesView.swift in Sources */,
|
||||
D7373BAA2B68A65A00F7783D /* PurpleAccountUpdateNotify.swift in Sources */,
|
||||
5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */,
|
||||
@@ -4702,6 +4708,7 @@
|
||||
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */,
|
||||
4C1253622A76D00B0004F4B8 /* PostNotify.swift in Sources */,
|
||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
||||
5CB017232D2D985E00A9ED05 /* CoinosButton.swift in Sources */,
|
||||
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */,
|
||||
4C9147002A2A891E00DDEA40 /* error.c in Sources */,
|
||||
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */,
|
||||
@@ -4795,6 +4802,7 @@
|
||||
D72A2D052AD9C1B5002AFF62 /* MockDamusState.swift in Sources */,
|
||||
E06336AA2B75832100A88E6B /* ImageMetadataTest.swift in Sources */,
|
||||
4C363AA02828A8DD006E126D /* LikeTests.swift in Sources */,
|
||||
D7A0D8752D1FE67900DCBE59 /* EditPictureControlTests.swift in Sources */,
|
||||
4C90BD1C283AC38E008EE7EF /* Bech32Tests.swift in Sources */,
|
||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */,
|
||||
4CE6DEF827F7A08200C66700 /* damusTests.swift in Sources */,
|
||||
@@ -4926,7 +4934,6 @@
|
||||
82D6FB0F2CD99F7900C925F4 /* DamusLogoGradient.swift in Sources */,
|
||||
82D6FB102CD99F7900C925F4 /* DamusBackground.swift in Sources */,
|
||||
82D6FB112CD99F7900C925F4 /* DamusLightGradient.swift in Sources */,
|
||||
82D6FB122CD99F7900C925F4 /* MutinyGradient.swift in Sources */,
|
||||
82D6FB132CD99F7900C925F4 /* Shimmer.swift in Sources */,
|
||||
82D6FB142CD99F7900C925F4 /* EndBlock.swift in Sources */,
|
||||
82D6FB152CD99F7900C925F4 /* ImageCarousel.swift in Sources */,
|
||||
@@ -5124,7 +5131,6 @@
|
||||
82D6FBD82CD99F7900C925F4 /* FriendsButton.swift in Sources */,
|
||||
82D6FBD92CD99F7900C925F4 /* GradientFollowButton.swift in Sources */,
|
||||
82D6FBDA2CD99F7900C925F4 /* AlbyButton.swift in Sources */,
|
||||
82D6FBDB2CD99F7900C925F4 /* MutinyButton.swift in Sources */,
|
||||
82D6FBDC2CD99F7900C925F4 /* DamusVideoPlayerView.swift in Sources */,
|
||||
82D6FBDD2CD99F7900C925F4 /* DamusVideoPlayer.swift in Sources */,
|
||||
82D6FBDE2CD99F7900C925F4 /* DamusVideoCoordinator.swift in Sources */,
|
||||
@@ -5179,6 +5185,7 @@
|
||||
82D6FC0E2CD99F7900C925F4 /* ProfilePicView.swift in Sources */,
|
||||
82D6FC0F2CD99F7900C925F4 /* ProfileView.swift in Sources */,
|
||||
82D6FC102CD99F7900C925F4 /* ProfileNameView.swift in Sources */,
|
||||
5CB017212D2D985E00A9ED05 /* CoinosButton.swift in Sources */,
|
||||
82D6FC112CD99F7900C925F4 /* MaybeAnonPfpView.swift in Sources */,
|
||||
82D6FC122CD99F7900C925F4 /* EventProfileName.swift in Sources */,
|
||||
82D6FC132CD99F7900C925F4 /* FriendIcon.swift in Sources */,
|
||||
@@ -5337,7 +5344,6 @@
|
||||
D73E5E442C6A97F4007EB227 /* DamusLogoGradient.swift in Sources */,
|
||||
D73E5E452C6A97F4007EB227 /* DamusBackground.swift in Sources */,
|
||||
D73E5E462C6A97F4007EB227 /* DamusLightGradient.swift in Sources */,
|
||||
D73E5E472C6A97F4007EB227 /* MutinyGradient.swift in Sources */,
|
||||
D73E5E482C6A97F4007EB227 /* Shimmer.swift in Sources */,
|
||||
D73E5E492C6A97F4007EB227 /* EndBlock.swift in Sources */,
|
||||
D73E5E4D2C6A97F4007EB227 /* NIP05Badge.swift in Sources */,
|
||||
@@ -5457,6 +5463,7 @@
|
||||
D73E5EBB2C6A97F4007EB227 /* Nip98HTTPAuth.swift in Sources */,
|
||||
D73E5EBC2C6A97F4007EB227 /* Relay.swift in Sources */,
|
||||
D73E5EBD2C6A97F4007EB227 /* NostrRequest.swift in Sources */,
|
||||
5CB017222D2D985E00A9ED05 /* CoinosButton.swift in Sources */,
|
||||
D73E5EBE2C6A97F4007EB227 /* NostrLink.swift in Sources */,
|
||||
D73E5EBF2C6A97F4007EB227 /* WebSocket.swift in Sources */,
|
||||
D73E5F812C6AA07A007EB227 /* HighlighterExtensionAliases.swift in Sources */,
|
||||
@@ -5476,7 +5483,6 @@
|
||||
D73E5ED42C6A97F4007EB227 /* FriendsButton.swift in Sources */,
|
||||
D73E5ED52C6A97F4007EB227 /* GradientFollowButton.swift in Sources */,
|
||||
D73E5ED62C6A97F4007EB227 /* AlbyButton.swift in Sources */,
|
||||
D73E5ED72C6A97F4007EB227 /* MutinyButton.swift in Sources */,
|
||||
D73E5ED82C6A97F4007EB227 /* DamusVideoPlayerView.swift in Sources */,
|
||||
D73E5ED92C6A97F4007EB227 /* DamusVideoPlayer.swift in Sources */,
|
||||
D73E5EDA2C6A97F4007EB227 /* DamusVideoCoordinator.swift in Sources */,
|
||||
@@ -6251,6 +6257,7 @@
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "damus-c/damus-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
@@ -6300,6 +6307,7 @@
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "damus-c/damus-Bridging-Header.h";
|
||||
SWIFT_STRICT_CONCURRENCY = complete;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
@@ -6711,6 +6719,14 @@
|
||||
minimumVersion = 1.14.1;
|
||||
};
|
||||
};
|
||||
D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/benedom/SwiftyCrop";
|
||||
requirement = {
|
||||
kind = revision;
|
||||
revision = 454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
@@ -6824,6 +6840,21 @@
|
||||
package = D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */;
|
||||
productName = SnapshotTesting;
|
||||
};
|
||||
D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */;
|
||||
productName = SwiftyCrop;
|
||||
};
|
||||
D7C48C0C2D12E34900A3BACF /* SwiftyCrop */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */;
|
||||
productName = SwiftyCrop;
|
||||
};
|
||||
D7C48C0E2D12E35600A3BACF /* SwiftyCrop */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */;
|
||||
productName = SwiftyCrop;
|
||||
};
|
||||
D7EDED242B117F7C0018B19C /* MarkdownUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 4C27C9302A64766F007DBC75 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "534c8e58993919d5ead25ceb4788c8e492c86bc2cf5833dc651ae60a0f30169c",
|
||||
"originHash" : "0d806129a33991730dd1aa3d38c47a745f9e9e6ff44999f4a7f28b695f024832",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "codescanner",
|
||||
@@ -97,6 +97,14 @@
|
||||
"version" : "0.1.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftycrop",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/benedom/SwiftyCrop",
|
||||
"state" : {
|
||||
"revision" : "454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swipeactions",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 511 KiB After Width: | Height: | Size: 511 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 547 KiB After Width: | Height: | Size: 547 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
6
damus/Assets.xcassets/Logos/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
23
damus/Assets.xcassets/Logos/alby.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "alby.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "alby.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "alby.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 300 KiB After Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "profile-banner.jpeg",
|
||||
"filename" : "coinos.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
BIN
damus/Assets.xcassets/Logos/coinos.imageset/coinos.png
vendored
Normal file
|
After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 290 KiB |
12
damus/Assets.xcassets/alby-go.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "alby-go.png",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
damus/Assets.xcassets/alby-go.imageset/alby-go.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "alby.svg",
|
||||
"idiom": "universal",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "2x",
|
||||
"filename": "alby.svg"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"scale": "3x",
|
||||
"filename": "alby.svg"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// MutinyGradient.swift
|
||||
// damus
|
||||
//
|
||||
// Created by eric on 3/9/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
fileprivate let mutiny_grad_c1 = hex_col(r: 39, g: 95, b: 161)
|
||||
fileprivate let mutiny_grad_c2 = hex_col(r: 13, g: 33, b: 56)
|
||||
fileprivate let mutiny_grad = [mutiny_grad_c2, mutiny_grad_c1]
|
||||
|
||||
let MutinyGradient: LinearGradient =
|
||||
LinearGradient(colors: mutiny_grad, startPoint: .top, endPoint: .bottom)
|
||||
@@ -931,7 +931,6 @@ enum FindEventType {
|
||||
|
||||
enum FoundEvent {
|
||||
case profile(Pubkey)
|
||||
case invalid_profile(NostrEvent)
|
||||
case event(NostrEvent)
|
||||
}
|
||||
|
||||
@@ -988,10 +987,6 @@ func find_event_with_subid(state: DamusState, query query_: FindEvent, subid: St
|
||||
switch query {
|
||||
case .profile:
|
||||
if ev.known_kind == .metadata {
|
||||
guard state.ndb.lookup_profile_key(ev.pubkey) != nil else {
|
||||
callback(.invalid_profile(ev))
|
||||
return
|
||||
}
|
||||
callback(.profile(ev.pubkey))
|
||||
}
|
||||
case .event:
|
||||
@@ -1000,17 +995,16 @@ func find_event_with_subid(state: DamusState, query query_: FindEvent, subid: St
|
||||
case .eose:
|
||||
if !has_event {
|
||||
attempts += 1
|
||||
if attempts == state.pool.our_descriptors.count / 2 {
|
||||
callback(nil)
|
||||
if attempts >= state.pool.our_descriptors.count {
|
||||
callback(nil) // If we could not find any events in any of the relays we are connected to, send back nil
|
||||
}
|
||||
state.pool.unsubscribe(sub_id: subid, to: [relay_id])
|
||||
}
|
||||
state.pool.unsubscribe(sub_id: subid, to: [relay_id]) // We are only finding an event once, so close subscription on eose
|
||||
case .notice:
|
||||
break
|
||||
case .auth:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>river</string>
|
||||
<string>alby</string>
|
||||
<string>albygo</string>
|
||||
<string>bitcoinbeach</string>
|
||||
<string>breez</string>
|
||||
<string>muun</string>
|
||||
|
||||
@@ -77,11 +77,19 @@ enum MediaUpload {
|
||||
}
|
||||
}
|
||||
|
||||
class ImageUploadModel: NSObject, URLSessionTaskDelegate, ObservableObject {
|
||||
protocol ImageUploadModelProtocol {
|
||||
init()
|
||||
|
||||
func start(media: MediaUpload, uploader: any MediaUploaderProtocol, mediaType: ImageUploadMediaType, keypair: Keypair?) async -> ImageUploadResult
|
||||
}
|
||||
|
||||
class ImageUploadModel: NSObject, URLSessionTaskDelegate, ObservableObject, ImageUploadModelProtocol {
|
||||
@Published var progress: Double? = nil
|
||||
|
||||
func start(media: MediaUpload, uploader: MediaUploader, keypair: Keypair? = nil) async -> ImageUploadResult {
|
||||
let res = await create_upload_request(mediaToUpload: media, mediaUploader: uploader, progress: self, keypair: keypair)
|
||||
override required init() { }
|
||||
|
||||
func start(media: MediaUpload, uploader: any MediaUploaderProtocol, mediaType: ImageUploadMediaType, keypair: Keypair? = nil) async -> ImageUploadResult {
|
||||
let res = await AttachMediaUtility.create_upload_request(mediaToUpload: media, mediaUploader: uploader, mediaType: mediaType, progress: self, keypair: keypair)
|
||||
|
||||
switch res {
|
||||
case .success(_):
|
||||
@@ -89,10 +97,17 @@ class ImageUploadModel: NSObject, URLSessionTaskDelegate, ObservableObject {
|
||||
self.progress = nil
|
||||
UINotificationFeedbackGenerator().notificationOccurred(.success)
|
||||
}
|
||||
case .failed(_):
|
||||
case .failed(let error):
|
||||
DispatchQueue.main.async {
|
||||
self.progress = nil
|
||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||
if let nsError = error as NSError?,
|
||||
nsError.domain == NSURLErrorDomain,
|
||||
nsError.code == NSURLErrorCancelled {
|
||||
print("Upload forced cancelled by user after Cancelling the Post, no feedback triggered.")
|
||||
} else {
|
||||
// Trigger feedback for all other errors
|
||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,18 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum MediaUploader: String, CaseIterable, Identifiable, StringCodable {
|
||||
protocol MediaUploaderProtocol: Identifiable {
|
||||
var nameParam: String { get }
|
||||
var mediaTypeParam: String { get }
|
||||
var supportsVideo: Bool { get }
|
||||
var requiresNip98: Bool { get }
|
||||
var postAPI: String { get }
|
||||
|
||||
func getMediaURL(from data: Data) -> String?
|
||||
func mediaTypeValue(for mediaType: ImageUploadMediaType) -> String?
|
||||
}
|
||||
|
||||
enum MediaUploader: String, CaseIterable, MediaUploaderProtocol, StringCodable {
|
||||
var id: String { self.rawValue }
|
||||
case nostrBuild
|
||||
case nostrcheck
|
||||
@@ -33,6 +44,19 @@ enum MediaUploader: String, CaseIterable, Identifiable, StringCodable {
|
||||
}
|
||||
}
|
||||
|
||||
var mediaTypeParam: String {
|
||||
return "media_type"
|
||||
}
|
||||
|
||||
func mediaTypeValue(for mediaType: ImageUploadMediaType) -> String? {
|
||||
switch mediaType {
|
||||
case .normal:
|
||||
return nil
|
||||
case .profile_picture:
|
||||
return "avatar"
|
||||
}
|
||||
}
|
||||
|
||||
var supportsVideo: Bool {
|
||||
switch self {
|
||||
case .nostrBuild:
|
||||
@@ -42,6 +66,15 @@ enum MediaUploader: String, CaseIterable, Identifiable, StringCodable {
|
||||
}
|
||||
}
|
||||
|
||||
var requiresNip98: Bool {
|
||||
switch self {
|
||||
case .nostrBuild:
|
||||
return true
|
||||
case .nostrcheck:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
struct Model: Identifiable, Hashable {
|
||||
var id: String { self.tag }
|
||||
var index: Int
|
||||
|
||||
@@ -185,9 +185,6 @@ class UserSettingsStore: ObservableObject {
|
||||
|
||||
@Setting(key: "show_music_statuses", default_value: true)
|
||||
var show_music_statuses: Bool
|
||||
|
||||
@Setting(key: "show_only_preferred_languages", default_value: false)
|
||||
var show_only_preferred_languages: Bool
|
||||
|
||||
@Setting(key: "multiple_events_per_pubkey", default_value: false)
|
||||
var multiple_events_per_pubkey: Bool
|
||||
|
||||
@@ -46,6 +46,7 @@ enum Wallet: String, CaseIterable, Identifiable, StringCodable {
|
||||
case bitcoinbeach
|
||||
case blixtwallet
|
||||
case river
|
||||
case albygo
|
||||
|
||||
var model: Model {
|
||||
switch self {
|
||||
@@ -90,6 +91,9 @@ enum Wallet: String, CaseIterable, Identifiable, StringCodable {
|
||||
case .river:
|
||||
return .init(index: 12, tag: "river", displayName: "River", link: "river://",
|
||||
appStoreLink: "https://apps.apple.com/us/app/river-buy-mine-bitcoin/id1536176542", image: "river")
|
||||
case .albygo:
|
||||
return .init(index: 13, tag: "albygo", displayName: "Alby Go", link: "alby:",
|
||||
appStoreLink: "https://apps.apple.com/us/app/alby-go/id6471335774", image: "alby-go")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,11 +259,10 @@ func should_translate(event: NostrEvent, our_keypair: Keypair, note_lang: String
|
||||
}
|
||||
|
||||
if let note_lang {
|
||||
let preferredLanguages = Set(Locale.preferredLanguages.map { localeToLanguage($0) })
|
||||
let currentLanguage = localeToLanguage(Locale.current.identifier)
|
||||
|
||||
// Don't translate if its in our preferred languages
|
||||
guard !preferredLanguages.contains(note_lang) else {
|
||||
// if its the same, give up and don't retry
|
||||
// Don't translate if the note is in our current language
|
||||
guard currentLanguage != note_lang else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,12 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
func bundleForLocale(locale: Locale?) -> Bundle {
|
||||
if locale == nil {
|
||||
return Bundle.main
|
||||
}
|
||||
|
||||
let path = Bundle.main.path(forResource: locale!.identifier, ofType: "lproj")
|
||||
func bundleForLocale(locale: Locale) -> Bundle {
|
||||
let path = Bundle.main.path(forResource: locale.identifier, ofType: "lproj")
|
||||
return path != nil ? (Bundle(path: path!) ?? Bundle.main) : Bundle.main
|
||||
}
|
||||
|
||||
func localizedStringFormat(key: String, locale: Locale?) -> String {
|
||||
func localizedStringFormat(key: String, locale: Locale) -> String {
|
||||
let bundle = bundleForLocale(locale: locale)
|
||||
let fallback = bundleForLocale(locale: Locale(identifier: "en-US")).localizedString(forKey: key, value: nil, table: nil)
|
||||
return bundle.localizedString(forKey: key, value: fallback, table: nil)
|
||||
|
||||
@@ -15,12 +15,30 @@ enum ImageUploadResult {
|
||||
case failed(Error?)
|
||||
}
|
||||
|
||||
fileprivate func create_upload_body(mediaData: Data, boundary: String, mediaUploader: MediaUploader, mediaToUpload: MediaUpload) -> Data {
|
||||
enum ImageUploadMediaType {
|
||||
case normal
|
||||
case profile_picture
|
||||
}
|
||||
|
||||
protocol AttachMediaUtilityProtocol {
|
||||
static func create_upload_request(mediaToUpload: MediaUpload, mediaUploader: any MediaUploaderProtocol, mediaType: ImageUploadMediaType, progress: URLSessionTaskDelegate, keypair: Keypair?) async -> ImageUploadResult
|
||||
}
|
||||
|
||||
class AttachMediaUtility {
|
||||
fileprivate static func create_upload_body(mediaData: Data, boundary: String, mediaUploader: any MediaUploaderProtocol, mediaToUpload: MediaUpload, mediaType: ImageUploadMediaType) -> Data {
|
||||
let mediaTypeFieldValue = mediaUploader.mediaTypeValue(for: mediaType)
|
||||
let mediaTypeFieldEntry: String?
|
||||
if let mediaTypeFieldValue {
|
||||
mediaTypeFieldEntry = "; \(mediaUploader.mediaTypeParam)=\(mediaTypeFieldValue)"
|
||||
}
|
||||
else {
|
||||
mediaTypeFieldEntry = nil
|
||||
}
|
||||
let body = NSMutableData();
|
||||
let contentType = mediaToUpload.mime_type
|
||||
body.appendString(string: "Content-Type: multipart/form-data; boundary=\(boundary)\r\n\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)\(mediaTypeFieldEntry ?? "")\r\n")
|
||||
body.appendString(string: "Content-Type: \(contentType)\r\n\r\n")
|
||||
body.append(mediaData as Data)
|
||||
body.appendString(string: "\r\n")
|
||||
@@ -28,59 +46,60 @@ fileprivate func create_upload_body(mediaData: Data, boundary: String, mediaUplo
|
||||
return body as Data
|
||||
}
|
||||
|
||||
func create_upload_request(mediaToUpload: MediaUpload, mediaUploader: MediaUploader, progress: URLSessionTaskDelegate, keypair: Keypair? = nil) async -> ImageUploadResult {
|
||||
var mediaData: Data?
|
||||
guard let url = URL(string: mediaUploader.postAPI) else {
|
||||
return .failed(nil)
|
||||
}
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST";
|
||||
let boundary = "Boundary-\(UUID().description)"
|
||||
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
// If uploading to a media host that support NIP-98 authorization, add the header
|
||||
if mediaUploader == .nostrBuild || mediaUploader == .nostrcheck,
|
||||
let keypair,
|
||||
let method = request.httpMethod,
|
||||
let signature = create_nip98_signature(keypair: keypair, method: method, url: url) {
|
||||
|
||||
request.setValue(signature, forHTTPHeaderField: "Authorization")
|
||||
}
|
||||
|
||||
switch mediaToUpload {
|
||||
case .image(let url):
|
||||
do {
|
||||
mediaData = try Data(contentsOf: url)
|
||||
} catch {
|
||||
return .failed(error)
|
||||
}
|
||||
case .video(let url):
|
||||
do {
|
||||
mediaData = try Data(contentsOf: url)
|
||||
} catch {
|
||||
return .failed(error)
|
||||
}
|
||||
}
|
||||
|
||||
guard let mediaData else {
|
||||
return .failed(nil)
|
||||
}
|
||||
|
||||
request.httpBody = create_upload_body(mediaData: mediaData, boundary: boundary, mediaUploader: mediaUploader, mediaToUpload: mediaToUpload)
|
||||
|
||||
do {
|
||||
let (data, _) = try await URLSession.shared.data(for: request, delegate: progress)
|
||||
|
||||
guard let url = mediaUploader.getMediaURL(from: data) else {
|
||||
print("Upload failed getting media url")
|
||||
static func create_upload_request(mediaToUpload: MediaUpload, mediaUploader: any MediaUploaderProtocol, mediaType: ImageUploadMediaType, progress: URLSessionTaskDelegate, keypair: Keypair? = nil) async -> ImageUploadResult {
|
||||
var mediaData: Data?
|
||||
guard let url = URL(string: mediaUploader.postAPI) else {
|
||||
return .failed(nil)
|
||||
}
|
||||
|
||||
return .success(url)
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST";
|
||||
let boundary = "Boundary-\(UUID().description)"
|
||||
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
} catch {
|
||||
return .failed(error)
|
||||
// If uploading to a media host that support NIP-98 authorization, add the header
|
||||
if mediaUploader.requiresNip98,
|
||||
let keypair,
|
||||
let method = request.httpMethod,
|
||||
let signature = create_nip98_signature(keypair: keypair, method: method, url: url) {
|
||||
|
||||
request.setValue(signature, forHTTPHeaderField: "Authorization")
|
||||
}
|
||||
|
||||
switch mediaToUpload {
|
||||
case .image(let url):
|
||||
do {
|
||||
mediaData = try Data(contentsOf: url)
|
||||
} catch {
|
||||
return .failed(error)
|
||||
}
|
||||
case .video(let url):
|
||||
do {
|
||||
mediaData = try Data(contentsOf: url)
|
||||
} catch {
|
||||
return .failed(error)
|
||||
}
|
||||
}
|
||||
|
||||
guard let mediaData else {
|
||||
return .failed(nil)
|
||||
}
|
||||
|
||||
request.httpBody = create_upload_body(mediaData: mediaData, boundary: boundary, mediaUploader: mediaUploader, mediaToUpload: mediaToUpload, mediaType: mediaType)
|
||||
|
||||
do {
|
||||
let (data, _) = try await URLSession.shared.data(for: request, delegate: progress)
|
||||
|
||||
guard let url = mediaUploader.getMediaURL(from: data) else {
|
||||
print("Upload failed getting media url")
|
||||
return .failed(nil)
|
||||
}
|
||||
|
||||
return .success(url)
|
||||
|
||||
} catch {
|
||||
return .failed(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,15 @@ struct EditBannerImageView: View {
|
||||
.onFailureImage(defaultImage)
|
||||
.kfClickable()
|
||||
|
||||
EditPictureControl(uploader: damus_state.settings.default_media_uploader, keypair: damus_state.keypair, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
|
||||
EditPictureControl(
|
||||
uploader: damus_state.settings.default_media_uploader,
|
||||
context: .normal,
|
||||
keypair: damus_state.keypair,
|
||||
pubkey: damus_state.pubkey,
|
||||
current_image_url: $banner_image,
|
||||
upload_observer: viewModel,
|
||||
callback: callback
|
||||
)
|
||||
.padding(10)
|
||||
.backwardsCompatibleSafeAreaPadding(self.safeAreaInsets)
|
||||
.accessibilityLabel(NSLocalizedString("Edit banner image", comment: "Accessibility label for edit banner image button"))
|
||||
|
||||
48
damus/Views/Buttons/CoinosButton.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// CoinosButton.swift
|
||||
// damus
|
||||
//
|
||||
// Created by eric on 1/7/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct CoinosButton: View {
|
||||
let action: () -> ()
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
init(action: @escaping () -> ()) {
|
||||
self.action = action
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
action()
|
||||
}) {
|
||||
HStack {
|
||||
Image("coinos")
|
||||
.resizable()
|
||||
.frame(width: 35, height: 35)
|
||||
|
||||
Text("Connect to Coinos", comment: "Button to attach a Coinos Wallet, a service that provides a Lightning wallet for zapping sats. Coinos is the name of the service and should not be translated.")
|
||||
.padding()
|
||||
.bold()
|
||||
}
|
||||
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
||||
.foregroundColor(DamusColors.black)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(GrayGradient, strokeBorder: colorScheme == .light ? DamusColors.black.opacity(0.2) : DamusColors.white.opacity(0.2), lineWidth: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CoinosButton_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CoinosButton(action: {
|
||||
print("mutiny button")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,18 @@ struct GradientFollowButton: View {
|
||||
.fontWeight(.medium)
|
||||
.padding([.top, .bottom], 10)
|
||||
.padding([.leading, .trailing], 12)
|
||||
.background(follow_state == .unfollows ? PinkGradient : GrayGradient)
|
||||
.cornerRadius(12)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(grayBorder, lineWidth: follow_state == .unfollows ? 0 : 1)
|
||||
.frame(width: 100)
|
||||
)
|
||||
.frame(width: 100)
|
||||
.minimumScaleFactor(0.5)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.background(follow_state == .unfollows ? PinkGradient : GrayGradient)
|
||||
.cornerRadius(12)
|
||||
.frame(width: 100)
|
||||
.onReceive(handle_notify(.followed)) { ref in
|
||||
guard target.follow_ref == ref else { return }
|
||||
self.follow_state = .follows
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
//
|
||||
// MutinyButton.swift
|
||||
// damus
|
||||
//
|
||||
// Created by eric on 3/9/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MutinyButton: View {
|
||||
let action: () -> ()
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
init(action: @escaping () -> ()) {
|
||||
self.action = action
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
action()
|
||||
}) {
|
||||
HStack {
|
||||
Image("mutiny")
|
||||
.resizable()
|
||||
.frame(width: 45, height: 45)
|
||||
|
||||
Text("Connect to Mutiny Wallet", comment: "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.")
|
||||
.padding()
|
||||
}
|
||||
.frame(minWidth: 300, maxWidth: .infinity, alignment: .center)
|
||||
.foregroundColor(DamusColors.white)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(MutinyGradient, strokeBorder: colorScheme == .light ? DamusColors.black.opacity(0.2) : DamusColors.white.opacity(0.2), lineWidth: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MutinyButton_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MutinyButton(action: {
|
||||
print("mutiny button")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,14 @@ struct CameraController: UIViewControllerRepresentable {
|
||||
@Environment(\.presentationMode)
|
||||
@Binding private var presentationMode
|
||||
|
||||
let uploader: MediaUploader
|
||||
let done: () -> Void
|
||||
let uploader: any MediaUploaderProtocol
|
||||
var imagesOnly: Bool = false
|
||||
var mode: Mode
|
||||
|
||||
enum Mode {
|
||||
case save_to_library(when_done: () -> Void)
|
||||
case handle_image(handler: (UIImage) -> Void)
|
||||
}
|
||||
|
||||
final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
|
||||
let parent: CameraController
|
||||
@@ -25,18 +30,29 @@ struct CameraController: UIViewControllerRepresentable {
|
||||
}
|
||||
|
||||
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
||||
if !parent.imagesOnly, let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
|
||||
// Handle the selected video
|
||||
UISaveVideoAtPathToSavedPhotosAlbum(videoURL.relativePath, nil, nil, nil)
|
||||
} else if let cameraImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
|
||||
let orientedImage = cameraImage.fixOrientation()
|
||||
UIImageWriteToSavedPhotosAlbum(orientedImage, nil, nil, nil)
|
||||
} else if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
|
||||
let orientedImage = editedImage.fixOrientation()
|
||||
UIImageWriteToSavedPhotosAlbum(orientedImage, nil, nil, nil)
|
||||
switch parent.mode {
|
||||
case .save_to_library(when_done: let done):
|
||||
if !parent.imagesOnly, let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
|
||||
// Handle the selected video
|
||||
UISaveVideoAtPathToSavedPhotosAlbum(videoURL.relativePath, nil, nil, nil)
|
||||
} else if let cameraImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
|
||||
let orientedImage = cameraImage.fixOrientation()
|
||||
UIImageWriteToSavedPhotosAlbum(orientedImage, nil, nil, nil)
|
||||
} else if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
|
||||
let orientedImage = editedImage.fixOrientation()
|
||||
UIImageWriteToSavedPhotosAlbum(orientedImage, nil, nil, nil)
|
||||
}
|
||||
done()
|
||||
case .handle_image(handler: let handler):
|
||||
if let cameraImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
|
||||
let orientedImage = cameraImage.fixOrientation()
|
||||
handler(orientedImage)
|
||||
} else if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
|
||||
let orientedImage = editedImage.fixOrientation()
|
||||
handler(orientedImage)
|
||||
}
|
||||
}
|
||||
|
||||
parent.done()
|
||||
}
|
||||
|
||||
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct CreateAccountView: View {
|
||||
struct CreateAccountView: View, KeyboardReadable {
|
||||
@StateObject var account: CreateAccountModel = CreateAccountModel()
|
||||
@StateObject var profileUploadObserver = ImageUploadingObserver()
|
||||
var nav: NavigationCoordinator
|
||||
@State var keyboardVisible: Bool = false
|
||||
let maxViewportHeightForAdaptiveContentSize: CGFloat = 975 // 956px height = iPhone 16 Pro Max
|
||||
|
||||
func SignupForm<FormContent: View>(@ViewBuilder content: () -> FormContent) -> some View {
|
||||
return VStack(alignment: .leading, spacing: 10.0, content: content)
|
||||
@@ -26,15 +29,25 @@ struct CreateAccountView: View {
|
||||
ZStack(alignment: .top) {
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
|
||||
EditPictureControl(uploader: .nostrBuild, keypair: account.keypair, pubkey: account.pubkey, size: 75, setup: true, image_url: $account.profile_image , uploadObserver: profileUploadObserver, callback: uploadedProfilePicture)
|
||||
let screenHeight = UIScreen.main.bounds.height
|
||||
let style = EditPictureControl.Style(
|
||||
size: keyboardVisible && screenHeight < maxViewportHeightForAdaptiveContentSize ? 25 : 75,
|
||||
first_time_setup: true
|
||||
)
|
||||
|
||||
EditPictureControl(
|
||||
uploader: MediaUploader.nostrBuild,
|
||||
context: .profile_picture,
|
||||
keypair: account.keypair,
|
||||
pubkey: account.pubkey,
|
||||
style: style,
|
||||
current_image_url: $account.profile_image,
|
||||
upload_observer: profileUploadObserver,
|
||||
callback: uploadedProfilePicture
|
||||
)
|
||||
.shadow(radius: 2)
|
||||
.padding(.top, 100)
|
||||
|
||||
Text("Add Photo", comment: "Label to indicate user can add a photo.")
|
||||
.bold()
|
||||
.foregroundColor(DamusColors.neutral6)
|
||||
}
|
||||
|
||||
SignupForm {
|
||||
@@ -42,13 +55,13 @@ struct CreateAccountView: View {
|
||||
.foregroundColor(DamusColors.neutral6)
|
||||
FormTextInput(NSLocalizedString("Satoshi Nakamoto", comment: "Name of Bitcoin creator(s)."), text: $account.name)
|
||||
.textInputAutocapitalization(.words)
|
||||
|
||||
|
||||
FormLabel(NSLocalizedString("Bio", comment: "Label to prompt bio entry for user to describe themself."), optional: true)
|
||||
.foregroundColor(DamusColors.neutral6)
|
||||
FormTextInput(NSLocalizedString("Absolute legend.", comment: "Example Bio"), text: $account.about)
|
||||
}
|
||||
.padding(.top, 25)
|
||||
|
||||
|
||||
Button(action: {
|
||||
nav.push(route: Route.SaveKeys(account: account))
|
||||
}) {
|
||||
@@ -72,6 +85,11 @@ struct CreateAccountView: View {
|
||||
}
|
||||
.background(DamusBackground(maxHeight: UIScreen.main.bounds.size.height/2), alignment: .top)
|
||||
.dismissKeyboardOnTap()
|
||||
.onReceive(keyboardPublisher) { visible in
|
||||
withAnimation {
|
||||
self.keyboardVisible = visible
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarBackButtonHidden(true)
|
||||
.navigationBarItems(leading: BackNav())
|
||||
|
||||
@@ -20,8 +20,14 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
@Binding private var presentationMode
|
||||
let mediaPickerEntry: MediaPickerEntry
|
||||
|
||||
@Binding var image_upload_confirm: Bool
|
||||
let onMediaSelected: (() -> Void)?
|
||||
let onMediaPicked: (PreUploadedMedia) -> Void
|
||||
|
||||
init(mediaPickerEntry: MediaPickerEntry, onMediaSelected: (() -> Void)? = nil, onMediaPicked: @escaping (PreUploadedMedia) -> Void) {
|
||||
self.mediaPickerEntry = mediaPickerEntry
|
||||
self.onMediaSelected = onMediaSelected
|
||||
self.onMediaPicked = onMediaPicked
|
||||
}
|
||||
|
||||
final class Coordinator: NSObject, PHPickerViewControllerDelegate {
|
||||
var parent: MediaPicker
|
||||
@@ -121,7 +127,7 @@ struct MediaPicker: UIViewControllerRepresentable {
|
||||
|
||||
|
||||
private func chooseMedia(_ media: PreUploadedMedia, orderId: String) {
|
||||
self.parent.image_upload_confirm = true
|
||||
self.parent.onMediaSelected?()
|
||||
self.orderMap[orderId] = media
|
||||
self.dispatchGroup.leave()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ struct AddMuteItemView: View {
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var trimmedText: String {
|
||||
new_text.trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Add mute item", comment: "Title text to indicate user to an add an item to their mutelist.")
|
||||
@@ -30,12 +34,13 @@ struct AddMuteItemView: View {
|
||||
Text("Duration", comment: "The duration in which to mute the given item.")
|
||||
}
|
||||
|
||||
let trimmedText = self.trimmedText
|
||||
|
||||
HStack {
|
||||
Label("", image: "copy2")
|
||||
.onTapGesture {
|
||||
if let pasted_text = UIPasteboard.general.string {
|
||||
self.new_text = pasted_text
|
||||
self.new_text = pasted_text.trimmingCharacters(in: .whitespaces)
|
||||
}
|
||||
}
|
||||
TextField(NSLocalizedString("npub, #hashtag, phrase", comment: "Placeholder example for relay server address."), text: $new_text)
|
||||
@@ -44,7 +49,7 @@ struct AddMuteItemView: View {
|
||||
|
||||
Label("", image: "close-circle")
|
||||
.foregroundColor(.accentColor)
|
||||
.opacity((new_text == "") ? 0.0 : 1.0)
|
||||
.opacity(trimmedText.isEmpty ? 0.0 : 1.0)
|
||||
.onTapGesture {
|
||||
self.new_text = ""
|
||||
}
|
||||
@@ -56,17 +61,17 @@ struct AddMuteItemView: View {
|
||||
Button(action: {
|
||||
let expiration_date: Date? = self.expiration.date_from_now
|
||||
let mute_item: MuteItem? = {
|
||||
if new_text.starts(with: "npub") {
|
||||
if let pubkey: Pubkey = bech32_pubkey_decode(new_text) {
|
||||
if trimmedText.starts(with: "npub") {
|
||||
if let pubkey: Pubkey = bech32_pubkey_decode(trimmedText) {
|
||||
return .user(pubkey, expiration_date)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if new_text.starts(with: "#") {
|
||||
} else if trimmedText.starts(with: "#") {
|
||||
// Remove the starting `#` character
|
||||
return .hashtag(Hashtag(hashtag: String("\(new_text)".dropFirst())), expiration_date)
|
||||
return .hashtag(Hashtag(hashtag: String("\(trimmedText)".dropFirst())), expiration_date)
|
||||
} else {
|
||||
return .word(new_text, expiration_date)
|
||||
return .word(trimmedText, expiration_date)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -99,6 +104,8 @@ struct AddMuteItemView: View {
|
||||
}
|
||||
.buttonStyle(GradientButtonStyle(padding: 10))
|
||||
.padding(.vertical)
|
||||
.opacity(trimmedText.isEmpty ? 0.5 : 1.0)
|
||||
.disabled(trimmedText.isEmpty)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func event_group_unique_pubkeys(profiles: Profiles, group: EventGroupType) -> [P
|
||||
"zapped_your_profile_2" - returned when 2 zaps occurred to the current user's profile
|
||||
"zapped_your_profile_3" - returned when 3 or more zaps occurred to the current user's profile
|
||||
*/
|
||||
func reacting_to_text(profiles: Profiles, our_pubkey: Pubkey, group: EventGroupType, ev: NostrEvent?, pubkeys: [Pubkey], locale: Locale? = nil) -> String {
|
||||
func reacting_to_text(profiles: Profiles, our_pubkey: Pubkey, group: EventGroupType, ev: NostrEvent?, pubkeys: [Pubkey], locale: Locale = Locale.current) -> String {
|
||||
if group.events.count == 0 {
|
||||
return "??"
|
||||
}
|
||||
@@ -188,7 +188,8 @@ struct EventGroupView: View {
|
||||
let group: EventGroupType
|
||||
|
||||
func GroupDescription(_ pubkeys: [Pubkey]) -> some View {
|
||||
Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys))")
|
||||
let text = reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys)
|
||||
return Text(text)
|
||||
}
|
||||
|
||||
func ZapIcon(_ zapgrp: ZapGroup) -> some View {
|
||||
|
||||
@@ -144,21 +144,25 @@ struct NotificationsView: View {
|
||||
func NotificationTab(_ filter: NotificationFilter) -> some View {
|
||||
ScrollViewReader { scroller in
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading) {
|
||||
Color.white.opacity(0)
|
||||
.id("startblock")
|
||||
.frame(height: 5)
|
||||
let notifs = Array(zip(1..., filter.filter(contacts: state.contacts, items: notifications.notifications)))
|
||||
ForEach(notifs, id: \.0) { zip in
|
||||
NotificationItemView(state: state, item: zip.1)
|
||||
let notifs = Array(zip(1..., filter.filter(contacts: state.contacts, items: notifications.notifications)))
|
||||
if notifs.isEmpty {
|
||||
EmptyTimelineView()
|
||||
} else {
|
||||
LazyVStack(alignment: .leading) {
|
||||
Color.white.opacity(0)
|
||||
.id("startblock")
|
||||
.frame(height: 5)
|
||||
ForEach(notifs, id: \.0) { zip in
|
||||
NotificationItemView(state: state, item: zip.1)
|
||||
}
|
||||
}
|
||||
.background(GeometryReader { proxy -> Color in
|
||||
DispatchQueue.main.async {
|
||||
handle_scroll_queue(proxy, queue: self.notifications)
|
||||
}
|
||||
return Color.clear
|
||||
})
|
||||
}
|
||||
.background(GeometryReader { proxy -> Color in
|
||||
DispatchQueue.main.async {
|
||||
handle_scroll_queue(proxy, queue: self.notifications)
|
||||
}
|
||||
return Color.clear
|
||||
})
|
||||
}
|
||||
.coordinateSpace(name: "scroll")
|
||||
.onReceive(handle_notify(.scroll_to_top)) { notif in
|
||||
|
||||
@@ -114,7 +114,10 @@ struct SuggestedUsersSectionHeader: View {
|
||||
let model: SuggestedUsersViewModel
|
||||
var body: some View {
|
||||
HStack {
|
||||
Text(group.title.uppercased())
|
||||
let locale = Locale.current
|
||||
let format = localizedStringFormat(key: group.category, locale: locale)
|
||||
let categoryName = String(format: format, locale: locale)
|
||||
Text(categoryName)
|
||||
Spacer()
|
||||
Button(NSLocalizedString("Follow All", comment: "Button to follow all users in this section")) {
|
||||
model.follow(pubkeys: group.users)
|
||||
|
||||
@@ -48,7 +48,10 @@ struct SuggestedUserView: View {
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Spacer()
|
||||
|
||||
GradientFollowButton(target: target, follows_you: false, follow_state: damus_state.contacts.follow_state(target.pubkey))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ import Combine
|
||||
|
||||
struct SuggestedUserGroup: Identifiable, Codable {
|
||||
let id = UUID()
|
||||
let title: String
|
||||
let category: String
|
||||
let users: [Pubkey]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case title, users
|
||||
case category, users
|
||||
}
|
||||
}
|
||||
|
||||
|
||||