From 1d043157e0d0e64f9dba8bf39c961bd8fd123058 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO <> Date: Mon, 4 Mar 2024 15:20:10 +1100 Subject: [PATCH] fix an issue where messages with quotes are truncated --- Session.xcodeproj/project.pbxproj | 33 +- .../MessageInfoScreen.swift | 580 ++++++++++++++++++ 2 files changed, 592 insertions(+), 21 deletions(-) create mode 100644 Session/Media Viewing & Editing/MessageInfoScreen.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 6321e55cf..9a4dc4362 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -107,9 +107,9 @@ 7B4EF25A2934743000CB351D /* SessionTableViewTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4EF2592934743000CB351D /* SessionTableViewTitleView.swift */; }; 7B50D64D28AC7CF80086CCEC /* silence.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 7B50D64C28AC7CF80086CCEC /* silence.aiff */; }; 7B521E0A29BFF84400C3C36A /* GroupLeavingJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B521E0929BFF84400C3C36A /* GroupLeavingJob.swift */; }; - 7B5802992AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5802982AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift */; }; 7B5233C42900E90F00F8F375 /* SessionLabelCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5233C32900E90F00F8F375 /* SessionLabelCarouselView.swift */; }; 7B5233C6290636D700F8F375 /* _018_DisappearingMessagesConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5233C5290636D700F8F375 /* _018_DisappearingMessagesConfiguration.swift */; }; + 7B5802992AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5802982AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift */; }; 7B7037432834B81F000DCF35 /* ReactionContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7037422834B81F000DCF35 /* ReactionContainerView.swift */; }; 7B7037452834BCC0000DCF35 /* ReactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7037442834BCC0000DCF35 /* ReactionView.swift */; }; 7B71A98F2925E2A600E54854 /* SessionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B71A98E2925E2A600E54854 /* SessionFooterView.swift */; }; @@ -139,8 +139,8 @@ 7B9F71D82853100A006DFE7B /* EmojiWithSkinTones.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9F71D628531009006DFE7B /* EmojiWithSkinTones.swift */; }; 7BA1E0E82A8087DB00123D0D /* SwiftUI+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA1E0E72A8087DB00123D0D /* SwiftUI+Utilities.swift */; }; 7BA37AF92AEB365C002438F8 /* DocumentView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA37AF82AEB365C002438F8 /* DocumentView_SwiftUI.swift */; }; - 7BA37AFD2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA37AFC2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift */; }; 7BA37AFB2AEB64CA002438F8 /* DisappearingMessageTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA37AFA2AEB64CA002438F8 /* DisappearingMessageTimerView.swift */; }; + 7BA37AFD2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA37AFC2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift */; }; 7BA68909272A27BE00EFC32F /* SessionCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA68908272A27BE00EFC32F /* SessionCall.swift */; }; 7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */; }; 7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */; }; @@ -151,8 +151,8 @@ 7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CD27ACCEEC003D12F8 /* EmptySearchResultCell.swift */; }; 7BAF54D327ACCF01003D12F8 /* ShareAppExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */; }; 7BAF54D427ACCF01003D12F8 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */; }; - 7BAFA75A2AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */; }; 7BAFA1192A39669400B76CB9 /* BezierPathView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAFA1182A39669400B76CB9 /* BezierPathView.swift */; }; + 7BAFA75A2AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */; }; 7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */; }; 7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */; }; 7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; }; @@ -161,7 +161,7 @@ 7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; }; 7BCD116C27016062006330F1 /* WebRTCSession+DataChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */; }; 7BD477A827EC39F5004E2822 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD477A727EC39F5004E2822 /* Atomic.swift */; }; - 7BD687D12A5D0D1200D8E455 /* MessageInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD687D02A5D0D1200D8E455 /* MessageInfoView.swift */; }; + 7BD687D12A5D0D1200D8E455 /* MessageInfoScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD687D02A5D0D1200D8E455 /* MessageInfoScreen.swift */; }; 7BD976972A776C76001B466F /* SessionCarouselView+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE2701D2A64C11500CEB71A /* SessionCarouselView+SwiftUI.swift */; }; 7BDCFC08242186E700641C39 /* NotificationServiceExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */; }; 7BF8D1FB2A70AF57005F1D6E /* SwiftUI+Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BF8D1FA2A70AF57005F1D6E /* SwiftUI+Theme.swift */; }; @@ -169,9 +169,9 @@ 7BFD1A8A2745C4F000FB91B9 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A892745C4F000FB91B9 /* Permissions.swift */; }; 7BFD1A8C2747150E00FB91B9 /* TurnServerInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A8B2747150E00FB91B9 /* TurnServerInfo.swift */; }; 7BFD1A972747689000FB91B9 /* Session-Turn-Server in Resources */ = {isa = PBXBuildFile; fileRef = 7BFD1A962747689000FB91B9 /* Session-Turn-Server */; }; - 94B3DC172AF8592200C88531 /* QuoteView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */; }; 9422EE2B2B8C3A97004C740D /* String+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9422EE2A2B8C3A97004C740D /* String+Utilities.swift */; }; 943C6D822B75E061004ACE64 /* Message+DisappearingMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943C6D812B75E061004ACE64 /* Message+DisappearingMessages.swift */; }; + 94B3DC172AF8592200C88531 /* QuoteView_SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */; }; 9593A1E796C9E6BE2352EA6F /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionSnodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8B0BA5257C58DC6FF797278 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionSnodeKit.framework */; }; 99978E3F7A80275823CA9014 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionNotificationServiceExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29E827FDF6C1032BB985740C /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_SessionNotificationServiceExtension.framework */; }; A11CD70D17FA230600A2D1B1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */; }; @@ -1232,7 +1232,6 @@ 70377AAA1918450100CAF501 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 76C87F18181EFCE600C4ACAB /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; 7B02DF432A16F47B00ADCFD2 /* EmojiGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiGenerator.swift; sourceTree = ""; }; - 7B02DF442A16F47B00ADCFD2 /* build_libSession_util.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_libSession_util.sh; sourceTree = ""; }; 7B0EFDED274F598600FFAAE7 /* TimestampUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimestampUtils.swift; sourceTree = ""; }; 7B0EFDEF275084AA00FFAAE7 /* CallMessageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMessageCell.swift; sourceTree = ""; }; 7B0EFDF3275490EA00FFAAE7 /* ringing.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = ringing.mp3; sourceTree = ""; }; @@ -1254,9 +1253,9 @@ 7B4EF2592934743000CB351D /* SessionTableViewTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionTableViewTitleView.swift; sourceTree = ""; }; 7B50D64C28AC7CF80086CCEC /* silence.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = silence.aiff; sourceTree = ""; }; 7B521E0929BFF84400C3C36A /* GroupLeavingJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLeavingJob.swift; sourceTree = ""; }; - 7B5802982AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupInvitationView_SwiftUI.swift; sourceTree = ""; }; 7B5233C32900E90F00F8F375 /* SessionLabelCarouselView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionLabelCarouselView.swift; sourceTree = ""; }; 7B5233C5290636D700F8F375 /* _018_DisappearingMessagesConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _018_DisappearingMessagesConfiguration.swift; sourceTree = ""; }; + 7B5802982AAEF1B50050EEB1 /* OpenGroupInvitationView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupInvitationView_SwiftUI.swift; sourceTree = ""; }; 7B7037422834B81F000DCF35 /* ReactionContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionContainerView.swift; sourceTree = ""; }; 7B7037442834BCC0000DCF35 /* ReactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionView.swift; sourceTree = ""; }; 7B71A98E2925E2A600E54854 /* SessionFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionFooterView.swift; sourceTree = ""; }; @@ -1286,8 +1285,8 @@ 7B9F71D628531009006DFE7B /* EmojiWithSkinTones.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmojiWithSkinTones.swift; sourceTree = ""; }; 7BA1E0E72A8087DB00123D0D /* SwiftUI+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftUI+Utilities.swift"; sourceTree = ""; }; 7BA37AF82AEB365C002438F8 /* DocumentView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentView_SwiftUI.swift; sourceTree = ""; }; - 7BA37AFC2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageView_SwiftUI.swift; sourceTree = ""; }; 7BA37AFA2AEB64CA002438F8 /* DisappearingMessageTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisappearingMessageTimerView.swift; sourceTree = ""; }; + 7BA37AFC2AEF7C3D002438F8 /* VoiceMessageView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageView_SwiftUI.swift; sourceTree = ""; }; 7BA68908272A27BE00EFC32F /* SessionCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCall.swift; sourceTree = ""; }; 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXCallController.swift"; sourceTree = ""; }; 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXProvider.swift"; sourceTree = ""; }; @@ -1299,8 +1298,8 @@ 7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareAppExtensionContext.swift; sourceTree = ""; }; 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = ""; }; 7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = ""; }; - 7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewView_SwiftUI.swift; sourceTree = ""; }; 7BAFA1182A39669400B76CB9 /* BezierPathView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BezierPathView.swift; sourceTree = ""; }; + 7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewView_SwiftUI.swift; sourceTree = ""; }; 7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationViewModel.swift; sourceTree = ""; }; 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableLabel.swift; sourceTree = ""; }; 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = ""; }; @@ -1310,7 +1309,7 @@ 7BC707F127290ACB002817AD /* SessionCallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCallManager.swift; sourceTree = ""; }; 7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTCSession+DataChannel.swift"; sourceTree = ""; }; 7BD477A727EC39F5004E2822 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; - 7BD687D02A5D0D1200D8E455 /* MessageInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageInfoView.swift; sourceTree = ""; }; + 7BD687D02A5D0D1200D8E455 /* MessageInfoScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageInfoScreen.swift; sourceTree = ""; }; 7BD687D22A5D283200D8E455 /* build_libSession_util.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_libSession_util.sh; sourceTree = ""; }; 7BDCFC0424206E7300641C39 /* SessionNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SessionNotificationServiceExtension.entitlements; sourceTree = ""; }; 7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtensionContext.swift; sourceTree = ""; }; @@ -1327,9 +1326,9 @@ 8E946CB54A221018E23599DE /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; path = "Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; sourceTree = ""; }; 92E8569C96285EE3CDB5960D /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SignalUtilitiesKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 93359C81CF2660040B7CD106 /* Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GlobalDependencies_FrameworkAndExtensionDependencies_ExtendedDependencies_SessionUtilitiesKit_SessionUtilitiesKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView_SwiftUI.swift; sourceTree = ""; }; 9422EE2A2B8C3A97004C740D /* String+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Utilities.swift"; sourceTree = ""; }; 943C6D812B75E061004ACE64 /* Message+DisappearingMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Message+DisappearingMessages.swift"; sourceTree = ""; }; + 94B3DC162AF8592200C88531 /* QuoteView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuoteView_SwiftUI.swift; sourceTree = ""; }; A11CD70C17FA230600A2D1B1 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; A163E8AA16F3F6A90094D68B /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; A1C32D4D17A0652C000A904E /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; @@ -2212,13 +2211,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - C8BB5A4618641C387640AE22 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; D221A086169C9E5E00537ABF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -3196,7 +3188,7 @@ 4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */, 4C4AE69F224AF21900D4AF6F /* SendMediaNavigationController.swift */, 7B46AAAE28766DF4001AF2DC /* AllMediaViewController.swift */, - 7BD687D02A5D0D1200D8E455 /* MessageInfoView.swift */, + 7BD687D02A5D0D1200D8E455 /* MessageInfoScreen.swift */, ); path = "Media Viewing & Editing"; sourceTree = ""; @@ -6448,8 +6440,7 @@ FD37E9CC28A1E578003AE748 /* AppearanceViewController.swift in Sources */, B8EB20F02640F7F000773E52 /* OpenGroupInvitationView.swift in Sources */, C328254025CA55880062D0A7 /* ContextMenuVC.swift in Sources */, - 7BD687D12A5D0D1200D8E455 /* MessageInfoView.swift in Sources */, - 3427C64320F500E000EEC730 /* OWSMessageTimerView.m in Sources */, + 7BD687D12A5D0D1200D8E455 /* MessageInfoScreen.swift in Sources */, B8269D2925C7A4B400488AB4 /* InputView.swift in Sources */, FD71162E28E168C700B47552 /* SettingsViewModel.swift in Sources */, ); diff --git a/Session/Media Viewing & Editing/MessageInfoScreen.swift b/Session/Media Viewing & Editing/MessageInfoScreen.swift new file mode 100644 index 000000000..213561e21 --- /dev/null +++ b/Session/Media Viewing & Editing/MessageInfoScreen.swift @@ -0,0 +1,580 @@ +// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. + +import SwiftUI +import SessionUIKit +import SessionSnodeKit +import SessionUtilitiesKit +import SessionMessagingKit + +struct MessageInfoScreen: View { + @EnvironmentObject var host: HostWrapper + + @State var index = 1 + + static private let cornerRadius: CGFloat = 17 + + var actions: [ContextMenuVC.Action] + var messageViewModel: MessageViewModel + var isMessageFailed: Bool { + return [.failed, .failedToSync].contains(messageViewModel.state) + } + + var body: some View { + ZStack (alignment: .topLeading) { + ScrollView(.vertical, showsIndicators: false) { + VStack( + alignment: .leading, + spacing: 10 + ) { + // Message bubble snapshot + MessageBubble( + messageViewModel: messageViewModel + ) + .background( + RoundedRectangle(cornerRadius: Self.cornerRadius) + .fill( + themeColor: (messageViewModel.variant == .standardIncoming || messageViewModel.variant == .standardIncomingDeleted ? + .messageBubble_incomingBackground : + .messageBubble_outgoingBackground) + ) + ) + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .topLeading + ) + .fixedSize(horizontal: false, vertical: true) + .padding(.top, Values.smallSpacing) + .padding(.bottom, Values.verySmallSpacing) + .padding(.horizontal, Values.largeSpacing) + + + if isMessageFailed { + let (image, statusText, tintColor) = messageViewModel.state.statusIconInfo( + variant: messageViewModel.variant, + hasAtLeastOneReadReceipt: messageViewModel.hasAtLeastOneReadReceipt + ) + + HStack(spacing: 6) { + if let image: UIImage = image?.withRenderingMode(.alwaysTemplate) { + Image(uiImage: image) + .resizable() + .scaledToFit() + .foregroundColor(themeColor: tintColor) + .frame(width: 13, height: 12) + } + + if let statusText: String = statusText { + Text(statusText) + .font(.system(size: Values.verySmallFontSize)) + .foregroundColor(themeColor: tintColor) + } + } + .padding(.top, -Values.smallSpacing) + .padding(.bottom, Values.verySmallSpacing) + .padding(.horizontal, Values.largeSpacing) + } + + if let attachments = messageViewModel.attachments, + messageViewModel.cellType == .mediaMessage + { + let attachment: Attachment = attachments[(index - 1 + attachments.count) % attachments.count] + + ZStack(alignment: .bottomTrailing) { + if attachments.count > 1 { + // Attachment carousel view + SessionCarouselView_SwiftUI( + index: $index, + isOutgoing: (messageViewModel.variant == .standardOutgoing), + contentInfos: attachments + ) + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .topLeading + ) + } else { + MediaView_SwiftUI( + attachment: attachments[0], + isOutgoing: (messageViewModel.variant == .standardOutgoing), + cornerRadius: 0 + ) + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .topLeading + ) + .aspectRatio(1, contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 15)) + .padding(.horizontal, Values.largeSpacing) + } + + if [ .downloaded, .uploaded ].contains(attachment.state) { + Button { + self.showMediaFullScreen(attachment: attachment) + } label: { + ZStack { + Circle() + .foregroundColor(.init(white: 0, opacity: 0.4)) + Image(systemName: "arrow.up.left.and.arrow.down.right") + .font(.system(size: 13)) + .foregroundColor(.white) + } + .frame(width: 26, height: 26) + } + .padding(.bottom, Values.smallSpacing) + .padding(.trailing, 38) + } + } + .padding(.vertical, Values.verySmallSpacing) + + // Attachment Info + ZStack { + VStack( + alignment: .leading, + spacing: Values.mediumSpacing + ) { + InfoBlock(title: "ATTACHMENT_INFO_FILE_ID".localized() + ":") { + Text(attachment.serverId ?? "") + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + HStack( + alignment: .center + ) { + InfoBlock(title: "ATTACHMENT_INFO_FILE_TYPE".localized() + ":") { + Text(attachment.contentType) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + Spacer() + + InfoBlock(title: "ATTACHMENT_INFO_FILE_SIZE".localized() + ":") { + Text(Format.fileSize(attachment.byteCount)) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + Spacer() + } + HStack( + alignment: .center + ) { + let resolution: String = { + guard let width = attachment.width, let height = attachment.height else { return "N/A" } + return "\(width)×\(height)" + }() + InfoBlock(title: "ATTACHMENT_INFO_RESOLUTION".localized() + ":") { + Text(resolution) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + Spacer() + + let duration: String = { + guard let duration = attachment.duration else { return "N/A" } + return floor(duration).formatted(format: .videoDuration) + }() + InfoBlock(title: "ATTACHMENT_INFO_DURATION".localized() + ":") { + Text(duration) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + Spacer() + } + } + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .topLeading + ) + .padding(.all, Values.largeSpacing) + } + .frame(maxHeight: .infinity) + .background(themeColor: .backgroundSecondary) + .cornerRadius(Self.cornerRadius) + .fixedSize(horizontal: false, vertical: true) + .padding(.vertical, Values.verySmallSpacing) + .padding(.horizontal, Values.largeSpacing) + } + + // Message Info + ZStack { + VStack( + alignment: .leading, + spacing: Values.mediumSpacing + ) { + InfoBlock(title: "MESSAGE_INFO_SENT".localized() + ":") { + Text(messageViewModel.dateForUI.fromattedForMessageInfo) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + InfoBlock(title: "MESSAGE_INFO_RECEIVED".localized() + ":") { + Text(messageViewModel.receivedDateForUI.fromattedForMessageInfo) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + + if isMessageFailed { + let failureText: String = messageViewModel.mostRecentFailureText ?? "Message failed to send" + InfoBlock(title: "ALERT_ERROR_TITLE".localized() + ":") { + Text(failureText) + .font(.system(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .danger) + } + } + + InfoBlock(title: "MESSAGE_INFO_FROM".localized() + ":") { + HStack( + spacing: 10 + ) { + let (info, additionalInfo) = ProfilePictureView.getProfilePictureInfo( + size: .message, + publicKey: messageViewModel.authorId, + threadVariant: .contact, // Always show the display picture in 'contact' mode + customImageData: nil, + profile: messageViewModel.profile, + profileIcon: (messageViewModel.isSenderOpenGroupModerator ? .crown : .none) + ) + + let size: ProfilePictureView.Size = .list + + if let info: ProfilePictureView.Info = info { + ProfilePictureSwiftUI( + size: size, + info: info, + additionalInfo: additionalInfo + ) + .frame( + width: size.viewSize, + height: size.viewSize, + alignment: .topLeading + ) + } + + VStack( + alignment: .leading, + spacing: Values.verySmallSpacing + ) { + if !messageViewModel.authorName.isEmpty { + Text(messageViewModel.authorName) + .bold() + .font(.system(size: Values.mediumLargeFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + Text(messageViewModel.authorId) + .font(.spaceMono(size: Values.mediumFontSize)) + .foregroundColor(themeColor: .textPrimary) + } + } + } + } + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .topLeading + ) + .padding(.all, Values.largeSpacing) + } + .frame(maxHeight: .infinity) + .background(themeColor: .backgroundSecondary) + .cornerRadius(Self.cornerRadius) + .fixedSize(horizontal: false, vertical: true) + .padding(.vertical, Values.verySmallSpacing) + .padding(.horizontal, Values.largeSpacing) + + // Actions + if !actions.isEmpty { + ZStack { + VStack( + alignment: .leading, + spacing: 0 + ) { + ForEach( + 0...(actions.count - 1), + id: \.self + ) { index in + let tintColor: ThemeValue = actions[index].themeColor + Button( + action: { + actions[index].work() + dismiss() + }, + label: { + HStack(spacing: Values.largeSpacing) { + Image(uiImage: actions[index].icon!.withRenderingMode(.alwaysTemplate)) + .resizable() + .scaledToFit() + .foregroundColor(themeColor: tintColor) + .frame(width: 26, height: 26) + Text(actions[index].title) + .bold() + .font(.system(size: Values.mediumLargeFontSize)) + .foregroundColor(themeColor: tintColor) + } + .frame(maxWidth: .infinity, alignment: .topLeading) + } + ) + .frame(height: 60) + + if index < (actions.count - 1) { + Divider() + .foregroundColor(themeColor: .borderSeparator) + } + } + } + .frame( + maxWidth: .infinity, + maxHeight: .infinity, + alignment: .topLeading + ) + .padding(.horizontal, Values.largeSpacing) + } + .frame(maxHeight: .infinity) + .background(themeColor: .backgroundSecondary) + .cornerRadius(Self.cornerRadius) + .fixedSize(horizontal: false, vertical: true) + .padding(.vertical, Values.verySmallSpacing) + .padding(.horizontal, Values.largeSpacing) + } + } + } + } + .background(themeColor: .backgroundPrimary) + } + + private func showMediaFullScreen(attachment: Attachment) { + if let mediaGalleryView = MediaGalleryViewModel.createDetailViewController( + for: messageViewModel.threadId, + threadVariant: messageViewModel.threadVariant, + interactionId: messageViewModel.id, + selectedAttachmentId: attachment.id, + options: [ .sliderEnabled ], + useTransitioningDelegate: false + ) { + self.host.controller?.present(mediaGalleryView, animated: true) + } + } + + func dismiss() { + self.host.controller?.navigationController?.popViewController(animated: true) + } +} + +struct MessageBubble: View { + static private let cornerRadius: CGFloat = 18 + static private let inset: CGFloat = 12 + + let messageViewModel: MessageViewModel + + var bodyLabelTextColor: ThemeValue { + messageViewModel.variant == .standardOutgoing ? + .messageBubble_outgoingText : + .messageBubble_incomingText + } + + var body: some View { + ZStack { + switch messageViewModel.cellType { + case .textOnlyMessage: + let maxWidth: CGFloat = (VisibleMessageCell.getMaxWidth(for: messageViewModel) - 2 * Self.inset) + + VStack( + alignment: .leading, + spacing: 0 + ) { + if let linkPreview: LinkPreview = messageViewModel.linkPreview { + switch linkPreview.variant { + case .standard: + LinkPreviewView_SwiftUI( + state: LinkPreview.SentState( + linkPreview: linkPreview, + imageAttachment: messageViewModel.linkPreviewAttachment + ), + isOutgoing: (messageViewModel.variant == .standardOutgoing), + maxWidth: maxWidth, + messageViewModel: messageViewModel, + bodyLabelTextColor: bodyLabelTextColor, + lastSearchText: nil + ) + + case .openGroupInvitation: + OpenGroupInvitationView_SwiftUI( + name: (linkPreview.title ?? ""), + url: linkPreview.url, + textColor: bodyLabelTextColor, + isOutgoing: (messageViewModel.variant == .standardOutgoing)) + } + } + else { + if let quote = messageViewModel.quote { + QuoteView_SwiftUI( + info: .init( + mode: .regular, + authorId: quote.authorId, + quotedText: quote.body, + threadVariant: messageViewModel.threadVariant, + currentUserPublicKey: messageViewModel.currentUserPublicKey, + currentUserBlinded15PublicKey: messageViewModel.currentUserBlinded15PublicKey, + currentUserBlinded25PublicKey: messageViewModel.currentUserBlinded25PublicKey, + direction: (messageViewModel.variant == .standardOutgoing ? .outgoing : .incoming), + attachment: messageViewModel.quoteAttachment + ) + ) + .fixedSize(horizontal: false, vertical: true) + .padding(.top, Self.inset) + .padding(.horizontal, Self.inset) + .padding(.bottom, -Values.smallSpacing) + } + } + + if let bodyText: NSAttributedString = VisibleMessageCell.getBodyAttributedText( + for: messageViewModel, + theme: ThemeManager.currentTheme, + primaryColor: ThemeManager.primaryColor, + textColor: bodyLabelTextColor, + searchText: nil + ) { + AttributedText(bodyText) + .padding(.all, Self.inset) + } + } + case .mediaMessage: + if let bodyText: NSAttributedString = VisibleMessageCell.getBodyAttributedText( + for: messageViewModel, + theme: ThemeManager.currentTheme, + primaryColor: ThemeManager.primaryColor, + textColor: bodyLabelTextColor, + searchText: nil + ) { + AttributedText(bodyText) + .padding(.all, Self.inset) + } + case .voiceMessage: + if let attachment: Attachment = messageViewModel.attachments?.first(where: { $0.isAudio }){ + // TODO: Playback Info and check if playing function is needed + VoiceMessageView_SwiftUI(attachment: attachment) + } + case .audio, .genericAttachment: + if let attachment: Attachment = messageViewModel.attachments?.first { + VStack(spacing: Values.smallSpacing) { + DocumentView_SwiftUI(attachment: attachment, textColor: bodyLabelTextColor) + + if let bodyText: NSAttributedString = VisibleMessageCell.getBodyAttributedText( + for: messageViewModel, + theme: ThemeManager.currentTheme, + primaryColor: ThemeManager.primaryColor, + textColor: bodyLabelTextColor, + searchText: nil + ) { + ZStack{ + AttributedText(bodyText) + } + .padding(.horizontal, Self.inset) + .padding(.bottom, Self.inset) + } + } + } + default: EmptyView() + } + } + } +} + +struct InfoBlock: View where Content: View { + let title: String + let content: () -> Content + + private let minWidth: CGFloat = 100 + + var body: some View { + VStack( + alignment: .leading, + spacing: Values.verySmallSpacing + ) { + Text(self.title) + .bold() + .font(.system(size: Values.mediumLargeFontSize)) + .foregroundColor(themeColor: .textPrimary) + self.content() + } + .frame( + minWidth: minWidth, + alignment: .leading + ) + } +} + +final class MessageInfoViewController: SessionHostingViewController { + init(actions: [ContextMenuVC.Action], messageViewModel: MessageViewModel) { + let messageInfoView = MessageInfoScreen( + actions: actions, + messageViewModel: messageViewModel + ) + + super.init(rootView: messageInfoView) + } + + @MainActor required dynamic init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + let customTitleFontSize = Values.largeFontSize + setNavBarTitle("message_info_title".localized(), customFontSize: customTitleFontSize) + } +} + +struct MessageInfoView_Previews: PreviewProvider { + static var messageViewModel: MessageViewModel { + let result = MessageViewModel( + optimisticMessageId: UUID(), + threadId: "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54gdfsg", + threadVariant: .contact, + threadExpirationType: nil, + threadExpirationTimer: nil, + threadOpenGroupServer: nil, + threadOpenGroupPublicKey: nil, + threadContactNameInternal: "Test", + timestampMs: SnodeAPI.currentOffsetTimestampMs(), + receivedAtTimestampMs: SnodeAPI.currentOffsetTimestampMs(), + authorId: "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54gdfsg", + authorNameInternal: "Test", + body: "Mauris sapien dui, sagittis et fringilla eget, tincidunt vel mauris. Mauris bibendum quis ipsum ac pulvinar. Integer semper elit vitae placerat efficitur. Quisque blandit scelerisque orci, a fringilla dui. In a sollicitudin tortor. Vivamus consequat sollicitudin felis, nec pretium dolor bibendum sit amet. Integer non congue risus, id imperdiet diam. Proin elementum enim at felis commodo semper. Pellentesque magna magna, laoreet nec hendrerit in, suscipit sit amet risus. Nulla et imperdiet massa. Donec commodo felis quis arcu dignissim lobortis. Praesent nec fringilla felis, ut pharetra sapien. Donec ac dignissim nisi, non lobortis justo. Nulla congue velit nec sodales bibendum. Nullam feugiat, mauris ac consequat posuere, eros sem dignissim nulla, ac convallis dolor sem rhoncus dolor. Cras ut luctus risus, quis viverra mauris.", + expiresStartedAtMs: nil, + expiresInSeconds: nil, + state: .failed, + isSenderOpenGroupModerator: false, + currentUserProfile: Profile.fetchOrCreateCurrentUser(), + quote: nil, + quoteAttachment: nil, + linkPreview: nil, + linkPreviewAttachment: nil, + attachments: nil + ) + + return result + } + + static var actions: [ContextMenuVC.Action] { + return [ + .reply(messageViewModel, nil, using: Dependencies()), + .retry(messageViewModel, nil, using: Dependencies()), + .delete(messageViewModel, nil, using: Dependencies()) + ] + } + + static var previews: some View { + MessageInfoScreen( + actions: actions, + messageViewModel: messageViewModel + ) + } +}