From c30412cf338e496f4cf7e0188074378e893b9a12 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 3 May 2024 13:50:52 -0700 Subject: [PATCH 1/4] model [nfc]: Move dartdocs to _StreamColorVariant members We'll soon make this enum public, for consuming code to use directly to get individual colors from the swatch. Move the dartdocs now, to simplify the diff in that upcoming change. --- lib/api/model/model.dart | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/api/model/model.dart b/lib/api/model/model.dart index 25b0e1debb..009f28f542 100644 --- a/lib/api/model/model.dart +++ b/lib/api/model/model.dart @@ -469,22 +469,10 @@ class StreamColorSwatch extends ColorSwatch<_StreamColorVariant> { Color get unreadCountBadgeBackground => this[_StreamColorVariant.unreadCountBadgeBackground]!; - /// The stream icon on a plain-colored surface, such as white. - /// - /// For the icon on a [barBackground]-colored surface, - /// use [iconOnBarBackground] instead. Color get iconOnPlainBackground => this[_StreamColorVariant.iconOnPlainBackground]!; - /// The stream icon on a [barBackground]-colored surface. - /// - /// For the icon on a plain surface, use [iconOnPlainBackground] instead. - /// This color is chosen to enhance contrast with [barBackground]: - /// Color get iconOnBarBackground => this[_StreamColorVariant.iconOnBarBackground]!; - /// The background color of a bar representing a stream, like a recipient bar. - /// - /// Use this in the message list, the "Inbox" view, and the "Streams" view. Color get barBackground => this[_StreamColorVariant.barBackground]!; static Map<_StreamColorVariant, Color> _compute(int base) { @@ -540,8 +528,23 @@ class StreamColorSwatch extends ColorSwatch<_StreamColorVariant> { enum _StreamColorVariant { base, unreadCountBadgeBackground, + + /// The stream icon on a plain-colored surface, such as white. + /// + /// For the icon on a [barBackground]-colored surface, + /// use [iconOnBarBackground] instead. iconOnPlainBackground, + + /// The stream icon on a [barBackground]-colored surface. + /// + /// For the icon on a plain surface, use [iconOnPlainBackground] instead. + /// This color is chosen to enhance contrast with [barBackground]: + /// iconOnBarBackground, + + /// The background color of a bar representing a stream, like a recipient bar. + /// + /// Use this in the message list, the "Inbox" view, and the "Streams" view. barBackground, } From 43b2efd33bd29806ed68035cbdfbaa4048e613f5 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 3 May 2024 13:52:42 -0700 Subject: [PATCH 2/4] model [nfc]: Add dartdoc for _StreamColorVariant.base --- lib/api/model/model.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/api/model/model.dart b/lib/api/model/model.dart index 009f28f542..6b249196ee 100644 --- a/lib/api/model/model.dart +++ b/lib/api/model/model.dart @@ -526,7 +526,9 @@ class StreamColorSwatch extends ColorSwatch<_StreamColorVariant> { } enum _StreamColorVariant { + /// The [Subscription.color] int that the swatch is based on. base, + unreadCountBadgeBackground, /// The stream icon on a plain-colored surface, such as white. From 7a2970e087d60e862e5a505340b552567363a4db Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 3 May 2024 13:02:47 -0700 Subject: [PATCH 3/4] model [nfc]: Remove StreamColorSwatch; use ColorSwatch This indirection was mildly helpful; it let us consume these colors with slightly more concise code, and without needing `!` to satisfy the analyzer. But we get the same correct functionality without the indirection, and a bit more transparently. The more substantive benefit of using ColorSwatch directly is that we now have a ready-made lerp function -- `ColorSwatch.lerp` -- that can operate on our swatch instances. That should be helpful later when we want stream colors to animate smoothly when dark mode is toggled, along with various other style attributes that will naturally animate too. --- lib/api/model/model.dart | 121 ++++++++++------------ lib/widgets/inbox.dart | 10 +- lib/widgets/message_list.dart | 9 +- lib/widgets/subscription_list.dart | 2 +- lib/widgets/unread_count_badge.dart | 8 +- test/api/model/model_checks.dart | 10 -- test/api/model/model_test.dart | 24 +++-- test/flutter_checks.dart | 4 + test/widgets/inbox_test.dart | 6 +- test/widgets/message_list_test.dart | 4 +- test/widgets/subscription_list_test.dart | 2 +- test/widgets/unread_count_badge_test.dart | 4 +- 12 files changed, 101 insertions(+), 103 deletions(-) diff --git a/lib/api/model/model.dart b/lib/api/model/model.dart index 6b249196ee..421d6f1704 100644 --- a/lib/api/model/model.dart +++ b/lib/api/model/model.dart @@ -416,16 +416,17 @@ class Subscription extends ZulipStream { return 0xff000000 | int.parse(str.substring(1), radix: 16); } - StreamColorSwatch? _swatch; - /// A [StreamColorSwatch] for the subscription, memoized. + ColorSwatch? _swatch; + /// A [ColorSwatch] for the subscription, memoized. // TODO I'm not sure this is the right home for this; it seems like we might // instead have chosen to put it in more UI-centered code, like in a custom // material [ColorScheme] class or something. But it works for now. - StreamColorSwatch colorSwatch() => _swatch ??= StreamColorSwatch(color); + ColorSwatch colorSwatch() => + _swatch ??= streamColorSwatch(color); @visibleForTesting @JsonKey(includeToJson: false) - StreamColorSwatch? get debugCachedSwatchValue => _swatch; + ColorSwatch? get debugCachedSwatchValue => _swatch; Subscription({ required super.streamId, @@ -462,70 +463,58 @@ class Subscription extends ZulipStream { /// /// Use this in UI code for colors related to [Subscription.color], /// such as the background of an unread count badge. -class StreamColorSwatch extends ColorSwatch<_StreamColorVariant> { - StreamColorSwatch(int base) : super(base, _compute(base)); - - Color get base => this[_StreamColorVariant.base]!; - - Color get unreadCountBadgeBackground => this[_StreamColorVariant.unreadCountBadgeBackground]!; - - Color get iconOnPlainBackground => this[_StreamColorVariant.iconOnPlainBackground]!; - - Color get iconOnBarBackground => this[_StreamColorVariant.iconOnBarBackground]!; - - Color get barBackground => this[_StreamColorVariant.barBackground]!; - - static Map<_StreamColorVariant, Color> _compute(int base) { - final baseAsColor = Color(base); - - final clamped20to75 = clampLchLightness(baseAsColor, 20, 75); - final clamped20to75AsHsl = HSLColor.fromColor(clamped20to75); - - return { - _StreamColorVariant.base: baseAsColor, - - // Follows `.unread-count` in Vlad's replit: - // - // - // - // TODO fix bug where our results differ from the replit's (see unit tests) - _StreamColorVariant.unreadCountBadgeBackground: - clampLchLightness(baseAsColor, 30, 70) - .withOpacity(0.3), - - // Follows `.sidebar-row__icon` in Vlad's replit: - // - // - // TODO fix bug where our results differ from the replit's (see unit tests) - _StreamColorVariant.iconOnPlainBackground: clamped20to75, - - // Follows `.recepeient__icon` in Vlad's replit: - // - // - // - // TODO fix bug where our results differ from the replit's (see unit tests) - _StreamColorVariant.iconOnBarBackground: - clamped20to75AsHsl - .withLightness(clamped20to75AsHsl.lightness - 0.12) - .toColor(), - - // Follows `.recepient` in Vlad's replit: - // - // - // TODO I think [LabColor.interpolate] doesn't actually do LAB mixing; - // it just calls up to the superclass method [ColorModel.interpolate]: - // - // which does ordinary RGB mixing. Investigate and send a PR? - // TODO fix bug where our results differ from the replit's (see unit tests) - _StreamColorVariant.barBackground: - LabColor.fromColor(const Color(0xfff9f9f9)) - .interpolate(LabColor.fromColor(clamped20to75), 0.22) - .toColor(), - }; - } +ColorSwatch streamColorSwatch(int base) { + final baseAsColor = Color(base); + + final clamped20to75 = clampLchLightness(baseAsColor, 20, 75); + final clamped20to75AsHsl = HSLColor.fromColor(clamped20to75); + + final map = { + StreamColorVariant.base: baseAsColor, + + // Follows `.unread-count` in Vlad's replit: + // + // + // + // TODO fix bug where our results differ from the replit's (see unit tests) + StreamColorVariant.unreadCountBadgeBackground: + clampLchLightness(baseAsColor, 30, 70) + .withOpacity(0.3), + + // Follows `.sidebar-row__icon` in Vlad's replit: + // + // + // TODO fix bug where our results differ from the replit's (see unit tests) + StreamColorVariant.iconOnPlainBackground: clamped20to75, + + // Follows `.recepeient__icon` in Vlad's replit: + // + // + // + // TODO fix bug where our results differ from the replit's (see unit tests) + StreamColorVariant.iconOnBarBackground: + clamped20to75AsHsl + .withLightness(clamped20to75AsHsl.lightness - 0.12) + .toColor(), + + // Follows `.recepient` in Vlad's replit: + // + // + // TODO I think [LabColor.interpolate] doesn't actually do LAB mixing; + // it just calls up to the superclass method [ColorModel.interpolate]: + // + // which does ordinary RGB mixing. Investigate and send a PR? + // TODO fix bug where our results differ from the replit's (see unit tests) + StreamColorVariant.barBackground: + LabColor.fromColor(const Color(0xfff9f9f9)) + .interpolate(LabColor.fromColor(clamped20to75), 0.22) + .toColor(), + }; + + return ColorSwatch(base, map); } -enum _StreamColorVariant { +enum StreamColorVariant { /// The [Subscription.color] int that the swatch is based on. base, diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index c2e0d776b0..9e70f0220e 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -410,12 +410,14 @@ class _StreamHeaderItem extends _HeaderItem { @override get title => subscription.name; @override get icon => iconDataForStream(subscription); - @override get collapsedIconColor => subscription.colorSwatch().iconOnPlainBackground; - @override get uncollapsedIconColor => subscription.colorSwatch().iconOnBarBackground; + @override get collapsedIconColor => + subscription.colorSwatch()[StreamColorVariant.iconOnPlainBackground]!; + @override get uncollapsedIconColor => + subscription.colorSwatch()[StreamColorVariant.iconOnBarBackground]!; @override get uncollapsedBackgroundColor => - subscription.colorSwatch().barBackground; + subscription.colorSwatch()[StreamColorVariant.barBackground]!; @override get unreadCountBadgeBackgroundColor => - subscription.colorSwatch().unreadCountBadgeBackground; + subscription.colorSwatch()[StreamColorVariant.unreadCountBadgeBackground]!; @override get onCollapseButtonTap => () async { await super.onCollapseButtonTap(); diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index f3c8b63ba0..c43d42c577 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -66,7 +66,8 @@ class _MessageListPageState extends State { case StreamNarrow(:final streamId): case TopicNarrow(:final streamId): - backgroundColor = store.subscriptions[streamId]?.colorSwatch().barBackground + backgroundColor = + store.subscriptions[streamId]?.colorSwatch()[StreamColorVariant.barBackground]! ?? _kUnsubscribedStreamRecipientHeaderColor; // All recipient headers will match this color; remove distracting line // (but are recipient headers even needed for topic narrows?) @@ -655,12 +656,12 @@ class StreamMessageRecipientHeader extends StatelessWidget { final Color iconColor; if (subscription != null) { final swatch = subscription.colorSwatch(); - backgroundColor = swatch.barBackground; + backgroundColor = swatch[StreamColorVariant.barBackground]!; contrastingColor = - (ThemeData.estimateBrightnessForColor(swatch.barBackground) == Brightness.dark) + (ThemeData.estimateBrightnessForColor(backgroundColor) == Brightness.dark) ? Colors.white : Colors.black; - iconColor = swatch.iconOnBarBackground; + iconColor = swatch[StreamColorVariant.iconOnBarBackground]!; } else { backgroundColor = _kUnsubscribedStreamRecipientHeaderColor; contrastingColor = Colors.black; diff --git a/lib/widgets/subscription_list.dart b/lib/widgets/subscription_list.dart index df6d3d1e68..32a41404a2 100644 --- a/lib/widgets/subscription_list.dart +++ b/lib/widgets/subscription_list.dart @@ -208,7 +208,7 @@ class SubscriptionItem extends StatelessWidget { const SizedBox(width: 16), Padding( padding: const EdgeInsets.symmetric(vertical: 11), - child: Icon(size: 18, color: swatch.iconOnPlainBackground, + child: Icon(size: 18, color: swatch[StreamColorVariant.iconOnPlainBackground]!, iconDataForStream(subscription))), const SizedBox(width: 5), Expanded( diff --git a/lib/widgets/unread_count_badge.dart b/lib/widgets/unread_count_badge.dart index 45df67e80d..116d5d285f 100644 --- a/lib/widgets/unread_count_badge.dart +++ b/lib/widgets/unread_count_badge.dart @@ -21,16 +21,18 @@ class UnreadCountBadge extends StatelessWidget { /// The badge's background color. /// - /// Pass a [StreamColorSwatch] if this badge represents messages in one - /// specific stream. The appropriate color from the swatch will be used. + /// Pass a [ColorSwatch] if this badge represents messages + /// in one specific stream. The appropriate color from the swatch will be used. /// /// If null, the default neutral background will be used. final Color? backgroundColor; @override Widget build(BuildContext context) { + final backgroundColor = this.backgroundColor; final effectiveBackgroundColor = switch (backgroundColor) { - StreamColorSwatch(unreadCountBadgeBackground: var color) => color, + ColorSwatch() => + backgroundColor[StreamColorVariant.unreadCountBadgeBackground]!, Color() => backgroundColor, null => const Color.fromRGBO(102, 102, 153, 0.15), }; diff --git a/test/api/model/model_checks.dart b/test/api/model/model_checks.dart index 801320cb57..f0c3a87373 100644 --- a/test/api/model/model_checks.dart +++ b/test/api/model/model_checks.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:checks/checks.dart'; import 'package:zulip/api/model/model.dart'; @@ -29,14 +27,6 @@ extension ZulipStreamChecks on Subject { Subject get canRemoveSubscribersGroup => has((e) => e.canRemoveSubscribersGroup, 'canRemoveSubscribersGroup'); } -extension StreamColorSwatchChecks on Subject { - Subject get base => has((s) => s.base, 'base'); - Subject get unreadCountBadgeBackground => has((s) => s.unreadCountBadgeBackground, 'unreadCountBadgeBackground'); - Subject get iconOnPlainBackground => has((s) => s.iconOnPlainBackground, 'iconOnPlainBackground'); - Subject get iconOnBarBackground => has((s) => s.iconOnBarBackground, 'iconOnBarBackground'); - Subject get barBackground => has((s) => s.barBackground, 'barBackground'); -} - extension MessageChecks on Subject { Subject get id => has((e) => e.id, 'id'); Subject get content => has((e) => e.content, 'content'); diff --git a/test/api/model/model_test.dart b/test/api/model/model_test.dart index ca854743ff..93d28f045f 100644 --- a/test/api/model/model_test.dart +++ b/test/api/model/model_test.dart @@ -5,6 +5,7 @@ import 'package:checks/checks.dart'; import 'package:test/scaffolding.dart'; import 'package:zulip/api/model/model.dart'; +import '../../flutter_checks.dart'; import '../../example_data.dart' as eg; import '../../stdlib_checks.dart'; import 'model_checks.dart'; @@ -120,21 +121,25 @@ void main() { final sub = eg.subscription(eg.stream(), color: 0xffffffff); check(sub.debugCachedSwatchValue).isNull(); sub.colorSwatch(); - check(sub.debugCachedSwatchValue).isNotNull().base.equals(const Color(0xffffffff)); + check(sub.debugCachedSwatchValue).isNotNull() + [StreamColorVariant.base].equals(const Color(0xffffffff)); sub.color = 0xffff0000; check(sub.debugCachedSwatchValue).isNull(); sub.colorSwatch(); - check(sub.debugCachedSwatchValue).isNotNull().base.equals(const Color(0xffff0000)); + check(sub.debugCachedSwatchValue).isNotNull() + [StreamColorVariant.base].equals(const Color(0xffff0000)); }); - group('StreamColorSwatch', () { + group('streamColorSwatch', () { test('base', () { - check(StreamColorSwatch(0xffffffff)).base.equals(const Color(0xffffffff)); + check(streamColorSwatch(0xffffffff))[StreamColorVariant.base] + .equals(const Color(0xffffffff)); }); test('unreadCountBadgeBackground', () { void runCheck(int base, Color expected) { - check(StreamColorSwatch(base)).unreadCountBadgeBackground.equals(expected); + check(streamColorSwatch(base)) + [StreamColorVariant.unreadCountBadgeBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS and EXTREME_COLORS @@ -196,7 +201,8 @@ void main() { test('iconOnPlainBackground', () { void runCheck(int base, Color expected) { - check(StreamColorSwatch(base)).iconOnPlainBackground.equals(expected); + check(streamColorSwatch(base)) + [StreamColorVariant.iconOnPlainBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS @@ -237,7 +243,8 @@ void main() { test('iconOnBarBackground', () { void runCheck(int base, Color expected) { - check(StreamColorSwatch(base)).iconOnBarBackground.equals(expected); + check(streamColorSwatch(base)) + [StreamColorVariant.iconOnBarBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS @@ -278,7 +285,8 @@ void main() { test('barBackground', () { void runCheck(int base, Color expected) { - check(StreamColorSwatch(base)).barBackground.equals(expected); + check(streamColorSwatch(base)) + [StreamColorVariant.barBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS diff --git a/test/flutter_checks.dart b/test/flutter_checks.dart index 20a38225dc..6c9a54b64c 100644 --- a/test/flutter_checks.dart +++ b/test/flutter_checks.dart @@ -5,6 +5,10 @@ import 'package:checks/checks.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +extension ColorSwatchChecks on Subject> { + Subject operator [](T index) => has((x) => x[index], '[$index]'); +} + extension RectChecks on Subject { Subject get top => has((d) => d.top, 'top'); Subject get bottom => has((d) => d.bottom, 'bottom'); diff --git a/test/widgets/inbox_test.dart b/test/widgets/inbox_test.dart index 1f70671f19..113ad3d928 100644 --- a/test/widgets/inbox_test.dart +++ b/test/widgets/inbox_test.dart @@ -411,7 +411,8 @@ void main() { final collapseIcon = findHeaderCollapseIcon(tester, headerRow!); check(collapseIcon).icon.equals(ZulipIcons.arrow_down); final streamIcon = findStreamHeaderIcon(tester, streamId); - check(streamIcon).color.equals(subscription.colorSwatch().iconOnBarBackground); + check(streamIcon).color + .equals(subscription.colorSwatch()[StreamColorVariant.iconOnBarBackground]!); // TODO check bar background color check(tester.widgetList(findSectionContent)).isNotEmpty(); } @@ -432,7 +433,8 @@ void main() { final collapseIcon = findHeaderCollapseIcon(tester, headerRow!); check(collapseIcon).icon.equals(ZulipIcons.arrow_right); final streamIcon = findStreamHeaderIcon(tester, streamId); - check(streamIcon).color.equals(subscription.colorSwatch().iconOnPlainBackground); + check(streamIcon).color + .equals(subscription.colorSwatch()[StreamColorVariant.iconOnPlainBackground]!); // TODO check bar background color check(tester.widgetList(findSectionContent)).isEmpty(); } diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 1c0ea226d6..b6fa70b090 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -282,7 +282,7 @@ void main() { find.descendant( of: find.byType(StreamMessageRecipientHeader), matching: find.byType(ColoredBox), - ))).color.equals(swatch.barBackground); + ))).color.equals(swatch[StreamColorVariant.barBackground]!); }); testWidgets('color of stream icon', (tester) async { @@ -294,7 +294,7 @@ void main() { subscriptions: [subscription]); await tester.pump(); check(tester.widget(find.byIcon(ZulipIcons.globe))) - .color.equals(swatch.iconOnBarBackground); + .color.equals(swatch[StreamColorVariant.iconOnBarBackground]!); }); testWidgets('normal streams show hash icon', (tester) async { diff --git a/test/widgets/subscription_list_test.dart b/test/widgets/subscription_list_test.dart index 92530884f5..88cfd6c7cd 100644 --- a/test/widgets/subscription_list_test.dart +++ b/test/widgets/subscription_list_test.dart @@ -186,7 +186,7 @@ void main() { ], unreadMsgs: unreadMsgs); check(getItemCount()).equals(1); check(tester.widget(find.byType(Icon)).color) - .equals(swatch.iconOnPlainBackground); + .equals(swatch[StreamColorVariant.iconOnPlainBackground]!); check(tester.widget(find.byType(UnreadCountBadge)).backgroundColor) .equals(swatch); }); diff --git a/test/widgets/unread_count_badge_test.dart b/test/widgets/unread_count_badge_test.dart index 848372b976..62d31ec809 100644 --- a/test/widgets/unread_count_badge_test.dart +++ b/test/widgets/unread_count_badge_test.dart @@ -38,9 +38,9 @@ void main() { }); testWidgets('stream color', (WidgetTester tester) async { - final swatch = StreamColorSwatch(0xff76ce90); + final swatch = streamColorSwatch(0xff76ce90); await prepare(tester, swatch); - check(findBackgroundColor(tester)).equals(swatch.unreadCountBadgeBackground); + check(findBackgroundColor(tester)).equals(swatch[StreamColorVariant.unreadCountBadgeBackground]!); }); }); }); From 689ac3715b75e22b56dea892a5afd5b2fe6a1c51 Mon Sep 17 00:00:00 2001 From: Chris Bobbe Date: Fri, 3 May 2024 13:57:04 -0700 Subject: [PATCH 4/4] model [nfc]: Shorten `StreamColorVariant` to `StreamColor` The old name seems longer than it needs to be. --- lib/api/model/model.dart | 22 +++++++++++----------- lib/widgets/inbox.dart | 8 ++++---- lib/widgets/message_list.dart | 6 +++--- lib/widgets/subscription_list.dart | 2 +- lib/widgets/unread_count_badge.dart | 4 ++-- test/api/model/model_test.dart | 14 +++++++------- test/widgets/inbox_test.dart | 4 ++-- test/widgets/message_list_test.dart | 4 ++-- test/widgets/subscription_list_test.dart | 2 +- test/widgets/unread_count_badge_test.dart | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/api/model/model.dart b/lib/api/model/model.dart index 421d6f1704..d686249277 100644 --- a/lib/api/model/model.dart +++ b/lib/api/model/model.dart @@ -416,17 +416,17 @@ class Subscription extends ZulipStream { return 0xff000000 | int.parse(str.substring(1), radix: 16); } - ColorSwatch? _swatch; + ColorSwatch? _swatch; /// A [ColorSwatch] for the subscription, memoized. // TODO I'm not sure this is the right home for this; it seems like we might // instead have chosen to put it in more UI-centered code, like in a custom // material [ColorScheme] class or something. But it works for now. - ColorSwatch colorSwatch() => + ColorSwatch colorSwatch() => _swatch ??= streamColorSwatch(color); @visibleForTesting @JsonKey(includeToJson: false) - ColorSwatch? get debugCachedSwatchValue => _swatch; + ColorSwatch? get debugCachedSwatchValue => _swatch; Subscription({ required super.streamId, @@ -463,21 +463,21 @@ class Subscription extends ZulipStream { /// /// Use this in UI code for colors related to [Subscription.color], /// such as the background of an unread count badge. -ColorSwatch streamColorSwatch(int base) { +ColorSwatch streamColorSwatch(int base) { final baseAsColor = Color(base); final clamped20to75 = clampLchLightness(baseAsColor, 20, 75); final clamped20to75AsHsl = HSLColor.fromColor(clamped20to75); final map = { - StreamColorVariant.base: baseAsColor, + StreamColor.base: baseAsColor, // Follows `.unread-count` in Vlad's replit: // // // // TODO fix bug where our results differ from the replit's (see unit tests) - StreamColorVariant.unreadCountBadgeBackground: + StreamColor.unreadCountBadgeBackground: clampLchLightness(baseAsColor, 30, 70) .withOpacity(0.3), @@ -485,14 +485,14 @@ ColorSwatch streamColorSwatch(int base) { // // // TODO fix bug where our results differ from the replit's (see unit tests) - StreamColorVariant.iconOnPlainBackground: clamped20to75, + StreamColor.iconOnPlainBackground: clamped20to75, // Follows `.recepeient__icon` in Vlad's replit: // // // // TODO fix bug where our results differ from the replit's (see unit tests) - StreamColorVariant.iconOnBarBackground: + StreamColor.iconOnBarBackground: clamped20to75AsHsl .withLightness(clamped20to75AsHsl.lightness - 0.12) .toColor(), @@ -505,16 +505,16 @@ ColorSwatch streamColorSwatch(int base) { // // which does ordinary RGB mixing. Investigate and send a PR? // TODO fix bug where our results differ from the replit's (see unit tests) - StreamColorVariant.barBackground: + StreamColor.barBackground: LabColor.fromColor(const Color(0xfff9f9f9)) .interpolate(LabColor.fromColor(clamped20to75), 0.22) .toColor(), }; - return ColorSwatch(base, map); + return ColorSwatch(base, map); } -enum StreamColorVariant { +enum StreamColor { /// The [Subscription.color] int that the swatch is based on. base, diff --git a/lib/widgets/inbox.dart b/lib/widgets/inbox.dart index 9e70f0220e..5dd16ea942 100644 --- a/lib/widgets/inbox.dart +++ b/lib/widgets/inbox.dart @@ -411,13 +411,13 @@ class _StreamHeaderItem extends _HeaderItem { @override get title => subscription.name; @override get icon => iconDataForStream(subscription); @override get collapsedIconColor => - subscription.colorSwatch()[StreamColorVariant.iconOnPlainBackground]!; + subscription.colorSwatch()[StreamColor.iconOnPlainBackground]!; @override get uncollapsedIconColor => - subscription.colorSwatch()[StreamColorVariant.iconOnBarBackground]!; + subscription.colorSwatch()[StreamColor.iconOnBarBackground]!; @override get uncollapsedBackgroundColor => - subscription.colorSwatch()[StreamColorVariant.barBackground]!; + subscription.colorSwatch()[StreamColor.barBackground]!; @override get unreadCountBadgeBackgroundColor => - subscription.colorSwatch()[StreamColorVariant.unreadCountBadgeBackground]!; + subscription.colorSwatch()[StreamColor.unreadCountBadgeBackground]!; @override get onCollapseButtonTap => () async { await super.onCollapseButtonTap(); diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index c43d42c577..23f0de6798 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -67,7 +67,7 @@ class _MessageListPageState extends State { case StreamNarrow(:final streamId): case TopicNarrow(:final streamId): backgroundColor = - store.subscriptions[streamId]?.colorSwatch()[StreamColorVariant.barBackground]! + store.subscriptions[streamId]?.colorSwatch()[StreamColor.barBackground]! ?? _kUnsubscribedStreamRecipientHeaderColor; // All recipient headers will match this color; remove distracting line // (but are recipient headers even needed for topic narrows?) @@ -656,12 +656,12 @@ class StreamMessageRecipientHeader extends StatelessWidget { final Color iconColor; if (subscription != null) { final swatch = subscription.colorSwatch(); - backgroundColor = swatch[StreamColorVariant.barBackground]!; + backgroundColor = swatch[StreamColor.barBackground]!; contrastingColor = (ThemeData.estimateBrightnessForColor(backgroundColor) == Brightness.dark) ? Colors.white : Colors.black; - iconColor = swatch[StreamColorVariant.iconOnBarBackground]!; + iconColor = swatch[StreamColor.iconOnBarBackground]!; } else { backgroundColor = _kUnsubscribedStreamRecipientHeaderColor; contrastingColor = Colors.black; diff --git a/lib/widgets/subscription_list.dart b/lib/widgets/subscription_list.dart index 32a41404a2..67db4316d6 100644 --- a/lib/widgets/subscription_list.dart +++ b/lib/widgets/subscription_list.dart @@ -208,7 +208,7 @@ class SubscriptionItem extends StatelessWidget { const SizedBox(width: 16), Padding( padding: const EdgeInsets.symmetric(vertical: 11), - child: Icon(size: 18, color: swatch[StreamColorVariant.iconOnPlainBackground]!, + child: Icon(size: 18, color: swatch[StreamColor.iconOnPlainBackground]!, iconDataForStream(subscription))), const SizedBox(width: 5), Expanded( diff --git a/lib/widgets/unread_count_badge.dart b/lib/widgets/unread_count_badge.dart index 116d5d285f..f98cbd67a6 100644 --- a/lib/widgets/unread_count_badge.dart +++ b/lib/widgets/unread_count_badge.dart @@ -31,8 +31,8 @@ class UnreadCountBadge extends StatelessWidget { Widget build(BuildContext context) { final backgroundColor = this.backgroundColor; final effectiveBackgroundColor = switch (backgroundColor) { - ColorSwatch() => - backgroundColor[StreamColorVariant.unreadCountBadgeBackground]!, + ColorSwatch() => + backgroundColor[StreamColor.unreadCountBadgeBackground]!, Color() => backgroundColor, null => const Color.fromRGBO(102, 102, 153, 0.15), }; diff --git a/test/api/model/model_test.dart b/test/api/model/model_test.dart index 93d28f045f..afa8a4b541 100644 --- a/test/api/model/model_test.dart +++ b/test/api/model/model_test.dart @@ -122,24 +122,24 @@ void main() { check(sub.debugCachedSwatchValue).isNull(); sub.colorSwatch(); check(sub.debugCachedSwatchValue).isNotNull() - [StreamColorVariant.base].equals(const Color(0xffffffff)); + [StreamColor.base].equals(const Color(0xffffffff)); sub.color = 0xffff0000; check(sub.debugCachedSwatchValue).isNull(); sub.colorSwatch(); check(sub.debugCachedSwatchValue).isNotNull() - [StreamColorVariant.base].equals(const Color(0xffff0000)); + [StreamColor.base].equals(const Color(0xffff0000)); }); group('streamColorSwatch', () { test('base', () { - check(streamColorSwatch(0xffffffff))[StreamColorVariant.base] + check(streamColorSwatch(0xffffffff))[StreamColor.base] .equals(const Color(0xffffffff)); }); test('unreadCountBadgeBackground', () { void runCheck(int base, Color expected) { check(streamColorSwatch(base)) - [StreamColorVariant.unreadCountBadgeBackground].equals(expected); + [StreamColor.unreadCountBadgeBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS and EXTREME_COLORS @@ -202,7 +202,7 @@ void main() { test('iconOnPlainBackground', () { void runCheck(int base, Color expected) { check(streamColorSwatch(base)) - [StreamColorVariant.iconOnPlainBackground].equals(expected); + [StreamColor.iconOnPlainBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS @@ -244,7 +244,7 @@ void main() { test('iconOnBarBackground', () { void runCheck(int base, Color expected) { check(streamColorSwatch(base)) - [StreamColorVariant.iconOnBarBackground].equals(expected); + [StreamColor.iconOnBarBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS @@ -286,7 +286,7 @@ void main() { test('barBackground', () { void runCheck(int base, Color expected) { check(streamColorSwatch(base)) - [StreamColorVariant.barBackground].equals(expected); + [StreamColor.barBackground].equals(expected); } // Check against everything in ZULIP_ASSIGNMENT_COLORS diff --git a/test/widgets/inbox_test.dart b/test/widgets/inbox_test.dart index 113ad3d928..f3b9eb9a9e 100644 --- a/test/widgets/inbox_test.dart +++ b/test/widgets/inbox_test.dart @@ -412,7 +412,7 @@ void main() { check(collapseIcon).icon.equals(ZulipIcons.arrow_down); final streamIcon = findStreamHeaderIcon(tester, streamId); check(streamIcon).color - .equals(subscription.colorSwatch()[StreamColorVariant.iconOnBarBackground]!); + .equals(subscription.colorSwatch()[StreamColor.iconOnBarBackground]!); // TODO check bar background color check(tester.widgetList(findSectionContent)).isNotEmpty(); } @@ -434,7 +434,7 @@ void main() { check(collapseIcon).icon.equals(ZulipIcons.arrow_right); final streamIcon = findStreamHeaderIcon(tester, streamId); check(streamIcon).color - .equals(subscription.colorSwatch()[StreamColorVariant.iconOnPlainBackground]!); + .equals(subscription.colorSwatch()[StreamColor.iconOnPlainBackground]!); // TODO check bar background color check(tester.widgetList(findSectionContent)).isEmpty(); } diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index b6fa70b090..4102bf6d7f 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -282,7 +282,7 @@ void main() { find.descendant( of: find.byType(StreamMessageRecipientHeader), matching: find.byType(ColoredBox), - ))).color.equals(swatch[StreamColorVariant.barBackground]!); + ))).color.equals(swatch[StreamColor.barBackground]!); }); testWidgets('color of stream icon', (tester) async { @@ -294,7 +294,7 @@ void main() { subscriptions: [subscription]); await tester.pump(); check(tester.widget(find.byIcon(ZulipIcons.globe))) - .color.equals(swatch[StreamColorVariant.iconOnBarBackground]!); + .color.equals(swatch[StreamColor.iconOnBarBackground]!); }); testWidgets('normal streams show hash icon', (tester) async { diff --git a/test/widgets/subscription_list_test.dart b/test/widgets/subscription_list_test.dart index 88cfd6c7cd..80503956bf 100644 --- a/test/widgets/subscription_list_test.dart +++ b/test/widgets/subscription_list_test.dart @@ -186,7 +186,7 @@ void main() { ], unreadMsgs: unreadMsgs); check(getItemCount()).equals(1); check(tester.widget(find.byType(Icon)).color) - .equals(swatch[StreamColorVariant.iconOnPlainBackground]!); + .equals(swatch[StreamColor.iconOnPlainBackground]!); check(tester.widget(find.byType(UnreadCountBadge)).backgroundColor) .equals(swatch); }); diff --git a/test/widgets/unread_count_badge_test.dart b/test/widgets/unread_count_badge_test.dart index 62d31ec809..bd76dacd4a 100644 --- a/test/widgets/unread_count_badge_test.dart +++ b/test/widgets/unread_count_badge_test.dart @@ -40,7 +40,7 @@ void main() { testWidgets('stream color', (WidgetTester tester) async { final swatch = streamColorSwatch(0xff76ce90); await prepare(tester, swatch); - check(findBackgroundColor(tester)).equals(swatch[StreamColorVariant.unreadCountBadgeBackground]!); + check(findBackgroundColor(tester)).equals(swatch[StreamColor.unreadCountBadgeBackground]!); }); }); });