Skip to content

Commit

Permalink
model: Implement StreamColorSwatch.dark
Browse files Browse the repository at this point in the history
We added the light-mode color computations in zulip#381.

We're not ready to use this yet, but the color computations are
unlikely to change before that time comes, and finding the right
ones is a chore that's good to get done.

Related: zulip#95
  • Loading branch information
chrisbobbe committed May 9, 2024
1 parent 9c12413 commit 377f7d1
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 2 deletions.
43 changes: 41 additions & 2 deletions lib/api/model/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,8 @@ 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) : this._(base, _compute(base));
StreamColorSwatch(int base) : this._(base, _computeLight(base));
StreamColorSwatch.dark(int base) : this._(base, _computeDark(base));

const StreamColorSwatch._(int base, this._swatch) : super(base, _swatch);

Expand Down Expand Up @@ -498,7 +499,7 @@ class StreamColorSwatch extends ColorSwatch<StreamColorVariant> {
/// 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) {
static Map<StreamColorVariant, Color> _computeLight(int base) {
final baseAsColor = Color(base);

final clamped20to75 = clampLchLightness(baseAsColor, 20, 75);
Expand Down Expand Up @@ -547,6 +548,44 @@ class StreamColorSwatch extends ColorSwatch<StreamColorVariant> {
};
}

static Map<StreamColorVariant, Color> _computeDark(int base) {
final baseAsColor = Color(base);

final clamped20to75 = clampLchLightness(baseAsColor, 20, 75);

return {
// See comments in [_computeLight] about what these computations are based
// on, and how the resulting values are a little off sometimes. The
// comments mostly apply here too.

StreamColorVariant.base: baseAsColor,
StreamColorVariant.unreadCountBadgeBackground:
clampLchLightness(baseAsColor, 30, 70)
.withOpacity(0.3),
StreamColorVariant.iconOnPlainBackground: clamped20to75,

// Follows the web app (as of zulip/zulip@db03369ac); see
// get_stream_privacy_icon_color in web/src/stream_color.ts.
//
// `.recepeient__icon` in Vlad's replit gives something different so we
// don't use that:
// <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>
// <https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/design.3A.20.23F117.20.22Inbox.22.20screen/near/1624484>
// But that's OK because Vlad said "I feel like current dark theme contrast
// is fine", and when he said that, this had been the web app's icon color
// for 6+ months (since zulip/zulip@023584e04):
// https://chat.zulip.org/#narrow/stream/101-design/topic/UI.20redesign.3A.20recipient.20bar.20colors/near/1675786
//
// TODO fix bug where our results are unexpected (see unit tests)
StreamColorVariant.iconOnBarBackground: clamped20to75,

StreamColorVariant.barBackground:
LabColor.fromColor(const Color(0xff000000))
.interpolate(LabColor.fromColor(clamped20to75), 0.38)
.toColor(),
};
}

/// Copied from [ColorSwatch.lerp].
static StreamColorSwatch? lerp(StreamColorSwatch? a, StreamColorSwatch? b, double t) {
if (identical(a, b)) {
Expand Down
196 changes: 196 additions & 0 deletions test/api/model/model_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,202 @@ void main() {
});
});

group('dark', () {
test('base', () {
check(StreamColorSwatch.dark(0xffffffff))
.base.equals(const Color(0xffffffff));
});

test('unreadCountBadgeBackground', () {
void runCheck(int base, Color expected) {
check(StreamColorSwatch.dark(base))
.unreadCountBadgeBackground.equals(expected);
}

// Check against everything in ZULIP_ASSIGNMENT_COLORS and EXTREME_COLORS
// in <https://replit.com/@VladKorobov/zulip-sidebar#script.js>.
// On how to extract expected results from the replit, see:
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972

// TODO Fix bug causing our implementation's results to differ from the
// replit's. Where they differ, see comment with what the replit gives.

// ZULIP_ASSIGNMENT_COLORS
runCheck(0xff76ce90, const Color(0x4d65bd80));
runCheck(0xfffae589, const Color(0x4dbdab53)); // 0x4dbdaa52
runCheck(0xffa6c7e5, const Color(0x4d8eafcc)); // 0x4d8fb0cd
runCheck(0xffe79ab5, const Color(0x4de295b0)); // 0x4de194af
runCheck(0xffbfd56f, const Color(0x4d9eb551)); // 0x4d9eb450
runCheck(0xfff4ae55, const Color(0x4de19d45)); // 0x4de09c44
runCheck(0xffb0a5fd, const Color(0x4daba0f8)); // 0x4daca2f9
runCheck(0xffaddfe5, const Color(0x4d83b4b9)); // 0x4d83b4ba
runCheck(0xfff5ce6e, const Color(0x4dcba749)); // 0x4dcaa648
runCheck(0xffc2726a, const Color(0x4dc2726a));
runCheck(0xff94c849, const Color(0x4d86ba3c)); // 0x4d86ba3b
runCheck(0xffbd86e5, const Color(0x4dbd86e5));
runCheck(0xffee7e4a, const Color(0x4dee7e4a));
runCheck(0xffa6dcbf, const Color(0x4d82b69b)); // 0x4d82b79b
runCheck(0xff95a5fd, const Color(0x4d95a5fd));
runCheck(0xff53a063, const Color(0x4d53a063));
runCheck(0xff9987e1, const Color(0x4d9987e1));
runCheck(0xffe4523d, const Color(0x4de4523d));
runCheck(0xffc2c2c2, const Color(0x4dababab));
runCheck(0xff4f8de4, const Color(0x4d4f8de4));
runCheck(0xffc6a8ad, const Color(0x4dc2a4a9)); // 0x4dc1a4a9
runCheck(0xffe7cc4d, const Color(0x4dc3ab2a)); // 0x4dc2aa28
runCheck(0xffc8bebf, const Color(0x4db3a9aa));
runCheck(0xffa47462, const Color(0x4da47462));

// EXTREME_COLORS
runCheck(0xFFFFFFFF, const Color(0x4dababab));
runCheck(0xFF000000, const Color(0x4d474747));
runCheck(0xFFD3D3D3, const Color(0x4dababab));
runCheck(0xFFA9A9A9, const Color(0x4da9a9a9));
runCheck(0xFF808080, const Color(0x4d808080));
runCheck(0xFFFFFF00, const Color(0x4dacb300)); // 0x4dacb200
runCheck(0xFFFF0000, const Color(0x4dff0000));
runCheck(0xFF008000, const Color(0x4d008000));
runCheck(0xFF0000FF, const Color(0x4d0000ff)); // 0x4d0902ff
runCheck(0xFFEE82EE, const Color(0x4dee82ee));
runCheck(0xFFFFA500, const Color(0x4def9800)); // 0x4ded9600
runCheck(0xFF800080, const Color(0x4d810181)); // 0x4d810281
runCheck(0xFF00FFFF, const Color(0x4d00c2c3)); // 0x4d00c3c5
runCheck(0xFFFF00FF, const Color(0x4dff00ff));
runCheck(0xFF00FF00, const Color(0x4d00cb00));
runCheck(0xFF800000, const Color(0x4d8d140c)); // 0x4d8b130b
runCheck(0xFF008080, const Color(0x4d008080));
runCheck(0xFF000080, const Color(0x4d492bae)); // 0x4d4b2eb3
runCheck(0xFFFFFFE0, const Color(0x4dadad90)); // 0x4dacad90
runCheck(0xFFFF69B4, const Color(0x4dff69b4));
});

test('iconOnPlainBackground', () {
void runCheck(int base, Color expected) {
check(StreamColorSwatch.dark(base))
.iconOnPlainBackground.equals(expected);
}

// Check against everything in ZULIP_ASSIGNMENT_COLORS
// in <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>.
// (Skipping `streamColors` because there are 100+ of them.)
// On how to extract expected results from the replit, see:
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972

// TODO Fix bug causing our implementation's results to differ from the
// replit's. Where they differ, see comment with what the replit gives.

runCheck(0xff76ce90, const Color(0xff73cb8d));
runCheck(0xfffae589, const Color(0xffccb95f)); // 0xffcbb85e
runCheck(0xffa6c7e5, const Color(0xff9cbcda)); // 0xff9cbddb
runCheck(0xffe79ab5, const Color(0xffe79ab5));
runCheck(0xffbfd56f, const Color(0xffacc25d));
runCheck(0xfff4ae55, const Color(0xfff0ab52)); // 0xffefa951
runCheck(0xffb0a5fd, const Color(0xffb0a5fd));
runCheck(0xffaddfe5, const Color(0xff90c1c7)); // 0xff90c2c8
runCheck(0xfff5ce6e, const Color(0xffd9b456)); // 0xffd8b355
runCheck(0xffc2726a, const Color(0xffc2726a));
runCheck(0xff94c849, const Color(0xff94c849));
runCheck(0xffbd86e5, const Color(0xffbd86e5));
runCheck(0xffee7e4a, const Color(0xffee7e4a));
runCheck(0xffa6dcbf, const Color(0xff8fc4a8));
runCheck(0xff95a5fd, const Color(0xff95a5fd));
runCheck(0xff53a063, const Color(0xff53a063));
runCheck(0xff9987e1, const Color(0xff9987e1));
runCheck(0xffe4523d, const Color(0xffe4523d));
runCheck(0xffc2c2c2, const Color(0xffb9b9b9));
runCheck(0xff4f8de4, const Color(0xff4f8de4));
runCheck(0xffc6a8ad, const Color(0xffc6a8ad));
runCheck(0xffe7cc4d, const Color(0xffd1b839)); // 0xffd0b737
runCheck(0xffc8bebf, const Color(0xffc0b6b7));
runCheck(0xffa47462, const Color(0xffa47462));
runCheck(0xffacc25d, const Color(0xffacc25d));
});

test('iconOnBarBackground', () {
void runCheck(int base, Color expected) {
check(StreamColorSwatch.dark(base))
.iconOnBarBackground.equals(expected);
}

// Check against everything in ZULIP_ASSIGNMENT_COLORS
// in <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>.
// (Skipping `streamColors` because there are 100+ of them.)
// On how to generate expected results, see:
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972

// TODO Fix bug causing our implementation's results to differ from the
// web app's. Where they differ, see comment with what web uses.

runCheck(0xff76ce90, const Color(0xff73cb8d));
runCheck(0xfffae589, const Color(0xffccb95f)); // 0xffcbb85e
runCheck(0xffa6c7e5, const Color(0xff9cbcda)); // 0xff9cbddb
runCheck(0xffe79ab5, const Color(0xffe79ab5));
runCheck(0xffbfd56f, const Color(0xffacc25d));
runCheck(0xfff4ae55, const Color(0xfff0ab52)); // 0xffefa951
runCheck(0xffb0a5fd, const Color(0xffb0a5fd));
runCheck(0xffaddfe5, const Color(0xff90c1c7)); // 0xff90c2c8
runCheck(0xfff5ce6e, const Color(0xffd9b456)); // 0xffd8b355
runCheck(0xffc2726a, const Color(0xffc2726a));
runCheck(0xff94c849, const Color(0xff94c849));
runCheck(0xffbd86e5, const Color(0xffbd86e5));
runCheck(0xffee7e4a, const Color(0xffee7e4a));
runCheck(0xffa6dcbf, const Color(0xff8fc4a8));
runCheck(0xff95a5fd, const Color(0xff95a5fd));
runCheck(0xff53a063, const Color(0xff53a063));
runCheck(0xff9987e1, const Color(0xff9987e1));
runCheck(0xffe4523d, const Color(0xffe4523d));
runCheck(0xffc2c2c2, const Color(0xffb9b9b9));
runCheck(0xff4f8de4, const Color(0xff4f8de4));
runCheck(0xffc6a8ad, const Color(0xffc6a8ad));
runCheck(0xffe7cc4d, const Color(0xffd1b839)); // 0xffd0b737
runCheck(0xffc8bebf, const Color(0xffc0b6b7));
runCheck(0xffa47462, const Color(0xffa47462));
runCheck(0xffacc25d, const Color(0xffacc25d));
});

test('barBackground', () {
void runCheck(int base, Color expected) {
check(StreamColorSwatch.dark(base))
.barBackground.equals(expected);
}

// Check against everything in ZULIP_ASSIGNMENT_COLORS
// in <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>.
// (Skipping `streamColors` because there are 100+ of them.)
// On how to extract expected results from the replit, see:
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972

// TODO Fix bug causing our implementation's results to differ from the
// replit's. Where they differ, see comment with what the replit gives.

runCheck(0xff76ce90, const Color(0xff2e4935));
runCheck(0xfffae589, const Color(0xff4a4327));
runCheck(0xffa6c7e5, const Color(0xff3a444e)); // 0xff3a454e
runCheck(0xffe79ab5, const Color(0xff523a42));
runCheck(0xffbfd56f, const Color(0xff404627));
runCheck(0xfff4ae55, const Color(0xff563f23)); // 0xff553e23
runCheck(0xffb0a5fd, const Color(0xff413d59));
runCheck(0xffaddfe5, const Color(0xff374648));
runCheck(0xfff5ce6e, const Color(0xff4e4224)); // 0xff4e4124
runCheck(0xffc2726a, const Color(0xff472d2a));
runCheck(0xff94c849, const Color(0xff394821)); // 0xff384821
runCheck(0xffbd86e5, const Color(0xff453351));
runCheck(0xffee7e4a, const Color(0xff563120));
runCheck(0xffa6dcbf, const Color(0xff36473e));
runCheck(0xff95a5fd, const Color(0xff393d59));
runCheck(0xff53a063, const Color(0xff243c28));
runCheck(0xff9987e1, const Color(0xff3a3350));
runCheck(0xffe4523d, const Color(0xff53241c)); // 0xff53241b
runCheck(0xffc2c2c2, const Color(0xff434343));
runCheck(0xff4f8de4, const Color(0xff263551)); // 0xff253551
runCheck(0xffc6a8ad, const Color(0xff483e40));
runCheck(0xffe7cc4d, const Color(0xff4c431d)); // 0xff4c431c
runCheck(0xffc8bebf, const Color(0xff464243));
runCheck(0xffa47462, const Color(0xff3d2d27));
runCheck(0xffacc25d, const Color(0xff404627));
});
});

test('lerp (different a, b)', () {
final swatchA = StreamColorSwatch(0xff76ce90);

Expand Down

0 comments on commit 377f7d1

Please sign in to comment.