|
1 | 1 | import 'package:json_annotation/json_annotation.dart';
|
2 | 2 |
|
| 3 | +import '../../basic.dart'; |
3 | 4 | import '../../model/algorithms.dart';
|
4 | 5 | import 'events.dart';
|
5 | 6 | import 'initial_snapshot.dart';
|
@@ -158,6 +159,119 @@ class RealmEmojiItem {
|
158 | 159 | Map<String, dynamic> toJson() => _$RealmEmojiItemToJson(this);
|
159 | 160 | }
|
160 | 161 |
|
| 162 | +/// A user's status, with [text] and [emoji] parts. |
| 163 | +/// |
| 164 | +/// If a part is null, that part is empty/unset. |
| 165 | +/// For a [UserStatus] with all parts empty, see [zero]. |
| 166 | +class UserStatus { |
| 167 | + /// The text part (e.g. 'Working remotely'), or null if unset. |
| 168 | + /// |
| 169 | + /// This won't be the empty string. |
| 170 | + final String? text; |
| 171 | + |
| 172 | + /// The emoji part, or null if unset. |
| 173 | + final StatusEmoji? emoji; |
| 174 | + |
| 175 | + const UserStatus({required this.text, required this.emoji}) : assert(text != ''); |
| 176 | + |
| 177 | + static const UserStatus zero = UserStatus(text: null, emoji: null); |
| 178 | +} |
| 179 | + |
| 180 | +/// A user's status emoji, as in [UserStatus.emoji]. |
| 181 | +class StatusEmoji { |
| 182 | + final String emojiName; |
| 183 | + final String emojiCode; |
| 184 | + final ReactionType reactionType; |
| 185 | + |
| 186 | + const StatusEmoji({ |
| 187 | + required this.emojiName, |
| 188 | + required this.emojiCode, |
| 189 | + required this.reactionType, |
| 190 | + }) : assert(emojiName != ''), assert(emojiCode != ''); |
| 191 | +} |
| 192 | + |
| 193 | +/// A change to part or all of a user's status. |
| 194 | +/// |
| 195 | +/// The absence of one of these means there is no change. |
| 196 | +class UserStatusChange { |
| 197 | + // final Option<bool> away; // deprecated in server-6 (FL-148); ignore |
| 198 | + final Option<String?> text; |
| 199 | + final Option<StatusEmoji?> emoji; |
| 200 | + |
| 201 | + const UserStatusChange({required this.text, required this.emoji}); |
| 202 | + |
| 203 | + factory UserStatusChange.fromJson(Map<String, dynamic> json) { |
| 204 | + return UserStatusChange( |
| 205 | + text: _textFromJson(json), emoji: _emojiFromJson(json)); |
| 206 | + } |
| 207 | + |
| 208 | + static Option<String?> _textFromJson(Map<String, dynamic> json) { |
| 209 | + return switch (json['status_text'] as String?) { |
| 210 | + null => OptionNone(), |
| 211 | + '' => OptionSome(null), |
| 212 | + final apiValue => OptionSome(apiValue), |
| 213 | + }; |
| 214 | + } |
| 215 | + |
| 216 | + static Option<StatusEmoji?> _emojiFromJson(Map<String, dynamic> json) { |
| 217 | + final reactionType = json['reaction_type'] as String?; |
| 218 | + final emojiCode = json['emoji_code'] as String?; |
| 219 | + final emojiName = json['emoji_name'] as String?; |
| 220 | + |
| 221 | + if (reactionType == null || emojiCode == null || emojiName == null) { |
| 222 | + return OptionNone(); |
| 223 | + } else if (reactionType == '' || emojiCode == '' || emojiName == '') { |
| 224 | + // Sometimes `reaction_type` is 'unicode_emoji' when the emoji is cleared. |
| 225 | + // This is an accident, to be handled by looking at `emoji_code` instead: |
| 226 | + // https://chat.zulip.org/#narrow/channel/378-api-design/topic/user.20status/near/2203132 |
| 227 | + return OptionSome(null); |
| 228 | + } else { |
| 229 | + return OptionSome(StatusEmoji( |
| 230 | + reactionType: ReactionType.fromApiValue(reactionType), |
| 231 | + emojiCode: emojiCode, |
| 232 | + emojiName: emojiName)); |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + Map<String, dynamic> toJson() { |
| 237 | + return { |
| 238 | + ..._textToJson(text), |
| 239 | + ..._emojiToJson(emoji), |
| 240 | + }; |
| 241 | + } |
| 242 | + |
| 243 | + Map<String, dynamic> _textToJson(Option<String?> text) { |
| 244 | + return { |
| 245 | + 'status_text': switch (text) { |
| 246 | + OptionNone<String?>() => null, |
| 247 | + OptionSome<String?>(:var value) => value ?? '', |
| 248 | + } |
| 249 | + }; |
| 250 | + } |
| 251 | + |
| 252 | + Map<String, dynamic> _emojiToJson(Option<StatusEmoji?> emoji) { |
| 253 | + return switch (emoji) { |
| 254 | + OptionNone<StatusEmoji?>() => { |
| 255 | + 'reaction_type': null, |
| 256 | + 'emoji_code': null, |
| 257 | + 'emoji_name': null, |
| 258 | + }, |
| 259 | + OptionSome<StatusEmoji?>(:var value) => |
| 260 | + value == null |
| 261 | + ? {'reaction_type': '', 'emoji_code': '', 'emoji_name': ''} |
| 262 | + : { |
| 263 | + 'reaction_type': value.reactionType, |
| 264 | + 'emoji_code': value.emojiCode, |
| 265 | + 'emoji_name': value.emojiName, |
| 266 | + }, |
| 267 | + }; |
| 268 | + } |
| 269 | + |
| 270 | + UserStatus apply(UserStatus old) { |
| 271 | + return UserStatus(text: text.or(old.text), emoji: emoji.or(old.emoji)); |
| 272 | + } |
| 273 | +} |
| 274 | + |
161 | 275 | /// The name of a user setting that has a property in [UserSettings].
|
162 | 276 | ///
|
163 | 277 | /// In Zulip event-handling code (for [UserSettingsUpdateEvent]),
|
|
0 commit comments