Adopt new colors for elements in chat message bubbles.

• new background color for quoted messages.
• new colors for buttons in chat message bubbles.

Also cleaned up Theme and UIColor extension.
This commit is contained in:
Igor Solomennikov 2026-02-20 16:21:08 -08:00 committed by GitHub
parent 3f70de2fc2
commit 5da1c6e1d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 165 additions and 91 deletions

View File

@ -46,7 +46,7 @@ class AudioMessagePresenter: AudioPresenter {
}
func primaryElementColor(isIncoming: Bool) -> UIColor {
return ConversationStyle.bubbleTextColor(isIncoming: isIncoming)
isIncoming ? .Signal.label : .Signal.ColorBase.labelPrimary
}
func playedColor(isIncoming: Bool) -> UIColor {
@ -54,15 +54,22 @@ class AudioMessagePresenter: AudioPresenter {
}
func unplayedColor(isIncoming: Bool) -> UIColor {
isIncoming ? .Signal.tertiaryLabel : .ows_whiteAlpha40
isIncoming ? .Signal.tertiaryLabel : .Signal.ColorBase.labelTertiary
}
func thumbColor(isIncoming: Bool) -> UIColor {
primaryElementColor(isIncoming: isIncoming)
}
func playPauseContainerBackgroundColor(isIncoming: Bool) -> UIColor {
isIncoming ? .Signal.materialButton : .ows_whiteAlpha20
func playPauseContainerBackgroundColor(
conversationStyle: ConversationStyle,
isIncoming: Bool,
) -> UIColor {
switch (isIncoming, conversationStyle.hasWallpaper) {
case (true, true): .Signal.MaterialBase.button
case (true, _): .Signal.LightBase.button
case (false, _): .Signal.ColorBase.button
}
}
func playPauseAnimationColor(isIncoming: Bool) -> ColorValueProvider {

View File

@ -50,7 +50,10 @@ protocol AudioPresenter {
func unplayedColor(isIncoming: Bool) -> UIColor
// Color of circle enclosing the play/pause icon.
func playPauseContainerBackgroundColor(isIncoming: Bool) -> UIColor
func playPauseContainerBackgroundColor(
conversationStyle: ConversationStyle,
isIncoming: Bool,
) -> UIColor
// Last chance to adjust constraints before the view appears.
func configureForRendering(conversationStyle: ConversationStyle)

View File

@ -136,7 +136,10 @@ class AudioMessageView: ManualStackView {
keypath: fillColorKeypath,
)
playPauseContainer.backgroundColor = presentation.playPauseContainerBackgroundColor(isIncoming: isIncoming)
playPauseContainer.backgroundColor = presentation.playPauseContainerBackgroundColor(
conversationStyle: conversationStyle,
isIncoming: isIncoming,
)
playPauseContainer.addSubviewToCenterOnSuperview(playPauseAnimation, size: CGSize(square: 24))
presentation.playedDotContainer.addSubviewToCenterOnSuperview(playedDotAnimation, size: CGSize(square: 16))

View File

@ -49,7 +49,7 @@ public class CVContactShareView: ManualStackView {
avatarView.image = state.avatar
let disclosureColor = state.isIncoming ? UIColor.Signal.tertiaryLabel : Theme.darkThemeTertiaryLabel
let disclosureColor: UIColor = state.isIncoming ? .Signal.tertiaryLabel : .Signal.ColorBase.labelTertiary
disclosureImageView.setTemplateImage(
UIImage(imageLiteralResourceName: "chevron-right-20"),
tintColor: disclosureColor,
@ -77,7 +77,7 @@ public class CVContactShareView: ManualStackView {
}
private static func contactNameLabelConfig(state: State) -> CVLabelConfig {
let textColor = state.conversationStyle.bubbleTextColor(isIncoming: state.isIncoming)
let textColor: UIColor = state.isIncoming ? .Signal.label : .Signal.ColorBase.labelPrimary
return CVLabelConfig.unstyledText(
state.contactShare.displayName,
font: .dynamicTypeHeadline,

View File

@ -104,13 +104,33 @@ public class CVQuotedMessageView: ManualStackViewWithLayer {
}
let stripeThickness: CGFloat = 4
var stripeColor: UIColor {
switch (isIncoming, conversationStyle.hasWallpaper) {
case (true, true): .Signal.MaterialBase.fillPrimary
case (true, _): .Signal.LightBase.fillPrimary
case (false, _): .Signal.ColorBase.fillPrimary
}
}
var backgroundTint: UIColor {
switch (isIncoming, conversationStyle.hasWallpaper) {
case (true, true): .Signal.MaterialBase.fillTertiary
case (true, _): .Signal.LightBase.fillSecondary
case (false, _): .Signal.ColorBase.fillSecondary
}
}
private var textColor: UIColor {
isIncoming ? .Signal.label : .Signal.ColorBase.labelInverted
}
var quotedAuthorFont: UIFont { UIFont.dynamicTypeSubheadlineClamped.semibold() }
var quotedAuthorColor: UIColor { conversationStyle.bubbleTextColor(isIncoming: isIncoming) }
var quotedTextColor: UIColor { conversationStyle.bubbleTextColor(isIncoming: isIncoming) }
var quotedAuthorColor: UIColor { textColor }
var quotedTextColor: UIColor { textColor }
var quotedTextFont: UIFont { UIFont.dynamicTypeSubheadline }
var fileTypeTextColor: UIColor { conversationStyle.bubbleTextColor(isIncoming: isIncoming) }
var fileTypeTextColor: UIColor { textColor }
var fileTypeFont: UIFont { quotedTextFont.italic() }
var filenameTextColor: UIColor { conversationStyle.bubbleTextColor(isIncoming: isIncoming) }
var filenameTextColor: UIColor { textColor }
var filenameFont: UIFont { quotedTextFont }
var quotedAuthorHeight: CGFloat { quotedAuthorFont.lineHeight }
let quotedAttachmentSizeWithoutQuotedText: CGFloat = 64
@ -465,7 +485,7 @@ public class CVQuotedMessageView: ManualStackViewWithLayer {
) -> ManualLayoutView {
// Background
tintView.backgroundColor = .Signal.materialTertiaryFill
tintView.backgroundColor = configurator.backgroundTint
bubbleView.addSubviewToFillSuperviewMargins(tintView)
// For incoming messages, manipulate leading margin
// to render stripe.
@ -516,7 +536,7 @@ public class CVQuotedMessageView: ManualStackViewWithLayer {
var hStackSubviews = [UIView]()
stripeView.backgroundColor = .Signal.materialPrimaryFill
stripeView.backgroundColor = configurator.stripeColor
hStackSubviews.append(stripeView)
var innerVStackSubviews = [UIView]()

View File

@ -42,11 +42,16 @@ public class CVComponentBottomButtons: CVComponentBase, CVComponent {
for action in actions {
let buttonView = CVMessageActionButton(action: action)
if isIncoming {
buttonView.backgroundColor = .Signal.materialButton
if conversationStyle.hasWallpaper {
buttonView.backgroundColor = .Signal.MaterialBase.button
} else {
buttonView.backgroundColor = .Signal.LightBase.button
}
buttonView.textColor = .Signal.label
} else {
buttonView.backgroundColor = Theme.darkThemeMaterialButton
buttonView.backgroundColor = .Signal.ColorBase.button
buttonView.textColor = .Signal.ColorBase.labelPrimary
}
buttonView.textColor = conversationStyle.bubbleTextColor(isIncoming: isIncoming)
subviews.append(buttonView)
componentView.buttonViews.append(buttonView)
}

View File

@ -255,7 +255,7 @@ public class CVComponentSystemMessage: CVComponentBase, CVRootComponent {
if interaction is OWSGroupCallMessage {
.Signal.green
} else if hasWallpaper {
.Signal.materialButton
.Signal.MaterialBase.button
} else {
.Signal.secondaryFill
}

View File

@ -52,7 +52,10 @@ class AudioAllMediaPresenter: AudioPresenter {
return playedColor(isIncoming: true)
}
func playPauseContainerBackgroundColor(isIncoming: Bool) -> UIColor {
func playPauseContainerBackgroundColor(
conversationStyle: ConversationStyle,
isIncoming: Bool,
) -> UIColor {
return Theme.isDarkThemeEnabled ? .ows_gray65 : .ows_gray05
}

View File

@ -181,43 +181,23 @@ public extension UIColor {
// MARK: Accent Colors
/// Nav Bar, Primary Buttons
@objc(ows_accentBlueColor)
class var ows_accentBlue: UIColor {
// Ultramarine UI
return UIColor(rgbHex: 0x2C6BED)
}
class var ows_accentBlueDark: UIColor {
// Ultramarine UI Light
return UIColor(rgbHex: 0x6191F3)
}
class var ows_accentBlueTint: UIColor {
return UIColor(rgbHex: 0xB0C8F9)
}
/// Making calls, success states
@objc(ows_accentGreenColor)
class var ows_accentGreen: UIColor {
return UIColor(rgbHex: 0x4CAF50)
}
/// Warning, update states
class var ows_accentYellow: UIColor {
return UIColor(rgbHex: 0xFFD624)
}
/// Ending calls, error states
@objc(ows_accentRedColor)
class var ows_accentRed: UIColor {
return UIColor(rgbHex: 0xF44336)
}
/// mute unmute background color
class var ows_accentIndigo: UIColor {
return UIColor(rgbHex: 0x5951c8)
}
// MARK: - GreyScale
@objc(ows_whiteColor)

View File

@ -304,14 +304,14 @@ public struct ConversationStyle {
// MARK: - Secondary text color
public static var bubbleSecondaryTextColorIncomingThemed: ThemedColor {
private static var bubbleSecondaryTextColorIncomingThemed: ThemedColor {
ThemedColor(
light: Theme.lightThemeSecondaryTextAndIconColor,
dark: Theme.darkThemeSecondaryTextAndIconColor,
)
}
public static var bubbleSecondaryTextColorOutgoingThemed: ThemedColor {
private static var bubbleSecondaryTextColorOutgoingThemed: ThemedColor {
ThemedColor(
light: UIColor.ows_whiteAlpha80,
dark: UIColor.ows_whiteAlpha60,

View File

@ -271,7 +271,6 @@ public final class Theme {
])
}
@objc
public class var backgroundColor: UIColor {
isDarkThemeEnabled
? darkThemeBackgroundColor
@ -279,13 +278,9 @@ public final class Theme {
}
public class var secondaryBackgroundColor: UIColor {
isDarkThemeEnabled
? darkThemeSecondaryBackgroundColor
: UIColor.Signal.secondaryBackground.resolvedColor(with: lightTraitCollection)
}
public class var darkThemeSecondaryBackgroundColor: UIColor {
UIColor.Signal.secondaryBackground.resolvedColor(with: darkTraitCollection)
UIColor.Signal.secondaryBackground.resolvedColor(
with: isDarkThemeEnabled ? darkTraitCollection : lightTraitCollection,
)
}
public static var actionSheetBackgroundColor: UIColor {
@ -304,11 +299,7 @@ public final class Theme {
if #available(iOS 26, *) {
return primaryTextColor
}
return legacyPrimaryIconColor
}
public class var legacyPrimaryIconColor: UIColor {
isDarkThemeEnabled ? darkThemeLegacyPrimaryIconColor : lightThemeLegacyPrimaryIconColor
return isDarkThemeEnabled ? darkThemeLegacyPrimaryIconColor : lightThemeLegacyPrimaryIconColor
}
public class var secondaryTextAndIconColor: UIColor {
@ -330,7 +321,6 @@ public final class Theme {
UIColor.Signal.accent.resolvedColor(with: currentThemeTraitCollection)
}
@objc
public class var launchScreenBackgroundColor: UIColor {
backgroundColor
}
@ -410,10 +400,6 @@ public final class Theme {
UIColor.Signal.secondaryLabel.resolvedColor(with: darkTraitCollection)
}
public class var darkThemeTertiaryLabel: UIColor {
UIColor.Signal.tertiaryLabel.resolvedColor(with: darkTraitCollection)
}
public class var darkThemeWashColor: UIColor { .ows_gray75 }
public class var darkThemeNavbarBackgroundColor: UIColor {
@ -442,10 +428,6 @@ public final class Theme {
public class var darkThemeTableView2PresentedSeparatorColor: UIColor { .ows_gray65 }
public class var darkThemeMaterialButton: UIColor {
UIColor.Signal.materialButton.resolvedColor(with: darkTraitCollection)
}
// MARK: - Blur Effect
public class var barBlurEffect: UIBlurEffect {
@ -472,12 +454,6 @@ public final class Theme {
isDarkThemeEnabled ? .black : .default
}
public class var searchFieldBackgroundColor: UIColor { washColor }
public class var searchFieldElevatedBackgroundColor: UIColor {
isDarkThemeEnabled ? .ows_gray75 : .ows_gray12
}
#if TESTABLE_BUILD
private var isDarkThemeEnabledForTests: Bool?

View File

@ -329,32 +329,109 @@ extension UIColor.Signal {
// MARK: Material
public static var materialPrimaryFill: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.24),
dark: UIColor(white: 1, alpha: 0.48),
)
/// Designed to be used on top of material (blur / glass) backgrounds.
public enum MaterialBase {
public static var fillPrimary: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.24),
dark: UIColor(white: 1, alpha: 0.48),
)
}
public static var fillSecondary: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.16),
dark: UIColor(white: 1, alpha: 0.24),
)
}
public static var fillTertiary: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.1),
dark: UIColor(white: 1, alpha: 0.16),
)
}
public static var button: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.12),
dark: UIColor(white: 1, alpha: 0.2),
)
}
}
public static var materialSecondaryFill: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.16),
dark: UIColor(white: 1, alpha: 0.32),
)
// MARK: Light
/// To be used on top of neutral backgrounds
/// (eg incoming message bubbles when no wallpaper).
public enum LightBase {
public static var fillPrimary: UIColor {
UIColor(
light: UIColor(white: 1, alpha: 1),
dark: UIColor(white: 1, alpha: 0.48),
)
}
public static var fillSecondary: UIColor {
UIColor(
light: UIColor(white: 1, alpha: 0.6),
dark: UIColor(white: 1, alpha: 0.16),
)
}
public static var fillTertiary: UIColor {
UIColor(
light: UIColor(white: 1, alpha: 0.2),
dark: UIColor(white: 1, alpha: 0.08),
)
}
public static var button: UIColor {
UIColor(
light: UIColor(white: 1, alpha: 0.8),
dark: UIColor(white: 1, alpha: 0.2),
)
}
}
public static var materialTertiaryFill: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.1),
dark: UIColor(white: 1, alpha: 0.16),
)
}
// MARK: Color
public static var materialButton: UIColor {
UIColor(
light: UIColor(white: 0, alpha: 0.12),
dark: UIColor(white: 1, alpha: 0.2),
)
/// To be used on top of any arbitrary color. Fixed across light/dark theme.
public enum ColorBase {
public static var labelPrimary: UIColor {
UIColor(white: 1, alpha: 1)
}
public static var labelSecondary: UIColor {
UIColor(white: 1, alpha: 0.8)
}
public static var labelTertiary: UIColor {
UIColor(white: 1, alpha: 0.4)
}
public static var labelInverted: UIColor {
UIColor(white: 0, alpha: 1)
}
public static var fillPrimary: UIColor {
UIColor(white: 1, alpha: 0.8)
}
public static var fillSecondary: UIColor {
UIColor(white: 1, alpha: 0.6)
}
public static var fillTertiary: UIColor {
UIColor(white: 1, alpha: 0.2)
}
public static var button: UIColor {
UIColor(white: 1, alpha: 0.2)
}
}
@available(iOS 26, *)

View File

@ -155,7 +155,7 @@ public class StickerHorizontalListView: UICollectionView {
var background = UIBackgroundConfiguration.clear()
background.cornerRadius = cellSize / 2
if item.isSelected {
background.backgroundColor = .Signal.materialButton
background.backgroundColor = .Signal.MaterialBase.button
} else {
background.backgroundColor = .clear
}