diff --git a/CHANGELOG.md b/CHANGELOG.md index 48fa1823..8e14a6e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). # Upcoming ### ✅ Added +- Colors and images for voice recording view [#704](https://github.com/GetStream/stream-chat-swiftui/pull/704) + - `ColorPalette.voiceMessageControlBackground` + - `Images.pauseFilled` - Exposes all the default message actions [#711](https://github.com/GetStream/stream-chat-swiftui/pull/711) ### 🐞 Fixed - Use bright color for typing indicator animation in dark mode [#702](https://github.com/GetStream/stream-chat-swiftui/pull/702) @@ -11,6 +14,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix composer command view not Themable [#710](https://github.com/GetStream/stream-chat-swiftui/pull/710) - Fix reactions users view not paginating results [#712](https://github.com/GetStream/stream-chat-swiftui/pull/712) +### 🔄 Changed +- Support theming and update layout of `VoiceRecordingContainerView` [#704](https://github.com/GetStream/stream-chat-swiftui/pull/704) +- Use `ColorPalette.highlightedAccentBackground` for `AudioVisualizationView.highlightedBarColor` [#704](https://github.com/GetStream/stream-chat-swiftui/pull/704) + # [4.69.0](https://github.com/GetStream/stream-chat-swiftui/releases/tag/4.69.0) _December 18, 2024_ diff --git a/Sources/StreamChatSwiftUI/ChatChannel/Composer/VoiceRecording/AddedVoiceRecordingsView.swift b/Sources/StreamChatSwiftUI/ChatChannel/Composer/VoiceRecording/AddedVoiceRecordingsView.swift index 185d5543..e38726c5 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/Composer/VoiceRecording/AddedVoiceRecordingsView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/Composer/VoiceRecording/AddedVoiceRecordingsView.swift @@ -29,6 +29,7 @@ struct AddedVoiceRecordingsView: View { let recording = addedVoiceRecordings[i] VoiceRecordingView( handler: voiceRecordingHandler, + textColor: textColor(currentUser: true), addedVoiceRecording: recording, index: i ) diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/AudioVisualizationView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/AudioVisualizationView.swift index d618fb8f..9c7f5627 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/AudioVisualizationView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/AudioVisualizationView.swift @@ -44,7 +44,7 @@ open class AudioVisualizationView: UIView { open var barColor: UIColor { colors.textLowEmphasis } /// The colour of the waveform bar that is part of the "played" duration. - open var highlightedBarColor: UIColor { .blue } + open var highlightedBarColor: UIColor { colors.highlightedAccentBackground } /// The colour of the waveform bar's background. open var barBackgroundColor: UIColor { colors.background } diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift index 8dc2b2f6..8e44411c 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/AsyncVoiceMessages/VoiceRecordingContainerView.swift @@ -39,7 +39,7 @@ public struct VoiceRecordingContainerView: View { } public var body: some View { - VStack { + VStack(spacing: 0) { VStack { if let quotedMessage = utils.messageCachingUtils.quotedMessage(for: message) { factory.makeQuotedMessageView( @@ -49,22 +49,24 @@ public struct VoiceRecordingContainerView: View { scrolledId: $scrolledId ) } - - ForEach(message.voiceRecordingAttachments, id: \.self) { attachment in - VoiceRecordingView( - handler: handler, - addedVoiceRecording: AddedVoiceRecording( - url: attachment.payload.voiceRecordingURL, - duration: attachment.payload.duration ?? 0, - waveform: attachment.payload.waveformData ?? [] - ), - index: index(for: attachment) - ) + VStack(spacing: 2) { + ForEach(message.voiceRecordingAttachments, id: \.self) { attachment in + VoiceRecordingView( + handler: handler, + textColor: textColor(for: message), + addedVoiceRecording: AddedVoiceRecording( + url: attachment.payload.voiceRecordingURL, + duration: attachment.payload.duration ?? 0, + waveform: attachment.payload.waveformData ?? [] + ), + index: index(for: attachment) + ) + .padding(.all, 8) + .background(Color(colors.background8)) + .roundWithBorder(cornerRadius: 14) + } } } - .padding(.all, 8) - .background(Color(colors.background8)) - .cornerRadius(16) if !message.text.isEmpty { AttachmentTextView(message: message) .frame(maxWidth: .infinity) @@ -94,7 +96,11 @@ public struct VoiceRecordingContainerView: View { } .modifier( factory.makeMessageViewModifier( - for: MessageModifierInfo(message: message, isFirst: isFirst) + for: MessageModifierInfo( + message: message, + isFirst: isFirst, + cornerRadius: 16 + ) ) ) } @@ -114,6 +120,7 @@ struct VoiceRecordingView: View { @State var rate: AudioPlaybackRate = .normal @ObservedObject var handler: VoiceRecordingHandler + let textColor: Color let addedVoiceRecording: AddedVoiceRecording let index: Int @@ -135,10 +142,17 @@ struct VoiceRecordingView: View { Button(action: { handlePlayTap() }, label: { - Image(systemName: isPlaying ? "pause.fill" : "play.fill") - .padding(.all, 8) + Image(uiImage: isPlaying ? images.pauseFilled : images.playFilled) + .frame(width: 36, height: 36) .foregroundColor(.primary) - .modifier(ShadowViewModifier(firstRadius: 2, firstY: 4)) + .modifier( + ShadowViewModifier( + backgroundColor: colors.voiceMessageControlBackground, + cornerRadius: 18, + firstRadius: 2, + firstY: 4 + ) + ) }) .opacity(loading ? 0 : 1) .overlay(loading ? ProgressView() : nil) @@ -152,6 +166,7 @@ struct VoiceRecordingView: View { ) .bold() .lineLimit(1) + .foregroundColor(textColor) HStack { RecordingDurationView( @@ -199,7 +214,7 @@ struct VoiceRecordingView: View { Image(uiImage: images.fileAac) .resizable() .aspectRatio(contentMode: .fit) - .frame(height: 36) + .frame(height: 40) } } .onReceive(handler.$context, perform: { value in diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift index 553fe025..b7600ac8 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/ImageAttachmentView.swift @@ -110,9 +110,11 @@ public struct AttachmentTextView: View { @Injected(\.fonts) private var fonts var message: ChatMessage + let injectedBackgroundColor: UIColor? - public init(message: ChatMessage) { + public init(message: ChatMessage, injectedBackgroundColor: UIColor? = nil) { self.message = message + self.injectedBackgroundColor = injectedBackgroundColor } public var body: some View { @@ -127,6 +129,9 @@ public struct AttachmentTextView: View { } private var backgroundColor: UIColor { + if let injectedBackgroundColor { + return injectedBackgroundColor + } var colors = colors if message.isSentByCurrentUser { if message.type == .ephemeral { diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListHelperViews.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListHelperViews.swift index e2471b7d..f02274e5 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListHelperViews.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/MessageListHelperViews.swift @@ -202,4 +202,9 @@ extension View { return Color(colors.messageOtherUserTextColor) } } + + func textColor(currentUser: Bool) -> Color { + @Injected(\.colors) var colors + return currentUser ? Color(colors.messageCurrentUserTextColor) : Color(colors.messageOtherUserTextColor) + } } diff --git a/Sources/StreamChatSwiftUI/ColorPalette.swift b/Sources/StreamChatSwiftUI/ColorPalette.swift index cd540a36..81600842 100644 --- a/Sources/StreamChatSwiftUI/ColorPalette.swift +++ b/Sources/StreamChatSwiftUI/ColorPalette.swift @@ -79,6 +79,7 @@ public struct ColorPalette { public lazy var reactionCurrentUserColor: UIColor? = UIColor(tintColor) public lazy var reactionOtherUserColor: UIColor? = textLowEmphasis public lazy var selectedReactionBackgroundColor: UIColor? = nil + public var voiceMessageControlBackground: UIColor = .streamWhiteStatic // MARK: - Composer diff --git a/Sources/StreamChatSwiftUI/Images.swift b/Sources/StreamChatSwiftUI/Images.swift index eb236ed8..e34bc504 100644 --- a/Sources/StreamChatSwiftUI/Images.swift +++ b/Sources/StreamChatSwiftUI/Images.swift @@ -237,6 +237,7 @@ public class Images { public var play: UIImage = loadImageSafely(with: "play") public var playFilled: UIImage = UIImage(systemName: "play.fill")! public var pause: UIImage = loadImageSafely(with: "pause") + public var pauseFilled: UIImage = loadImageSafely(with: "pause.fill") public var checkmarkFilled: UIImage = UIImage(systemName: "checkmark.circle.fill")! diff --git a/Sources/StreamChatSwiftUI/Utils/Modifiers.swift b/Sources/StreamChatSwiftUI/Utils/Modifiers.swift index 49172578..9796bfe9 100644 --- a/Sources/StreamChatSwiftUI/Utils/Modifiers.swift +++ b/Sources/StreamChatSwiftUI/Utils/Modifiers.swift @@ -8,12 +8,13 @@ import SwiftUI struct ShadowViewModifier: ViewModifier { @Injected(\.colors) private var colors + var backgroundColor: UIColor = .systemBackground var cornerRadius: CGFloat = 16 var firstRadius: CGFloat = 10 var firstY: CGFloat = 12 func body(content: Content) -> some View { - content.background(Color(UIColor.systemBackground)) + content.background(Color(backgroundColor)) .cornerRadius(cornerRadius) .modifier(ShadowModifier(firstRadius: firstRadius, firstY: firstY)) .overlay( diff --git a/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift b/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift index 8e6eeec9..c2bb39f0 100644 --- a/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift +++ b/StreamChatSwiftUITests/Tests/ChatChannel/ChatChannelTestHelpers.swift @@ -200,23 +200,28 @@ class ChatChannelTestHelpers { return fileAttachments } + static func voiceRecordingAttachments(count: Int) -> [AnyChatMessageAttachment] { + (0.. Void) { + guard let streamChat else { return } + var appearance = streamChat.appearance + block(&appearance) + streamChat.appearance = appearance + } }