Skip to content

Commit

Permalink
Adjust layout and colors of the voice recording view
Browse files Browse the repository at this point in the history
  • Loading branch information
laevandus committed Jan 8, 2025
1 parent af374ba commit 3738c4f
Show file tree
Hide file tree
Showing 23 changed files with 162 additions and 40 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ 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.voiceMessageCurrentUserBackground` and `ColorPalette.voiceMessageOtherUserBackground`
- `ColorPalette.voiceMessageCurrentUserRecordingBackground` and `ColorPalette.voiceMessageOtherUserRecordingBackground`
- `ColorPalette.staticWhiteBackground`
- `Images.pauseFilled`

### 🐞 Fixed
- Use bright color for typing indicator animation in dark mode [#702](https://github.com/GetStream/stream-chat-swiftui/pull/702)

### 🔄 Changed
- `VoiceRecordingContainerView`: adjust layout, update background colors and corner radii, use configurable text colors and assets [#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_

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct AddedVoiceRecordingsView: View {
let recording = addedVoiceRecordings[i]
VoiceRecordingView(
handler: voiceRecordingHandler,
textColor: textColor(currentUser: true),
addedVoiceRecording: recording,
index: i
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public struct VoiceRecordingContainerView<Factory: ViewFactory>: View {
}

public var body: some View {
VStack {
VStack(spacing: 0) {
VStack {
if let quotedMessage = utils.messageCachingUtils.quotedMessage(for: message) {
factory.makeQuotedMessageView(
Expand All @@ -49,24 +49,26 @@ public struct VoiceRecordingContainerView<Factory: ViewFactory>: 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(recordingItemBackgroundColor))
.roundWithBorder(cornerRadius: 14)
}
}
}
.padding(.all, 8)
.background(Color(colors.background8))
.cornerRadius(16)
if !message.text.isEmpty {
AttachmentTextView(message: message)
AttachmentTextView(message: message, injectedBackgroundColor: bubbleBackgroundColor)
.frame(maxWidth: .infinity)
}
}
Expand Down Expand Up @@ -94,11 +96,28 @@ public struct VoiceRecordingContainerView<Factory: ViewFactory>: View {
}
.modifier(
factory.makeMessageViewModifier(
for: MessageModifierInfo(message: message, isFirst: isFirst)
for: MessageModifierInfo(
message: message,
isFirst: isFirst,
injectedBackgroundColor: bubbleBackgroundColor,
cornerRadius: 16
)
)
)
}

private var bubbleBackgroundColor: UIColor {
message.isSentByCurrentUser ?
colors.voiceMessageCurrentUserBackground :
colors.voiceMessageOtherUserBackground
}

private var recordingItemBackgroundColor: UIColor {
message.isSentByCurrentUser ?
colors.voiceMessageCurrentUserRecordingBackground :
colors.voiceMessageOtherUserRecordingBackground
}

private func index(for attachment: ChatMessageVoiceRecordingAttachment) -> Int {
message.voiceRecordingAttachments.firstIndex(of: attachment) ?? 0
}
Expand All @@ -114,6 +133,7 @@ struct VoiceRecordingView: View {
@State var rate: AudioPlaybackRate = .normal
@ObservedObject var handler: VoiceRecordingHandler

let textColor: Color
let addedVoiceRecording: AddedVoiceRecording
let index: Int

Expand All @@ -135,10 +155,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.staticWhiteBackground,
cornerRadius: 18,
firstRadius: 2,
firstY: 4
)
)
})
.opacity(loading ? 0 : 1)
.overlay(loading ? ProgressView() : nil)
Expand All @@ -152,6 +179,7 @@ struct VoiceRecordingView: View {
)
.bold()
.lineLimit(1)
.foregroundColor(textColor)

HStack {
RecordingDurationView(
Expand Down Expand Up @@ -199,7 +227,7 @@ struct VoiceRecordingView: View {
Image(uiImage: images.fileAac)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 36)
.frame(height: 40)
}
}
.onReceive(handler.$context, perform: { value in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,9 @@ extension View {
return Color(colors.messageOtherUserTextColor)
}
}

func textColor(currentUser: Bool) -> Color {
@Injected(\.colors) var colors
return currentUser ? Color(colors.messageCurrentUserTextColor) : Color(colors.messageOtherUserTextColor)
}
}
6 changes: 6 additions & 0 deletions Sources/StreamChatSwiftUI/ColorPalette.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public struct ColorPalette {
public var highlightedAccentBackground: UIColor = .streamAccentBlue
public var highlightedAccentBackground1: UIColor = .streamBlueAlice
public var pinnedBackground: UIColor = .streamHighlight
public var staticWhiteBackground: UIColor = .streamWhiteStatic

// MARK: - Borders and shadows

Expand Down Expand Up @@ -79,6 +80,10 @@ public struct ColorPalette {
public lazy var reactionCurrentUserColor: UIColor? = UIColor(tintColor)
public lazy var reactionOtherUserColor: UIColor? = textLowEmphasis
public lazy var selectedReactionBackgroundColor: UIColor? = nil
public var voiceMessageCurrentUserBackground: UIColor = .streamInnerBorder
public var voiceMessageOtherUserBackground: UIColor = .streamBarsBackground
public var voiceMessageCurrentUserRecordingBackground: UIColor = .streamBarsBackground
public var voiceMessageOtherUserRecordingBackground: UIColor = .streamBarsBackground

// MARK: - Composer

Expand Down Expand Up @@ -114,6 +119,7 @@ private extension UIColor {
static let streamInnerBorder = mode(0xdbdde1, 0x272a30)
static let streamHighlight = mode(0xfbf4dd, 0x333024)
static let streamDisabled = mode(0xb4b7bb, 0x4c525c)
static let streamBarsBackground = mode(0xffffff, 0x17191c)

// Currently we are not using the correct shadow color from figma's color palette. This is to avoid
// an issue with snapshots inconsistency between Intel vs M1. We can't use shadows with transparency.
Expand Down
1 change: 1 addition & 0 deletions Sources/StreamChatSwiftUI/Images.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")!

Expand Down
3 changes: 2 additions & 1 deletion Sources/StreamChatSwiftUI/Utils/Modifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,23 +200,28 @@ class ChatChannelTestHelpers {
return fileAttachments
}

static func voiceRecordingAttachments(count: Int) -> [AnyChatMessageAttachment] {
(0..<count).map { index in
let title = index == 0 ? "Recording" : "Recording-\(index)"
let payload = VoiceRecordingAttachmentPayload(
title: title,
voiceRecordingRemoteURL: .localYodaImage,
file: try! .init(url: .localYodaImage),
duration: Double(index) + 5.0,
waveformData: [0, 0.1, 0.5, 1],
extraData: nil
)
return ChatMessageVoiceRecordingAttachment(
id: .unique,
type: .voiceRecording,
payload: payload,
downloadingState: nil,
uploadingState: nil
).asAnyAttachment
}
}

static var voiceRecordingAttachments: [AnyChatMessageAttachment] {
let payload = VoiceRecordingAttachmentPayload(
title: "Recording",
voiceRecordingRemoteURL: .localYodaImage,
file: try! .init(url: .localYodaImage),
duration: 5,
waveformData: [0, 0.1, 0.5, 1],
extraData: nil
)
let attachment = ChatMessageVoiceRecordingAttachment(
id: .unique,
type: .voiceRecording,
payload: payload,
downloadingState: nil,
uploadingState: nil
).asAnyAttachment

return [attachment]
voiceRecordingAttachments(count: 1)
}
}
60 changes: 60 additions & 0 deletions StreamChatSwiftUITests/Tests/ChatChannel/MessageView_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,66 @@ class MessageView_Tests: StreamChatTestCase {
size: CGSize(width: defaultScreenSize.width, height: 130)
)
}

func test_messageViewVoiceRecordingWithTextFromParticipantMultiple_snapshot() {
// Given
let voiceMessage = ChatMessage.mock(
id: .unique,
cid: .unique,
text: "Hello",
author: .mock(id: .unique),
attachments: ChatChannelTestHelpers.voiceRecordingAttachments(count: 3),
isSentByCurrentUser: false
)

// When
let view = MessageView(
factory: DefaultViewFactory.shared,
message: voiceMessage,
contentWidth: defaultScreenSize.width,
isFirst: true,
scrolledId: .constant(nil)
)
.frame(width: defaultScreenSize.width, height: 250)
.padding()

// Then
AssertSnapshot(
view,
variants: .onlyUserInterfaceStyles,
size: CGSize(width: defaultScreenSize.width, height: 250)
)
}

func test_messageViewVoiceRecordingWithTextFromMeMultiple_snapshot() {
// Given
let voiceMessage = ChatMessage.mock(
id: .unique,
cid: .unique,
text: "Hello",
author: .mock(id: .unique),
attachments: ChatChannelTestHelpers.voiceRecordingAttachments(count: 3),
isSentByCurrentUser: true
)

// When
let view = MessageView(
factory: DefaultViewFactory.shared,
message: voiceMessage,
contentWidth: defaultScreenSize.width,
isFirst: true,
scrolledId: .constant(nil)
)
.frame(width: defaultScreenSize.width, height: 250)
.padding()

// Then
AssertSnapshot(
view,
variants: .onlyUserInterfaceStyles,
size: CGSize(width: defaultScreenSize.width, height: 250)
)
}

func test_messageViewFileText_snapshot() {
// Given
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3738c4f

Please sign in to comment.