diff --git a/Session/Media Viewing & Editing/MediaInfoVC+MediaInfoView.swift b/Session/Media Viewing & Editing/MediaInfoVC+MediaInfoView.swift deleted file mode 100644 index 5aff94506..000000000 --- a/Session/Media Viewing & Editing/MediaInfoVC+MediaInfoView.swift +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. - -import UIKit -import SessionUIKit -import SessionUtilitiesKit -import SessionMessagingKit -import SignalUtilitiesKit - -extension MediaInfoVC { - final class MediaInfoView: UIView { - private static let cornerRadius: CGFloat = 12 - - private var attachment: Attachment? - private let width: CGFloat = MediaInfoVC.mediaSize - 2 * MediaInfoVC.arrowSize.width - - // MARK: - UI - - private lazy var fileIdLabel: UILabel = { - let result: UILabel = UILabel() - result.font = .systemFont(ofSize: Values.mediumFontSize) - result.themeTextColor = .textPrimary - - return result - }() - - private lazy var fileTypeLabel: UILabel = { - let result: UILabel = UILabel() - result.font = .systemFont(ofSize: Values.mediumFontSize) - result.themeTextColor = .textPrimary - - return result - }() - - private lazy var fileSizeLabel: UILabel = { - let result: UILabel = UILabel() - result.font = .systemFont(ofSize: Values.mediumFontSize) - result.themeTextColor = .textPrimary - - return result - }() - - private lazy var resolutionLabel: UILabel = { - let result: UILabel = UILabel() - result.font = .systemFont(ofSize: Values.mediumFontSize) - result.themeTextColor = .textPrimary - - return result - }() - - private lazy var durationLabel: UILabel = { - let result: UILabel = UILabel() - result.font = .systemFont(ofSize: Values.mediumFontSize) - result.themeTextColor = .textPrimary - - return result - }() - - // MARK: - Lifecycle - - init(attachment: Attachment?) { - self.attachment = attachment - - super.init(frame: CGRect.zero) - self.accessibilityLabel = "Media info" - setUpViewHierarchy() - update(attachment: attachment) - } - - override init(frame: CGRect) { - preconditionFailure("Use init(attachment:) instead.") - } - - required init?(coder: NSCoder) { - preconditionFailure("Use init(attachment:) instead.") - } - - private func setUpViewHierarchy() { - let backgroundView: UIView = UIView() - backgroundView.clipsToBounds = true - backgroundView.themeBackgroundColor = .contextMenu_background - backgroundView.layer.cornerRadius = Self.cornerRadius - addSubview(backgroundView) - backgroundView.pin(to: self) - - let container: UIView = UIView() - container.set(.width, to: self.width) - - // File ID - let fileIdTitleLabel: UILabel = { - let result = UILabel() - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.text = "attachmentsFileId".localized() - result.themeTextColor = .textPrimary - - return result - }() - let fileIdContainerStackView: UIStackView = UIStackView(arrangedSubviews: [ fileIdTitleLabel, fileIdLabel ]) - fileIdContainerStackView.axis = .vertical - fileIdContainerStackView.spacing = 6 - container.addSubview(fileIdContainerStackView) - fileIdContainerStackView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.top ], to: container) - - // File Type - let fileTypeTitleLabel: UILabel = { - let result = UILabel() - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.text = "attachmentsFileType".localized() - result.themeTextColor = .textPrimary - - return result - }() - let fileTypeContainerStackView: UIStackView = UIStackView(arrangedSubviews: [ fileTypeTitleLabel, fileTypeLabel ]) - fileTypeContainerStackView.axis = .vertical - fileTypeContainerStackView.spacing = 6 - container.addSubview(fileTypeContainerStackView) - fileTypeContainerStackView.pin(.leading, to: .leading, of: container) - fileTypeContainerStackView.pin(.top, to: .bottom, of: fileIdContainerStackView, withInset: Values.largeSpacing) - - // File Size - let fileSizeTitleLabel: UILabel = { - let result = UILabel() - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.text = "attachmentsFileSize".localized() - result.themeTextColor = .textPrimary - - return result - }() - let fileSizeContainerStackView: UIStackView = UIStackView(arrangedSubviews: [ fileSizeTitleLabel, fileSizeLabel ]) - fileSizeContainerStackView.axis = .vertical - fileSizeContainerStackView.spacing = 6 - container.addSubview(fileSizeContainerStackView) - fileSizeContainerStackView.pin(.trailing, to: .trailing, of: container) - fileSizeContainerStackView.pin(.top, to: .bottom, of: fileIdContainerStackView, withInset: Values.largeSpacing) - fileSizeContainerStackView.set(.width, to: 90) - - // Resolution - let resolutionTitleLabel: UILabel = { - let result = UILabel() - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.text = "attachmentsResolution".localized() - result.themeTextColor = .textPrimary - - return result - }() - let resolutionContainerStackView: UIStackView = UIStackView(arrangedSubviews: [ resolutionTitleLabel, resolutionLabel ]) - resolutionContainerStackView.axis = .vertical - resolutionContainerStackView.spacing = 6 - container.addSubview(resolutionContainerStackView) - resolutionContainerStackView.pin(.leading, to: .leading, of: container) - resolutionContainerStackView.pin(.top, to: .bottom, of: fileTypeContainerStackView, withInset: Values.largeSpacing) - - // Duration - let durationTitleLabel: UILabel = { - let result = UILabel() - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) - result.text = "attachmentsDuration".localized() - result.themeTextColor = .textPrimary - - return result - }() - let durationContainerStackView: UIStackView = UIStackView(arrangedSubviews: [ durationTitleLabel, durationLabel ]) - durationContainerStackView.axis = .vertical - durationContainerStackView.spacing = 6 - container.addSubview(durationContainerStackView) - durationContainerStackView.pin(.trailing, to: .trailing, of: container) - durationContainerStackView.pin(.top, to: .bottom, of: fileSizeContainerStackView, withInset: Values.largeSpacing) - durationContainerStackView.set(.width, to: 90) - container.pin(.bottom, to: .bottom, of: durationContainerStackView) - - backgroundView.addSubview(container) - container.pin(to: backgroundView, withInset: Values.largeSpacing) - } - - // MARK: - Interaction - - public func update(attachment: Attachment?) { - guard let attachment: Attachment = attachment else { return } - - self.attachment = attachment - - fileIdLabel.text = attachment.serverId - fileTypeLabel.text = attachment.contentType - fileSizeLabel.text = Format.fileSize(attachment.byteCount) - resolutionLabel.text = { - guard let width = attachment.width, let height = attachment.height else { return "attachmentsNa".localized() } - return "\(width)×\(height)" - }() - durationLabel.text = { - guard let duration = attachment.duration else { return "attachmentsNa".localized() } - return floor(duration).formatted(format: .videoDuration) - }() - } - } -} diff --git a/Session/Media Viewing & Editing/MediaInfoVC+MediaPreviewView.swift b/Session/Media Viewing & Editing/MediaInfoVC+MediaPreviewView.swift deleted file mode 100644 index c1ccd4861..000000000 --- a/Session/Media Viewing & Editing/MediaInfoVC+MediaPreviewView.swift +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. - -import UIKit -import SessionUIKit -import SessionUtilitiesKit -import SessionMessagingKit - -extension MediaInfoVC { - final class MediaPreviewView: UIView { - private static let cornerRadius: CGFloat = 8 - - private let attachment: Attachment - private let isOutgoing: Bool - - // MARK: - UI - - private lazy var mediaView: MediaView = { - let result: MediaView = MediaView.init( - attachment: attachment, - isOutgoing: isOutgoing, - shouldSupressControls: false, - cornerRadius: 0 - ) - - return result - }() - - // MARK: - Lifecycle - - init(attachment: Attachment, isOutgoing: Bool) { - self.attachment = attachment - self.isOutgoing = isOutgoing - - super.init(frame: CGRect.zero) - self.accessibilityLabel = "Media info" - setUpViewHierarchy() - } - - override init(frame: CGRect) { - preconditionFailure("Use init(attachment:) instead.") - } - - required init?(coder: NSCoder) { - preconditionFailure("Use init(attachment:) instead.") - } - - private func setUpViewHierarchy() { - set(.width, to: MediaInfoVC.mediaSize) - set(.height, to: MediaInfoVC.mediaSize) - - addSubview(mediaView) - mediaView.pin(to: self) - - mediaView.loadMedia() - } - - // MARK: - Copy - - /// This function is used to make sure the carousel view contains this class can loop infinitely - func copyView() -> MediaPreviewView { - return MediaPreviewView(attachment: self.attachment, isOutgoing: self.isOutgoing) - } - } -} diff --git a/Session/Media Viewing & Editing/MediaInfoVC.swift b/Session/Media Viewing & Editing/MediaInfoVC.swift deleted file mode 100644 index 47eb2e48b..000000000 --- a/Session/Media Viewing & Editing/MediaInfoVC.swift +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. - -import UIKit -import SessionUIKit -import SessionMessagingKit -import SessionUtilitiesKit -import SignalUtilitiesKit - -final class MediaInfoVC: BaseVC, SessionCarouselViewDelegate { - internal static let mediaSize: CGFloat = UIScreen.main.bounds.width - 2 * Values.veryLargeSpacing - internal static let arrowSize: CGSize = CGSize(width: 20, height: 30) - - private let attachments: [Attachment] - private let isOutgoing: Bool - private let threadId: String - private let threadVariant: SessionThread.Variant - private let interactionId: Int64 - - private var currentPage: Int = 0 - - // MARK: - UI - private lazy var mediaInfoView: MediaInfoView = MediaInfoView(attachment: nil) - private lazy var mediaCarouselView: SessionCarouselView = { - let slices: [MediaPreviewView] = self.attachments.map { - MediaPreviewView( - attachment: $0, - isOutgoing: self.isOutgoing - ) - } - let result: SessionCarouselView = SessionCarouselView( - info: SessionCarouselView.Info( - slices: slices, - copyOfFirstSlice: slices.first?.copyView(), - copyOfLastSlice: slices.last?.copyView(), - sliceSize: CGSize( - width: Self.mediaSize, - height: Self.mediaSize - ), - shouldShowPageControl: true, - pageControlStyle: SessionCarouselView.PageControlStyle( - size: .medium, - backgroundColor: .init(white: 0, alpha: 0.4), - bottomInset: Values.mediumSpacing - ), - shouldShowArrows: true, - arrowsSize: Self.arrowSize, - cornerRadius: 8 - ) - ) - result.set(.height, to: Self.mediaSize) - result.delegate = self - - return result - }() - - private lazy var fullScreenButton: UIButton = { - let result: UIButton = UIButton(type: .custom) - result.setImage( - UIImage(systemName: "arrow.up.left.and.arrow.down.right")? - .withRenderingMode(.alwaysTemplate), - for: .normal - ) - result.themeTintColor = .textPrimary - result.backgroundColor = .init(white: 0, alpha: 0.4) - result.layer.cornerRadius = 14 - result.set(.width, to: 28) - result.set(.height, to: 28) - result.addTarget(self, action: #selector(showMediaFullScreen), for: .touchUpInside) - - return result - }() - - // MARK: - Initialization - - init( - attachments: [Attachment], - isOutgoing: Bool, - threadId: String, - threadVariant: SessionThread.Variant, - interactionId: Int64 - ) { - self.threadId = threadId - self.threadVariant = threadVariant - self.interactionId = interactionId - self.isOutgoing = isOutgoing - self.attachments = attachments - super.init(nibName: nil, bundle: nil) - } - - override init(nibName: String?, bundle: Bundle?) { - preconditionFailure("Use init(attachments:) instead.") - } - - required init?(coder: NSCoder) { - preconditionFailure("Use init(attachments:) instead.") - } - - // MARK: - Lifecycle - - override func viewDidLoad() { - super.viewDidLoad() - - ViewControllerUtilities.setUpDefaultSessionStyle( - for: self, - title: "messageInfo".localized(), - hasCustomBackButton: false - ) - - let mediaStackView: UIStackView = UIStackView() - mediaStackView.axis = .horizontal - - mediaInfoView.update(attachment: attachments[0]) - - mediaCarouselView.addSubview(fullScreenButton) - fullScreenButton.pin(.trailing, to: .trailing, of: mediaCarouselView, withInset: -(Values.smallSpacing + Values.veryLargeSpacing)) - fullScreenButton.pin(.bottom, to: .bottom, of: mediaCarouselView, withInset: -Values.smallSpacing) - - let stackView: UIStackView = UIStackView(arrangedSubviews: [ mediaCarouselView, mediaInfoView ]) - stackView.axis = .vertical - stackView.alignment = .center - stackView.spacing = Values.largeSpacing - - self.view.addSubview(stackView) - stackView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing ], to: self.view) - stackView.pin(.top, to: .top, of: self.view, withInset: Values.veryLargeSpacing) - } - - // MARK: - Interaction - - @objc func showMediaFullScreen() { - let attachment = self.attachments[self.currentPage] - let viewController: UIViewController? = MediaGalleryViewModel.createDetailViewController( - for: self.threadId, - threadVariant: self.threadVariant, - interactionId: self.interactionId, - selectedAttachmentId: attachment.id, - options: [ .sliderEnabled ] - ) - if let viewController: UIViewController = viewController { - viewController.transitioningDelegate = nil - self.present(viewController, animated: true) - } - } - - // MARK: - SessionCarouselViewDelegate - - func carouselViewDidScrollToNewSlice(currentPage: Int) { - self.currentPage = currentPage - mediaInfoView.update(attachment: attachments[currentPage]) - } -} diff --git a/Session/Media Viewing & Editing/MessageInfoView.swift b/Session/Media Viewing & Editing/MessageInfoView.swift deleted file mode 100644 index a295e1c09..000000000 --- a/Session/Media Viewing & Editing/MessageInfoView.swift +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. - -import SwiftUI -import SessionUIKit -import SessionSnodeKit -import SessionUtilitiesKit -import SessionMessagingKit - -struct MessageInfoView: View { - @Environment(\.viewController) private var viewControllerHolder: UIViewController? - - @State var index = 1 - @State var showingAttachmentFullScreen = false - - var actions: [ContextMenuVC.Action] - var messageViewModel: MessageViewModel - var isMessageFailed: Bool { - return [.failed, .failedToSync].contains(messageViewModel.state) - } - - var dismiss: (() -> Void)? - - var body: some View { - NavigationView { - ZStack (alignment: .topLeading) { - if #available(iOS 14.0, *) { - ThemeManager.currentTheme.colorSwiftUI(for: .backgroundPrimary).ignoresSafeArea() - } else { - ThemeManager.currentTheme.colorSwiftUI(for: .backgroundPrimary) - } - - ScrollView(.vertical, showsIndicators: false) { - VStack( - alignment: .leading, - spacing: 10 - ) { - // Message bubble snapshot - if let body: String = messageViewModel.body, !body.isEmpty { - let (bubbleBackgroundColor, bubbleTextColor): (ThemeValue, ThemeValue) = ( - messageViewModel.variant == .standardIncoming || - messageViewModel.variant == .standardIncomingDeleted - ) ? - (.messageBubble_incomingBackground, .messageBubble_incomingText) : - (.messageBubble_outgoingBackground, .messageBubble_outgoingText) - - ZStack { - RoundedRectangle(cornerRadius: 18) - .fill(themeColor: bubbleBackgroundColor) - - Text(body) - .foregroundColor(themeColor: bubbleTextColor) - .padding( - EdgeInsets( - top: 8, - leading: 16, - bottom: 8, - trailing: 16 - ) - ) - } - .frame( - maxWidth: .infinity, - maxHeight: .infinity, - alignment: .topLeading - ) - .fixedSize(horizontal: true, vertical: true) - .padding( - EdgeInsets( - top: 8, - leading: 30, - bottom: 4, - trailing: 30 - ) - ) - } - - 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: 11)) - .foregroundColor(themeColor: tintColor) - } - } - .padding( - EdgeInsets( - top: -8, - leading: 30, - bottom: 4, - trailing: 30 - ) - ) - } - - if let attachments = messageViewModel.attachments { - 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), - shouldSupressControls: true, - cornerRadius: 0 - ) - .frame( - maxWidth: .infinity, - maxHeight: .infinity, - alignment: .topLeading - ) - .aspectRatio(1, contentMode: .fit) - .clipShape(RoundedRectangle(cornerRadius: 15)) - .padding( - EdgeInsets( - top: 0, - leading: 30, - bottom: 0, - trailing: 30 - ) - ) - } - - Button { - self.viewControllerHolder?.present(style: .fullScreen) { - MediaGalleryViewModel.createDetailViewSwiftUI( - for: messageViewModel.threadId, - threadVariant: messageViewModel.threadVariant, - interactionId: messageViewModel.id, - selectedAttachmentId: attachment.id, - options: [ .sliderEnabled ] - ) - } - } 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( - EdgeInsets( - top: 0, - leading: 0, - bottom: 8, - trailing: 38 - ) - ) - } - .padding( - EdgeInsets( - top: 4, - leading: 0, - bottom: 4, - trailing: 0 - ) - ) - - // Attachment Info - ZStack { - RoundedRectangle(cornerRadius: 17) - .fill(themeColor: .backgroundSecondary) - - VStack( - alignment: .leading, - spacing: 16 - ) { - InfoBlock(title: "attachmentsFileId".localized()) { - Text(attachment.serverId ?? "") - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - HStack( - alignment: .center - ) { - InfoBlock(title: "attachmentsFileType".localized()) { - Text(attachment.contentType) - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - Spacer() - - InfoBlock(title: "attachmentsFileSize".localized()) { - Text(Format.fileSize(attachment.byteCount)) - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - Spacer() - } - HStack( - alignment: .center - ) { - let resolution: String = { - guard let width = attachment.width, let height = attachment.height else { return "attachmentsNa".localized() } - return "\(width)×\(height)" - }() - InfoBlock(title: "attachmentsResolution".localized()) { - Text(resolution) - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - Spacer() - - let duration: String = { - guard let duration = attachment.duration else { return "attachmentsNa".localized() } - return floor(duration).formatted(format: .videoDuration) - }() - InfoBlock(title: "attachmentsDuration".localized()) { - Text(duration) - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - Spacer() - } - } - .frame( - maxWidth: .infinity, - maxHeight: .infinity, - alignment: .topLeading - ) - .padding( - EdgeInsets( - top: 24, - leading: 24, - bottom: 24, - trailing: 24 - ) - ) - } - .frame(maxHeight: .infinity) - .fixedSize(horizontal: false, vertical: true) - .padding( - EdgeInsets( - top: 4, - leading: 30, - bottom: 4, - trailing: 30 - ) - ) - } - - // Message Info - ZStack { - RoundedRectangle(cornerRadius: 17) - .fill(themeColor: .backgroundSecondary) - - VStack( - alignment: .leading, - spacing: 16 - ) { - InfoBlock(title: "sent".localized()) { - Text(messageViewModel.dateForUI.fromattedForMessageInfo) - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - InfoBlock(title: "received".localized()) { - Text(messageViewModel.receivedDateForUI.fromattedForMessageInfo) - .font(.system(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - - if isMessageFailed { - let failureText: String = messageViewModel.mostRecentFailureText ?? "Message failed to send" - InfoBlock(title: "theError".localized() + ":") { - Text(failureText) - .font(.system(size: 16)) - .foregroundColor(themeColor: .danger) - } - } - - InfoBlock(title: "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: 4 - ) { - if !messageViewModel.authorName.isEmpty { - Text(messageViewModel.authorName) - .bold() - .font(.system(size: 18)) - .foregroundColor(themeColor: .textPrimary) - } - Text(messageViewModel.authorId) - .font(.spaceMono(size: 16)) - .foregroundColor(themeColor: .textPrimary) - } - } - } - } - .frame( - maxWidth: .infinity, - maxHeight: .infinity, - alignment: .topLeading - ) - .padding( - EdgeInsets( - top: 24, - leading: 24, - bottom: 24, - trailing: 24 - ) - ) - } - .frame(maxHeight: .infinity) - .fixedSize(horizontal: false, vertical: true) - .padding( - EdgeInsets( - top: 4, - leading: 30, - bottom: 4, - trailing: 30 - ) - ) - - // Actions - if !actions.isEmpty { - ZStack { - RoundedRectangle(cornerRadius: 17) - .fill(themeColor: .backgroundSecondary) - - 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: 24) { - 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: 18)) - .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( - EdgeInsets( - top: 0, - leading: 24, - bottom: 0, - trailing: 24 - ) - ) - } - .frame(maxHeight: .infinity) - .fixedSize(horizontal: false, vertical: true) - .padding( - EdgeInsets( - top: 4, - leading: 30, - bottom: 4, - trailing: 30 - ) - ) - } - } - } - } - } - } -} - -struct InfoBlock: View where Content: View { - let title: String - let content: () -> Content - - var body: some View { - VStack( - alignment: .leading, - spacing: 4 - ) { - Text(self.title) - .bold() - .font(.system(size: 18)) - .foregroundColor(themeColor: .textPrimary) - self.content() - } - .frame( - minWidth: 100, - alignment: .leading - ) - } -} - -final class MessageInfoViewController: SessionHostingViewController { - init(actions: [ContextMenuVC.Action], messageViewModel: MessageViewModel) { - let messageInfoView = MessageInfoView( - actions: actions, - messageViewModel: messageViewModel - ) - - super.init(rootView: messageInfoView) - rootView.content.dismiss = dismiss - } - - @MainActor required dynamic init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - let customTitleFontSize = Values.largeFontSize - setNavBarTitle("messageInfo".localized(), customFontSize: customTitleFontSize) - } - - func dismiss() { - self.navigationController?.popViewController(animated: true) - } -} - -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: "Test Message", - 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 { - MessageInfoView( - actions: actions, - messageViewModel: messageViewModel - ) - } -}