From be5418dc4a7242498e10e18531be087ad721f149 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO <> Date: Thu, 9 Nov 2023 15:48:51 +1100 Subject: [PATCH] further refactor on background --- .../MessageInfoView.swift | 551 +++++++++--------- .../Style Guide/Themes/SwiftUI+Theme.swift | 12 +- 2 files changed, 281 insertions(+), 282 deletions(-) diff --git a/Session/Media Viewing & Editing/MessageInfoView.swift b/Session/Media Viewing & Editing/MessageInfoView.swift index b9e79080c..9208beb79 100644 --- a/Session/Media Viewing & Editing/MessageInfoView.swift +++ b/Session/Media Viewing & Editing/MessageInfoView.swift @@ -20,265 +20,171 @@ struct MessageInfoView: View { } 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 - 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 + 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 ) - .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) + } - 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) - } + 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) } + .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] - 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() - } - } + 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 ) - .padding(.all, Values.largeSpacing) + } 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) } - .frame(maxHeight: .infinity) - .background(themeColor: .backgroundSecondary) - .cornerRadius(Self.cornerRadius) - .fixedSize(horizontal: false, vertical: true) - .padding(.vertical, Values.verySmallSpacing) - .padding(.horizontal, Values.largeSpacing) } - - // Message Info + .padding(.vertical, Values.verySmallSpacing) + + // Attachment 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) + InfoBlock(title: "ATTACHMENT_INFO_FILE_ID".localized() + ":") { + Text(attachment.serverId ?? "") .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) + 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: .danger) + .foregroundColor(themeColor: .textPrimary) } + + Spacer() } - - 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) - } + 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( @@ -294,65 +200,152 @@ struct MessageInfoView: View { .fixedSize(horizontal: false, vertical: true) .padding(.vertical, Values.verySmallSpacing) .padding(.horizontal, Values.largeSpacing) + } - // Actions - if !actions.isEmpty { - ZStack { - VStack( - alignment: .leading, - spacing: 0 + // 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 ) { - ForEach( - 0...(actions.count - 1), - id: \.self - ) { index in - let tintColor: ThemeValue = actions[index].isDestructive ? .danger : .textPrimary - 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) - } + 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 ) - .frame(height: 60) - - if index < (actions.count - 1) { - Divider() - .foregroundColor(themeColor: .borderSeparator) + } + + 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(.horizontal, Values.largeSpacing) } - .frame(maxHeight: .infinity) - .background(themeColor: .backgroundSecondary) - .cornerRadius(Self.cornerRadius) - .fixedSize(horizontal: false, vertical: true) - .padding(.vertical, Values.verySmallSpacing) + } + .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].isDestructive ? .danger : .textPrimary + 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) { diff --git a/SessionUIKit/Style Guide/Themes/SwiftUI+Theme.swift b/SessionUIKit/Style Guide/Themes/SwiftUI+Theme.swift index 2915ec462..552624576 100644 --- a/SessionUIKit/Style Guide/Themes/SwiftUI+Theme.swift +++ b/SessionUIKit/Style Guide/Themes/SwiftUI+Theme.swift @@ -10,9 +10,15 @@ public extension View { } func background(themeColor: ThemeValue) -> some View { - return self.background( - ThemeManager.currentTheme.colorSwiftUI(for: themeColor) - ) + if #available(iOSApplicationExtension 14.0, *) { + return self.background( + ThemeManager.currentTheme.colorSwiftUI(for: themeColor)?.ignoresSafeArea() + ) + } else { + return self.background( + ThemeManager.currentTheme.colorSwiftUI(for: themeColor) + ) + } } }