Skip to content

Commit 4ed0d5a

Browse files
chrisbobbegnprice
authored andcommitted
model: Implement StreamColorSwatch.dark
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
1 parent 5bbe7e4 commit 4ed0d5a

File tree

2 files changed

+237
-2
lines changed

2 files changed

+237
-2
lines changed

lib/api/model/model.dart

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,8 @@ class Subscription extends ZulipStream {
463463
/// Use this in UI code for colors related to [Subscription.color],
464464
/// such as the background of an unread count badge.
465465
class StreamColorSwatch extends ColorSwatch<StreamColorVariant> {
466-
StreamColorSwatch(int base) : this._(base, _compute(base));
466+
StreamColorSwatch(int base) : this._(base, _computeLight(base));
467+
StreamColorSwatch.dark(int base) : this._(base, _computeDark(base));
467468

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

@@ -498,7 +499,7 @@ class StreamColorSwatch extends ColorSwatch<StreamColorVariant> {
498499
/// Use this in the message list, the "Inbox" view, and the "Streams" view.
499500
Color get barBackground => this[StreamColorVariant.barBackground]!;
500501

501-
static Map<StreamColorVariant, Color> _compute(int base) {
502+
static Map<StreamColorVariant, Color> _computeLight(int base) {
502503
final baseAsColor = Color(base);
503504

504505
final clamped20to75 = clampLchLightness(baseAsColor, 20, 75);
@@ -547,6 +548,44 @@ class StreamColorSwatch extends ColorSwatch<StreamColorVariant> {
547548
};
548549
}
549550

551+
static Map<StreamColorVariant, Color> _computeDark(int base) {
552+
final baseAsColor = Color(base);
553+
554+
final clamped20to75 = clampLchLightness(baseAsColor, 20, 75);
555+
556+
return {
557+
// See comments in [_computeLight] about what these computations are based
558+
// on, and how the resulting values are a little off sometimes. The
559+
// comments mostly apply here too.
560+
561+
StreamColorVariant.base: baseAsColor,
562+
StreamColorVariant.unreadCountBadgeBackground:
563+
clampLchLightness(baseAsColor, 30, 70)
564+
.withOpacity(0.3),
565+
StreamColorVariant.iconOnPlainBackground: clamped20to75,
566+
567+
// Follows the web app (as of zulip/zulip@db03369ac); see
568+
// get_stream_privacy_icon_color in web/src/stream_color.ts.
569+
//
570+
// `.recepeient__icon` in Vlad's replit gives something different so we
571+
// don't use that:
572+
// <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>
573+
// <https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/design.3A.20.23F117.20.22Inbox.22.20screen/near/1624484>
574+
// But that's OK because Vlad said "I feel like current dark theme contrast
575+
// is fine", and when he said that, this had been the web app's icon color
576+
// for 6+ months (since zulip/zulip@023584e04):
577+
// https://chat.zulip.org/#narrow/stream/101-design/topic/UI.20redesign.3A.20recipient.20bar.20colors/near/1675786
578+
//
579+
// TODO fix bug where our results are unexpected (see unit tests)
580+
StreamColorVariant.iconOnBarBackground: clamped20to75,
581+
582+
StreamColorVariant.barBackground:
583+
LabColor.fromColor(const Color(0xff000000))
584+
.interpolate(LabColor.fromColor(clamped20to75), 0.38)
585+
.toColor(),
586+
};
587+
}
588+
550589
/// Copied from [ColorSwatch.lerp].
551590
static StreamColorSwatch? lerp(StreamColorSwatch? a, StreamColorSwatch? b, double t) {
552591
if (identical(a, b)) {

test/api/model/model_test.dart

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,202 @@ void main() {
319319
});
320320
});
321321

322+
group('dark', () {
323+
test('base', () {
324+
check(StreamColorSwatch.dark(0xffffffff))
325+
.base.equals(const Color(0xffffffff));
326+
});
327+
328+
test('unreadCountBadgeBackground', () {
329+
void runCheck(int base, Color expected) {
330+
check(StreamColorSwatch.dark(base))
331+
.unreadCountBadgeBackground.equals(expected);
332+
}
333+
334+
// Check against everything in ZULIP_ASSIGNMENT_COLORS and EXTREME_COLORS
335+
// in <https://replit.com/@VladKorobov/zulip-sidebar#script.js>.
336+
// On how to extract expected results from the replit, see:
337+
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972
338+
339+
// TODO Fix bug causing our implementation's results to differ from the
340+
// replit's. Where they differ, see comment with what the replit gives.
341+
342+
// ZULIP_ASSIGNMENT_COLORS
343+
runCheck(0xff76ce90, const Color(0x4d65bd80));
344+
runCheck(0xfffae589, const Color(0x4dbdab53)); // 0x4dbdaa52
345+
runCheck(0xffa6c7e5, const Color(0x4d8eafcc)); // 0x4d8fb0cd
346+
runCheck(0xffe79ab5, const Color(0x4de295b0)); // 0x4de194af
347+
runCheck(0xffbfd56f, const Color(0x4d9eb551)); // 0x4d9eb450
348+
runCheck(0xfff4ae55, const Color(0x4de19d45)); // 0x4de09c44
349+
runCheck(0xffb0a5fd, const Color(0x4daba0f8)); // 0x4daca2f9
350+
runCheck(0xffaddfe5, const Color(0x4d83b4b9)); // 0x4d83b4ba
351+
runCheck(0xfff5ce6e, const Color(0x4dcba749)); // 0x4dcaa648
352+
runCheck(0xffc2726a, const Color(0x4dc2726a));
353+
runCheck(0xff94c849, const Color(0x4d86ba3c)); // 0x4d86ba3b
354+
runCheck(0xffbd86e5, const Color(0x4dbd86e5));
355+
runCheck(0xffee7e4a, const Color(0x4dee7e4a));
356+
runCheck(0xffa6dcbf, const Color(0x4d82b69b)); // 0x4d82b79b
357+
runCheck(0xff95a5fd, const Color(0x4d95a5fd));
358+
runCheck(0xff53a063, const Color(0x4d53a063));
359+
runCheck(0xff9987e1, const Color(0x4d9987e1));
360+
runCheck(0xffe4523d, const Color(0x4de4523d));
361+
runCheck(0xffc2c2c2, const Color(0x4dababab));
362+
runCheck(0xff4f8de4, const Color(0x4d4f8de4));
363+
runCheck(0xffc6a8ad, const Color(0x4dc2a4a9)); // 0x4dc1a4a9
364+
runCheck(0xffe7cc4d, const Color(0x4dc3ab2a)); // 0x4dc2aa28
365+
runCheck(0xffc8bebf, const Color(0x4db3a9aa));
366+
runCheck(0xffa47462, const Color(0x4da47462));
367+
368+
// EXTREME_COLORS
369+
runCheck(0xFFFFFFFF, const Color(0x4dababab));
370+
runCheck(0xFF000000, const Color(0x4d474747));
371+
runCheck(0xFFD3D3D3, const Color(0x4dababab));
372+
runCheck(0xFFA9A9A9, const Color(0x4da9a9a9));
373+
runCheck(0xFF808080, const Color(0x4d808080));
374+
runCheck(0xFFFFFF00, const Color(0x4dacb300)); // 0x4dacb200
375+
runCheck(0xFFFF0000, const Color(0x4dff0000));
376+
runCheck(0xFF008000, const Color(0x4d008000));
377+
runCheck(0xFF0000FF, const Color(0x4d0000ff)); // 0x4d0902ff
378+
runCheck(0xFFEE82EE, const Color(0x4dee82ee));
379+
runCheck(0xFFFFA500, const Color(0x4def9800)); // 0x4ded9600
380+
runCheck(0xFF800080, const Color(0x4d810181)); // 0x4d810281
381+
runCheck(0xFF00FFFF, const Color(0x4d00c2c3)); // 0x4d00c3c5
382+
runCheck(0xFFFF00FF, const Color(0x4dff00ff));
383+
runCheck(0xFF00FF00, const Color(0x4d00cb00));
384+
runCheck(0xFF800000, const Color(0x4d8d140c)); // 0x4d8b130b
385+
runCheck(0xFF008080, const Color(0x4d008080));
386+
runCheck(0xFF000080, const Color(0x4d492bae)); // 0x4d4b2eb3
387+
runCheck(0xFFFFFFE0, const Color(0x4dadad90)); // 0x4dacad90
388+
runCheck(0xFFFF69B4, const Color(0x4dff69b4));
389+
});
390+
391+
test('iconOnPlainBackground', () {
392+
void runCheck(int base, Color expected) {
393+
check(StreamColorSwatch.dark(base))
394+
.iconOnPlainBackground.equals(expected);
395+
}
396+
397+
// Check against everything in ZULIP_ASSIGNMENT_COLORS
398+
// in <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>.
399+
// (Skipping `streamColors` because there are 100+ of them.)
400+
// On how to extract expected results from the replit, see:
401+
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972
402+
403+
// TODO Fix bug causing our implementation's results to differ from the
404+
// replit's. Where they differ, see comment with what the replit gives.
405+
406+
runCheck(0xff76ce90, const Color(0xff73cb8d));
407+
runCheck(0xfffae589, const Color(0xffccb95f)); // 0xffcbb85e
408+
runCheck(0xffa6c7e5, const Color(0xff9cbcda)); // 0xff9cbddb
409+
runCheck(0xffe79ab5, const Color(0xffe79ab5));
410+
runCheck(0xffbfd56f, const Color(0xffacc25d));
411+
runCheck(0xfff4ae55, const Color(0xfff0ab52)); // 0xffefa951
412+
runCheck(0xffb0a5fd, const Color(0xffb0a5fd));
413+
runCheck(0xffaddfe5, const Color(0xff90c1c7)); // 0xff90c2c8
414+
runCheck(0xfff5ce6e, const Color(0xffd9b456)); // 0xffd8b355
415+
runCheck(0xffc2726a, const Color(0xffc2726a));
416+
runCheck(0xff94c849, const Color(0xff94c849));
417+
runCheck(0xffbd86e5, const Color(0xffbd86e5));
418+
runCheck(0xffee7e4a, const Color(0xffee7e4a));
419+
runCheck(0xffa6dcbf, const Color(0xff8fc4a8));
420+
runCheck(0xff95a5fd, const Color(0xff95a5fd));
421+
runCheck(0xff53a063, const Color(0xff53a063));
422+
runCheck(0xff9987e1, const Color(0xff9987e1));
423+
runCheck(0xffe4523d, const Color(0xffe4523d));
424+
runCheck(0xffc2c2c2, const Color(0xffb9b9b9));
425+
runCheck(0xff4f8de4, const Color(0xff4f8de4));
426+
runCheck(0xffc6a8ad, const Color(0xffc6a8ad));
427+
runCheck(0xffe7cc4d, const Color(0xffd1b839)); // 0xffd0b737
428+
runCheck(0xffc8bebf, const Color(0xffc0b6b7));
429+
runCheck(0xffa47462, const Color(0xffa47462));
430+
runCheck(0xffacc25d, const Color(0xffacc25d));
431+
});
432+
433+
test('iconOnBarBackground', () {
434+
void runCheck(int base, Color expected) {
435+
check(StreamColorSwatch.dark(base))
436+
.iconOnBarBackground.equals(expected);
437+
}
438+
439+
// Check against everything in ZULIP_ASSIGNMENT_COLORS
440+
// in <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>.
441+
// (Skipping `streamColors` because there are 100+ of them.)
442+
// On how to generate expected results, see:
443+
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972
444+
445+
// TODO Fix bug causing our implementation's results to differ from the
446+
// web app's. Where they differ, see comment with what web uses.
447+
448+
runCheck(0xff76ce90, const Color(0xff73cb8d));
449+
runCheck(0xfffae589, const Color(0xffccb95f)); // 0xffcbb85e
450+
runCheck(0xffa6c7e5, const Color(0xff9cbcda)); // 0xff9cbddb
451+
runCheck(0xffe79ab5, const Color(0xffe79ab5));
452+
runCheck(0xffbfd56f, const Color(0xffacc25d));
453+
runCheck(0xfff4ae55, const Color(0xfff0ab52)); // 0xffefa951
454+
runCheck(0xffb0a5fd, const Color(0xffb0a5fd));
455+
runCheck(0xffaddfe5, const Color(0xff90c1c7)); // 0xff90c2c8
456+
runCheck(0xfff5ce6e, const Color(0xffd9b456)); // 0xffd8b355
457+
runCheck(0xffc2726a, const Color(0xffc2726a));
458+
runCheck(0xff94c849, const Color(0xff94c849));
459+
runCheck(0xffbd86e5, const Color(0xffbd86e5));
460+
runCheck(0xffee7e4a, const Color(0xffee7e4a));
461+
runCheck(0xffa6dcbf, const Color(0xff8fc4a8));
462+
runCheck(0xff95a5fd, const Color(0xff95a5fd));
463+
runCheck(0xff53a063, const Color(0xff53a063));
464+
runCheck(0xff9987e1, const Color(0xff9987e1));
465+
runCheck(0xffe4523d, const Color(0xffe4523d));
466+
runCheck(0xffc2c2c2, const Color(0xffb9b9b9));
467+
runCheck(0xff4f8de4, const Color(0xff4f8de4));
468+
runCheck(0xffc6a8ad, const Color(0xffc6a8ad));
469+
runCheck(0xffe7cc4d, const Color(0xffd1b839)); // 0xffd0b737
470+
runCheck(0xffc8bebf, const Color(0xffc0b6b7));
471+
runCheck(0xffa47462, const Color(0xffa47462));
472+
runCheck(0xffacc25d, const Color(0xffacc25d));
473+
});
474+
475+
test('barBackground', () {
476+
void runCheck(int base, Color expected) {
477+
check(StreamColorSwatch.dark(base))
478+
.barBackground.equals(expected);
479+
}
480+
481+
// Check against everything in ZULIP_ASSIGNMENT_COLORS
482+
// in <https://replit.com/@VladKorobov/zulip-topic-feed-colors#script.js>.
483+
// (Skipping `streamColors` because there are 100+ of them.)
484+
// On how to extract expected results from the replit, see:
485+
// https://github.com/zulip/zulip-flutter/pull/643#issuecomment-2093940972
486+
487+
// TODO Fix bug causing our implementation's results to differ from the
488+
// replit's. Where they differ, see comment with what the replit gives.
489+
490+
runCheck(0xff76ce90, const Color(0xff2e4935));
491+
runCheck(0xfffae589, const Color(0xff4a4327));
492+
runCheck(0xffa6c7e5, const Color(0xff3a444e)); // 0xff3a454e
493+
runCheck(0xffe79ab5, const Color(0xff523a42));
494+
runCheck(0xffbfd56f, const Color(0xff404627));
495+
runCheck(0xfff4ae55, const Color(0xff563f23)); // 0xff553e23
496+
runCheck(0xffb0a5fd, const Color(0xff413d59));
497+
runCheck(0xffaddfe5, const Color(0xff374648));
498+
runCheck(0xfff5ce6e, const Color(0xff4e4224)); // 0xff4e4124
499+
runCheck(0xffc2726a, const Color(0xff472d2a));
500+
runCheck(0xff94c849, const Color(0xff394821)); // 0xff384821
501+
runCheck(0xffbd86e5, const Color(0xff453351));
502+
runCheck(0xffee7e4a, const Color(0xff563120));
503+
runCheck(0xffa6dcbf, const Color(0xff36473e));
504+
runCheck(0xff95a5fd, const Color(0xff393d59));
505+
runCheck(0xff53a063, const Color(0xff243c28));
506+
runCheck(0xff9987e1, const Color(0xff3a3350));
507+
runCheck(0xffe4523d, const Color(0xff53241c)); // 0xff53241b
508+
runCheck(0xffc2c2c2, const Color(0xff434343));
509+
runCheck(0xff4f8de4, const Color(0xff263551)); // 0xff253551
510+
runCheck(0xffc6a8ad, const Color(0xff483e40));
511+
runCheck(0xffe7cc4d, const Color(0xff4c431d)); // 0xff4c431c
512+
runCheck(0xffc8bebf, const Color(0xff464243));
513+
runCheck(0xffa47462, const Color(0xff3d2d27));
514+
runCheck(0xffacc25d, const Color(0xff404627));
515+
});
516+
});
517+
322518
test('lerp (different a, b)', () {
323519
final swatchA = StreamColorSwatch(0xff76ce90);
324520

0 commit comments

Comments
 (0)