further refactor on background

pull/874/head
Ryan ZHAO 2 years ago
parent 20db5a48a3
commit be5418dc4a

@ -20,265 +20,171 @@ struct MessageInfoView: View {
} }
var body: some View { var body: some View {
NavigationView { ZStack (alignment: .topLeading) {
ZStack (alignment: .topLeading) { ScrollView(.vertical, showsIndicators: false) {
if #available(iOS 14.0, *) { VStack(
ThemeManager.currentTheme.colorSwiftUI(for: .backgroundPrimary).ignoresSafeArea() alignment: .leading,
} else { spacing: 10
ThemeManager.currentTheme.colorSwiftUI(for: .backgroundPrimary) ) {
} // Message bubble snapshot
MessageBubble(
ScrollView(.vertical, showsIndicators: false) { messageViewModel: messageViewModel
VStack( )
alignment: .leading, .background(
spacing: 10 RoundedRectangle(cornerRadius: Self.cornerRadius)
) { .fill(
// Message bubble snapshot themeColor: (messageViewModel.variant == .standardIncoming || messageViewModel.variant == .standardIncomingDeleted ?
MessageBubble( .messageBubble_incomingBackground :
messageViewModel: messageViewModel .messageBubble_outgoingBackground)
) )
.background( )
RoundedRectangle(cornerRadius: Self.cornerRadius) .frame(
.fill( maxWidth: .infinity,
themeColor: (messageViewModel.variant == .standardIncoming || messageViewModel.variant == .standardIncomingDeleted ? maxHeight: .infinity,
.messageBubble_incomingBackground : alignment: .topLeading
.messageBubble_outgoingBackground) )
) .fixedSize(horizontal: false, vertical: true)
) .padding(.top, Values.smallSpacing)
.frame( .padding(.bottom, Values.verySmallSpacing)
maxWidth: .infinity, .padding(.horizontal, Values.largeSpacing)
maxHeight: .infinity,
alignment: .topLeading
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 { HStack(spacing: 6) {
let (image, statusText, tintColor) = messageViewModel.state.statusIconInfo( if let image: UIImage = image?.withRenderingMode(.alwaysTemplate) {
variant: messageViewModel.variant, Image(uiImage: image)
hasAtLeastOneReadReceipt: messageViewModel.hasAtLeastOneReadReceipt .resizable()
) .scaledToFit()
.foregroundColor(themeColor: tintColor)
.frame(width: 13, height: 12)
}
HStack(spacing: 6) { if let statusText: String = statusText {
if let image: UIImage = image?.withRenderingMode(.alwaysTemplate) { Text(statusText)
Image(uiImage: image) .font(.system(size: Values.verySmallFontSize))
.resizable() .foregroundColor(themeColor: tintColor)
.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)
} }
.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, ZStack(alignment: .bottomTrailing) {
messageViewModel.cellType == .mediaMessage if attachments.count > 1 {
{ // Attachment carousel view
let attachment: Attachment = attachments[(index - 1 + attachments.count) % attachments.count] SessionCarouselView_SwiftUI(
index: $index,
ZStack(alignment: .bottomTrailing) { isOutgoing: (messageViewModel.variant == .standardOutgoing),
if attachments.count > 1 { contentInfos: attachments
// 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( .frame(
maxWidth: .infinity, maxWidth: .infinity,
maxHeight: .infinity, maxHeight: .infinity,
alignment: .topLeading 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)
} }
.padding(.vertical, Values.verySmallSpacing)
// Message Info
// Attachment Info
ZStack { ZStack {
VStack( VStack(
alignment: .leading, alignment: .leading,
spacing: Values.mediumSpacing spacing: Values.mediumSpacing
) { ) {
InfoBlock(title: "MESSAGE_INFO_SENT".localized() + ":") { InfoBlock(title: "ATTACHMENT_INFO_FILE_ID".localized() + ":") {
Text(messageViewModel.dateForUI.fromattedForMessageInfo) Text(attachment.serverId ?? "")
.font(.system(size: Values.mediumFontSize))
.foregroundColor(themeColor: .textPrimary)
}
InfoBlock(title: "MESSAGE_INFO_RECEIVED".localized() + ":") {
Text(messageViewModel.receivedDateForUI.fromattedForMessageInfo)
.font(.system(size: Values.mediumFontSize)) .font(.system(size: Values.mediumFontSize))
.foregroundColor(themeColor: .textPrimary) .foregroundColor(themeColor: .textPrimary)
} }
if isMessageFailed { HStack(
let failureText: String = messageViewModel.mostRecentFailureText ?? "Message failed to send" alignment: .center
InfoBlock(title: "ALERT_ERROR_TITLE".localized() + ":") { ) {
Text(failureText) 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)) .font(.system(size: Values.mediumFontSize))
.foregroundColor(themeColor: .danger) .foregroundColor(themeColor: .textPrimary)
} }
Spacer()
} }
HStack(
InfoBlock(title: "MESSAGE_INFO_FROM".localized() + ":") { alignment: .center
HStack( ) {
spacing: 10 let resolution: String = {
) { guard let width = attachment.width, let height = attachment.height else { return "N/A" }
let (info, additionalInfo) = ProfilePictureView.getProfilePictureInfo( return "\(width)×\(height)"
size: .message, }()
publicKey: messageViewModel.authorId, InfoBlock(title: "ATTACHMENT_INFO_RESOLUTION".localized() + ":") {
threadVariant: .contact, // Always show the display picture in 'contact' mode Text(resolution)
customImageData: nil, .font(.system(size: Values.mediumFontSize))
profile: messageViewModel.profile, .foregroundColor(themeColor: .textPrimary)
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)
}
} }
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( .frame(
@ -294,65 +200,152 @@ struct MessageInfoView: View {
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.padding(.vertical, Values.verySmallSpacing) .padding(.vertical, Values.verySmallSpacing)
.padding(.horizontal, Values.largeSpacing) .padding(.horizontal, Values.largeSpacing)
}
// Actions // Message Info
if !actions.isEmpty { ZStack {
ZStack { VStack(
VStack( alignment: .leading,
alignment: .leading, spacing: Values.mediumSpacing
spacing: 0 ) {
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( let (info, additionalInfo) = ProfilePictureView.getProfilePictureInfo(
0...(actions.count - 1), size: .message,
id: \.self publicKey: messageViewModel.authorId,
) { index in threadVariant: .contact, // Always show the display picture in 'contact' mode
let tintColor: ThemeValue = actions[index].isDestructive ? .danger : .textPrimary customImageData: nil,
Button( profile: messageViewModel.profile,
action: { profileIcon: (messageViewModel.isSenderOpenGroupModerator ? .crown : .none)
actions[index].work() )
dismiss()
}, let size: ProfilePictureView.Size = .list
label: {
HStack(spacing: Values.largeSpacing) { if let info: ProfilePictureView.Info = info {
Image(uiImage: actions[index].icon!.withRenderingMode(.alwaysTemplate)) ProfilePictureSwiftUI(
.resizable() size: size,
.scaledToFit() info: info,
.foregroundColor(themeColor: tintColor) additionalInfo: additionalInfo
.frame(width: 26, height: 26) )
Text(actions[index].title) .frame(
.bold() width: size.viewSize,
.font(.system(size: Values.mediumLargeFontSize)) height: size.viewSize,
.foregroundColor(themeColor: tintColor) alignment: .topLeading
}
.frame(maxWidth: .infinity, alignment: .topLeading)
}
) )
.frame(height: 60) }
if index < (actions.count - 1) { VStack(
Divider() alignment: .leading,
.foregroundColor(themeColor: .borderSeparator) 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) .frame(
.cornerRadius(Self.cornerRadius) maxWidth: .infinity,
.fixedSize(horizontal: false, vertical: true) maxHeight: .infinity,
.padding(.vertical, Values.verySmallSpacing) 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) .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) { private func showMediaFullScreen(attachment: Attachment) {

@ -10,9 +10,15 @@ public extension View {
} }
func background(themeColor: ThemeValue) -> some View { func background(themeColor: ThemeValue) -> some View {
return self.background( if #available(iOSApplicationExtension 14.0, *) {
ThemeManager.currentTheme.colorSwiftUI(for: themeColor) return self.background(
) ThemeManager.currentTheme.colorSwiftUI(for: themeColor)?.ignoresSafeArea()
)
} else {
return self.background(
ThemeManager.currentTheme.colorSwiftUI(for: themeColor)
)
}
} }
} }

Loading…
Cancel
Save