Skip to content

Commit aee4dab

Browse files
committed
model: Cache stream-color swatch on Subscription object
As mentioned in a TODO, I'm not sure it makes sense to deal with package:flutter/painting.dart from within our lib/api/model/ code. We might end up factoring that out somewhere else. Next, we'll have our UnreadCountBadge widget optionally consume one of these swatches.
1 parent eac1971 commit aee4dab

File tree

3 files changed

+68
-4
lines changed

3 files changed

+68
-4
lines changed

lib/api/model/model.dart

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter/painting.dart';
13
import 'package:json_annotation/json_annotation.dart';
24

35
import 'reaction.dart';
@@ -328,14 +330,29 @@ class Subscription extends ZulipStream {
328330
/// As an int that dart:ui's Color constructor will take:
329331
/// <https://api.flutter.dev/flutter/dart-ui/Color/Color.html>
330332
@JsonKey(readValue: _readColor)
331-
int color;
332-
333+
int get color => _color;
334+
int _color;
335+
set color(int value) {
336+
_color = value;
337+
_swatch = null;
338+
}
333339
static Object? _readColor(Map json, String key) {
334340
final str = (json[key] as String);
335341
assert(RegExp(r'^#[0-9a-f]{6}$').hasMatch(str));
336342
return 0xff000000 | int.parse(str.substring(1), radix: 16);
337343
}
338344

345+
StreamColorSwatch? _swatch;
346+
/// A [StreamColorSwatch] for the subscription, memoized.
347+
// TODO I'm not sure this is the right home for this; it seems like we might
348+
// instead have chosen to put it in more UI-centered code, like in a custom
349+
// material [ColorScheme] class or something. But it works for now.
350+
StreamColorSwatch colorSwatch() => _swatch ??= StreamColorSwatch(color);
351+
352+
@visibleForTesting
353+
@JsonKey(includeToJson: false)
354+
StreamColorSwatch? get debugCachedSwatchValue => _swatch;
355+
339356
Subscription({
340357
required super.streamId,
341358
required super.name,
@@ -357,8 +374,8 @@ class Subscription extends ZulipStream {
357374
required this.audibleNotifications,
358375
required this.pinToTop,
359376
required this.isMuted,
360-
required this.color,
361-
});
377+
required int color,
378+
}) : _color = color;
362379

363380
factory Subscription.fromJson(Map<String, dynamic> json) =>
364381
_$SubscriptionFromJson(json);
@@ -367,6 +384,29 @@ class Subscription extends ZulipStream {
367384
Map<String, dynamic> toJson() => _$SubscriptionToJson(this);
368385
}
369386

387+
/// A [ColorSwatch] with colors related to a base stream color.
388+
///
389+
/// Use this in UI code for colors related to [Subscription.color],
390+
/// such as the background of an unread count badge.
391+
class StreamColorSwatch extends ColorSwatch<_StreamColorVariant> {
392+
StreamColorSwatch(int base) : super(base, _compute(base));
393+
394+
Color get base => this[_StreamColorVariant.base]!;
395+
396+
static Map<_StreamColorVariant, Color> _compute(int base) {
397+
final baseAsColor = Color(base);
398+
399+
return {
400+
_StreamColorVariant.base: baseAsColor,
401+
};
402+
}
403+
}
404+
405+
enum _StreamColorVariant {
406+
base,
407+
// TODO more, like the unread-count badge background color
408+
}
409+
370410
/// As in the get-messages response.
371411
///
372412
/// https://zulip.com/api/get-messages#response

test/api/model/model_checks.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import 'dart:ui';
2+
13
import 'package:checks/checks.dart';
24
import 'package:zulip/api/model/model.dart';
35

46
extension ZulipStreamChecks on Subject<ZulipStream> {
57
Subject<int?> get canRemoveSubscribersGroup => has((e) => e.canRemoveSubscribersGroup, 'canRemoveSubscribersGroup');
68
}
79

10+
extension StreamColorSwatchChecks on Subject<StreamColorSwatch> {
11+
Subject<Color> get base => has((s) => s.base, 'base');
12+
}
13+
814
extension MessageChecks on Subject<Message> {
915
Subject<String> get content => has((e) => e.content, 'content');
1016
Subject<bool> get isMeMessage => has((e) => e.isMeMessage, 'isMeMessage');

test/api/model/model_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:convert';
2+
import 'dart:ui';
23

34
import 'package:checks/checks.dart';
45
import 'package:test/scaffolding.dart';
@@ -114,6 +115,23 @@ void main() {
114115
check(subWithColor('#ffffff').color).equals(0xffffffff);
115116
check(subWithColor('#000000').color).equals(0xff000000);
116117
});
118+
119+
test('colorSwatch caching', () {
120+
final sub = eg.subscription(eg.stream(), color: 0xffffffff);
121+
check(sub.debugCachedSwatchValue).isNull();
122+
sub.colorSwatch();
123+
check(sub.debugCachedSwatchValue).isNotNull().base.equals(const Color(0xffffffff));
124+
sub.color = 0xffff0000;
125+
check(sub.debugCachedSwatchValue).isNull();
126+
sub.colorSwatch();
127+
check(sub.debugCachedSwatchValue).isNotNull().base.equals(const Color(0xffff0000));
128+
});
129+
130+
group('StreamColorSwatch', () {
131+
test('base', () {
132+
check(StreamColorSwatch(0xffffffff)).base.equals(const Color(0xffffffff));
133+
});
134+
});
117135
});
118136

119137
group('Message', () {

0 commit comments

Comments
 (0)