WIP: fix messages not correctly shown in message info screen
This commit is contained in:
parent
3e0a4dec20
commit
a576037cf5
|
@ -146,6 +146,7 @@
|
||||||
7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CD27ACCEEC003D12F8 /* EmptySearchResultCell.swift */; };
|
7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CD27ACCEEC003D12F8 /* EmptySearchResultCell.swift */; };
|
||||||
7BAF54D327ACCF01003D12F8 /* ShareAppExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */; };
|
7BAF54D327ACCF01003D12F8 /* ShareAppExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */; };
|
||||||
7BAF54D427ACCF01003D12F8 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.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 */; };
|
||||||
7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */; };
|
7BB92B3F28C825FD0082762F /* NewConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */; };
|
||||||
7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */; };
|
7BBBDC44286EAD2D00747E59 /* TappableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */; };
|
||||||
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; };
|
7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; };
|
||||||
|
@ -1262,6 +1263,7 @@
|
||||||
7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareAppExtensionContext.swift; sourceTree = "<group>"; };
|
7BAF54D127ACCF01003D12F8 /* ShareAppExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShareAppExtensionContext.swift; sourceTree = "<group>"; };
|
||||||
7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
|
7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
|
||||||
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
|
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
|
||||||
|
7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreviewView_SwiftUI.swift; sourceTree = "<group>"; };
|
||||||
7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationViewModel.swift; sourceTree = "<group>"; };
|
7BB92B3E28C825FD0082762F /* NewConversationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationViewModel.swift; sourceTree = "<group>"; };
|
||||||
7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableLabel.swift; sourceTree = "<group>"; };
|
7BBBDC43286EAD2D00747E59 /* TappableLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TappableLabel.swift; sourceTree = "<group>"; };
|
||||||
7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = "<group>"; };
|
7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2459,6 +2461,7 @@
|
||||||
C328250E25CA06020062D0A7 /* VoiceMessageView.swift */,
|
C328250E25CA06020062D0A7 /* VoiceMessageView.swift */,
|
||||||
B8569AE225CBB19A00DBA3DB /* DocumentView.swift */,
|
B8569AE225CBB19A00DBA3DB /* DocumentView.swift */,
|
||||||
B849789525D4A2F500D0D0B3 /* LinkPreviewView.swift */,
|
B849789525D4A2F500D0D0B3 /* LinkPreviewView.swift */,
|
||||||
|
7BAFA7592AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift */,
|
||||||
B8D84EA225DF745A005A043E /* LinkPreviewState.swift */,
|
B8D84EA225DF745A005A043E /* LinkPreviewState.swift */,
|
||||||
B8EB20EF2640F7F000773E52 /* OpenGroupInvitationView.swift */,
|
B8EB20EF2640F7F000773E52 /* OpenGroupInvitationView.swift */,
|
||||||
7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */,
|
7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */,
|
||||||
|
@ -6087,6 +6090,7 @@
|
||||||
B88FA7F2260C3EB10049422F /* OpenGroupSuggestionGrid.swift in Sources */,
|
B88FA7F2260C3EB10049422F /* OpenGroupSuggestionGrid.swift in Sources */,
|
||||||
FD71160428C95B5600B47552 /* PhotoCollectionPickerViewModel.swift in Sources */,
|
FD71160428C95B5600B47552 /* PhotoCollectionPickerViewModel.swift in Sources */,
|
||||||
FD37EA1928AC5CCA003AE748 /* NotificationSoundViewModel.swift in Sources */,
|
FD37EA1928AC5CCA003AE748 /* NotificationSoundViewModel.swift in Sources */,
|
||||||
|
7BAFA75A2AAEA281001DA43E /* LinkPreviewView_SwiftUI.swift in Sources */,
|
||||||
FD71163E28E2C82900B47552 /* SessionCell.swift in Sources */,
|
FD71163E28E2C82900B47552 /* SessionCell.swift in Sources */,
|
||||||
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */,
|
4CA485BB2232339F004B9E7D /* PhotoCaptureViewController.swift in Sources */,
|
||||||
C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */,
|
C328254925CA60E60062D0A7 /* ContextMenuVC+Action.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import NVActivityIndicatorView
|
||||||
|
import SessionUIKit
|
||||||
|
import SessionMessagingKit
|
||||||
|
|
||||||
|
public struct LinkPreviewView_SwiftUI: View {
|
||||||
|
private static let loaderSize: CGFloat = 24
|
||||||
|
private static let cancelButtonSize: CGFloat = 45
|
||||||
|
|
||||||
|
private let maxWidth: CGFloat
|
||||||
|
private let onCancel: (() -> ())?
|
||||||
|
|
||||||
|
public init(maxWidth: CGFloat, onCancel: (() -> ())? = nil) {
|
||||||
|
self.maxWidth = maxWidth
|
||||||
|
self.onCancel = onCancel
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
VStack(
|
||||||
|
alignment: .leading,
|
||||||
|
spacing: 0
|
||||||
|
) {
|
||||||
|
HStack(
|
||||||
|
alignment: .center,
|
||||||
|
spacing: 0
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LinkPreviewView_SwiftUI_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
LinkPreviewView_SwiftUI(
|
||||||
|
maxWidth: 200,
|
||||||
|
onCancel: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,14 +12,13 @@ struct MessageInfoView: View {
|
||||||
@State var index = 1
|
@State var index = 1
|
||||||
@State var showingAttachmentFullScreen = false
|
@State var showingAttachmentFullScreen = false
|
||||||
|
|
||||||
static private let cornerRadius: CGFloat = 18
|
static private let cornerRadius: CGFloat = 17
|
||||||
|
|
||||||
var actions: [ContextMenuVC.Action]
|
var actions: [ContextMenuVC.Action]
|
||||||
var messageViewModel: MessageViewModel
|
var messageViewModel: MessageViewModel
|
||||||
var isMessageFailed: Bool {
|
var isMessageFailed: Bool {
|
||||||
return [.failed, .failedToSync].contains(messageViewModel.state)
|
return [.failed, .failedToSync].contains(messageViewModel.state)
|
||||||
}
|
}
|
||||||
var snapshot: UIView?
|
|
||||||
|
|
||||||
var dismiss: (() -> Void)?
|
var dismiss: (() -> Void)?
|
||||||
|
|
||||||
|
@ -38,29 +37,9 @@ struct MessageInfoView: View {
|
||||||
spacing: 10
|
spacing: 10
|
||||||
) {
|
) {
|
||||||
// Message bubble snapshot
|
// Message bubble snapshot
|
||||||
ZStack {
|
MessageBubble(
|
||||||
if let snapshot = self.snapshot {
|
messageViewModel: messageViewModel
|
||||||
UIView_SwiftUI(view: snapshot)
|
)
|
||||||
} else {
|
|
||||||
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)
|
|
||||||
|
|
||||||
RoundedRectangle(cornerRadius: Self.cornerRadius)
|
|
||||||
.fill(themeColor: bubbleBackgroundColor)
|
|
||||||
|
|
||||||
Text(body)
|
|
||||||
.foregroundColor(themeColor: bubbleTextColor)
|
|
||||||
.padding(.vertical, Values.smallSpacing)
|
|
||||||
.padding(.horizontal, Values.mediumSpacing)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(
|
.frame(
|
||||||
maxWidth: .infinity,
|
maxWidth: .infinity,
|
||||||
maxHeight: .infinity,
|
maxHeight: .infinity,
|
||||||
|
@ -379,6 +358,209 @@ struct MessageInfoView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MessageBubble: View {
|
||||||
|
static private let cornerRadius: CGFloat = 18
|
||||||
|
|
||||||
|
let messageViewModel: MessageViewModel
|
||||||
|
var bubbleBackgroundColor: ThemeValue {
|
||||||
|
messageViewModel.variant == .standardIncoming || messageViewModel.variant == .standardIncomingDeleted ?
|
||||||
|
.messageBubble_incomingBackground :
|
||||||
|
.messageBubble_outgoingBackground
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
switch messageViewModel.cellType {
|
||||||
|
case .typingIndicator, .dateHeader, .unreadMarker: break
|
||||||
|
|
||||||
|
case .textOnlyMessage:
|
||||||
|
let inset: CGFloat = 12
|
||||||
|
let maxWidth: CGFloat = (VisibleMessageCell.getMaxWidth(for: messageViewModel) - 2 * inset)
|
||||||
|
|
||||||
|
if let linkPreview: LinkPreview = messageViewModel.linkPreview {
|
||||||
|
switch linkPreview.variant {
|
||||||
|
case .standard:
|
||||||
|
let linkPreviewView: LinkPreviewView = LinkPreviewView(maxWidth: maxWidth)
|
||||||
|
linkPreviewView.update(
|
||||||
|
with: LinkPreview.SentState(
|
||||||
|
linkPreview: linkPreview,
|
||||||
|
imageAttachment: messageViewModel.linkPreviewAttachment
|
||||||
|
),
|
||||||
|
isOutgoing: (messageViewModel.variant == .standardOutgoing),
|
||||||
|
delegate: self,
|
||||||
|
cellViewModel: messageViewModel,
|
||||||
|
bodyLabelTextColor: bodyLabelTextColor,
|
||||||
|
lastSearchText: lastSearchText
|
||||||
|
)
|
||||||
|
bubbleView.addSubview(linkPreviewView)
|
||||||
|
linkPreviewView.pin(to: bubbleView, withInset: 0)
|
||||||
|
snContentView.addArrangedSubview(bubbleBackgroundView)
|
||||||
|
self.bodyTappableLabel = linkPreviewView.bodyTappableLabel
|
||||||
|
|
||||||
|
case .openGroupInvitation:
|
||||||
|
let openGroupInvitationView: OpenGroupInvitationView = OpenGroupInvitationView(
|
||||||
|
name: (linkPreview.title ?? ""),
|
||||||
|
url: linkPreview.url,
|
||||||
|
textColor: bodyLabelTextColor,
|
||||||
|
isOutgoing: (cellViewModel.variant == .standardOutgoing)
|
||||||
|
)
|
||||||
|
bubbleView.addSubview(openGroupInvitationView)
|
||||||
|
bubbleView.pin(to: openGroupInvitationView)
|
||||||
|
snContentView.addArrangedSubview(bubbleBackgroundView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Stack view
|
||||||
|
let stackView = UIStackView(arrangedSubviews: [])
|
||||||
|
stackView.axis = .vertical
|
||||||
|
stackView.spacing = 2
|
||||||
|
|
||||||
|
// Quote view
|
||||||
|
if let quote: Quote = cellViewModel.quote {
|
||||||
|
let hInset: CGFloat = 2
|
||||||
|
let quoteView: QuoteView = QuoteView(
|
||||||
|
for: .regular,
|
||||||
|
authorId: quote.authorId,
|
||||||
|
quotedText: quote.body,
|
||||||
|
threadVariant: cellViewModel.threadVariant,
|
||||||
|
currentUserPublicKey: cellViewModel.currentUserPublicKey,
|
||||||
|
currentUserBlinded15PublicKey: cellViewModel.currentUserBlinded15PublicKey,
|
||||||
|
currentUserBlinded25PublicKey: cellViewModel.currentUserBlinded25PublicKey,
|
||||||
|
direction: (cellViewModel.variant == .standardOutgoing ?
|
||||||
|
.outgoing :
|
||||||
|
.incoming
|
||||||
|
),
|
||||||
|
attachment: cellViewModel.quoteAttachment,
|
||||||
|
hInset: hInset,
|
||||||
|
maxWidth: maxWidth
|
||||||
|
)
|
||||||
|
let quoteViewContainer = UIView(wrapping: quoteView, withInsets: UIEdgeInsets(top: 0, leading: hInset, bottom: 0, trailing: hInset))
|
||||||
|
stackView.addArrangedSubview(quoteViewContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body text view
|
||||||
|
let bodyTappableLabel = VisibleMessageCell.getBodyTappableLabel(
|
||||||
|
for: cellViewModel,
|
||||||
|
with: maxWidth,
|
||||||
|
textColor: bodyLabelTextColor,
|
||||||
|
searchText: lastSearchText,
|
||||||
|
delegate: self
|
||||||
|
)
|
||||||
|
self.bodyTappableLabel = bodyTappableLabel
|
||||||
|
stackView.addArrangedSubview(bodyTappableLabel)
|
||||||
|
|
||||||
|
// Constraints
|
||||||
|
bubbleView.addSubview(stackView)
|
||||||
|
stackView.pin(to: bubbleView, withInset: inset)
|
||||||
|
stackView.widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth).isActive = true
|
||||||
|
snContentView.addArrangedSubview(bubbleBackgroundView)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .mediaMessage:
|
||||||
|
// Body text view
|
||||||
|
if let body: String = cellViewModel.body, !body.isEmpty {
|
||||||
|
let inset: CGFloat = 12
|
||||||
|
let maxWidth: CGFloat = (VisibleMessageCell.getMaxWidth(for: cellViewModel) - 2 * inset)
|
||||||
|
let bodyTappableLabel = VisibleMessageCell.getBodyTappableLabel(
|
||||||
|
for: cellViewModel,
|
||||||
|
with: maxWidth,
|
||||||
|
textColor: bodyLabelTextColor,
|
||||||
|
searchText: lastSearchText,
|
||||||
|
delegate: self
|
||||||
|
)
|
||||||
|
|
||||||
|
self.bodyTappableLabel = bodyTappableLabel
|
||||||
|
bubbleView.addSubview(bodyTappableLabel)
|
||||||
|
bodyTappableLabel.pin(to: bubbleView, withInset: inset)
|
||||||
|
snContentView.addArrangedSubview(bubbleBackgroundView)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Album view
|
||||||
|
let maxMessageWidth: CGFloat = VisibleMessageCell.getMaxWidth(for: cellViewModel)
|
||||||
|
let albumView = MediaAlbumView(
|
||||||
|
mediaCache: mediaCache,
|
||||||
|
items: (cellViewModel.attachments?
|
||||||
|
.filter { $0.isVisualMedia })
|
||||||
|
.defaulting(to: []),
|
||||||
|
isOutgoing: (cellViewModel.variant == .standardOutgoing),
|
||||||
|
maxMessageWidth: maxMessageWidth
|
||||||
|
)
|
||||||
|
self.albumView = albumView
|
||||||
|
let size = getSize(for: cellViewModel)
|
||||||
|
albumView.set(.width, to: size.width)
|
||||||
|
albumView.set(.height, to: size.height)
|
||||||
|
albumView.loadMedia()
|
||||||
|
snContentView.addArrangedSubview(albumView)
|
||||||
|
|
||||||
|
unloadContent = { albumView.unloadMedia() }
|
||||||
|
|
||||||
|
case .audio:
|
||||||
|
guard let attachment: Attachment = cellViewModel.attachments?.first(where: { $0.isAudio }) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let voiceMessageView: VoiceMessageView = VoiceMessageView()
|
||||||
|
voiceMessageView.update(
|
||||||
|
with: attachment,
|
||||||
|
isPlaying: (playbackInfo?.state == .playing),
|
||||||
|
progress: (playbackInfo?.progress ?? 0),
|
||||||
|
playbackRate: (playbackInfo?.playbackRate ?? 1),
|
||||||
|
oldPlaybackRate: (playbackInfo?.oldPlaybackRate ?? 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
bubbleView.addSubview(voiceMessageView)
|
||||||
|
voiceMessageView.pin(to: bubbleView)
|
||||||
|
snContentView.addArrangedSubview(bubbleBackgroundView)
|
||||||
|
self.voiceMessageView = voiceMessageView
|
||||||
|
|
||||||
|
case .genericAttachment:
|
||||||
|
guard let attachment: Attachment = cellViewModel.attachments?.first else { preconditionFailure() }
|
||||||
|
|
||||||
|
let inset: CGFloat = 12
|
||||||
|
let maxWidth = (VisibleMessageCell.getMaxWidth(for: cellViewModel) - 2 * inset)
|
||||||
|
|
||||||
|
// Stack view
|
||||||
|
let stackView = UIStackView(arrangedSubviews: [])
|
||||||
|
stackView.axis = .vertical
|
||||||
|
stackView.spacing = Values.smallSpacing
|
||||||
|
|
||||||
|
// Document view
|
||||||
|
let documentView = DocumentView(attachment: attachment, textColor: bodyLabelTextColor)
|
||||||
|
stackView.addArrangedSubview(documentView)
|
||||||
|
|
||||||
|
// Body text view
|
||||||
|
if let body: String = cellViewModel.body, !body.isEmpty { // delegate should always be set at this point
|
||||||
|
let bodyContainerView: UIView = UIView()
|
||||||
|
let bodyTappableLabel = VisibleMessageCell.getBodyTappableLabel(
|
||||||
|
for: cellViewModel,
|
||||||
|
with: maxWidth,
|
||||||
|
textColor: bodyLabelTextColor,
|
||||||
|
searchText: lastSearchText,
|
||||||
|
delegate: self
|
||||||
|
)
|
||||||
|
|
||||||
|
self.bodyTappableLabel = bodyTappableLabel
|
||||||
|
bodyContainerView.addSubview(bodyTappableLabel)
|
||||||
|
bodyTappableLabel.pin(.top, to: .top, of: bodyContainerView)
|
||||||
|
bodyTappableLabel.pin(.leading, to: .leading, of: bodyContainerView, withInset: 12)
|
||||||
|
bodyTappableLabel.pin(.trailing, to: .trailing, of: bodyContainerView, withInset: -12)
|
||||||
|
bodyTappableLabel.pin(.bottom, to: .bottom, of: bodyContainerView, withInset: -12)
|
||||||
|
stackView.addArrangedSubview(bodyContainerView)
|
||||||
|
}
|
||||||
|
|
||||||
|
bubbleView.addSubview(stackView)
|
||||||
|
stackView.pin(to: bubbleView)
|
||||||
|
snContentView.addArrangedSubview(bubbleBackgroundView)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: Self.cornerRadius)
|
||||||
|
.fill(themeColor: bubbleBackgroundColor)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct InfoBlock<Content>: View where Content: View {
|
struct InfoBlock<Content>: View where Content: View {
|
||||||
let title: String
|
let title: String
|
||||||
let content: () -> Content
|
let content: () -> Content
|
||||||
|
|
Loading…
Reference in New Issue