11import 'package:flutter/material.dart' ;
22
33import '../generated/l10n/zulip_localizations.dart' ;
4+ import '../model/content.dart' ;
45import '../model/emoji.dart' ;
56import '../model/store.dart' ;
7+ import 'content.dart' ;
68import 'emoji.dart' ;
79import 'icons.dart' ;
810import 'store.dart' ;
@@ -45,8 +47,7 @@ class _AutocompleteFieldState<QueryT extends AutocompleteQuery, ResultT extends
4547 }
4648
4749 void _handleControllerChange () {
48- var newQuery = widget.autocompleteIntent ()? .query;
49- if (newQuery is ChannelLinkAutocompleteQuery ) newQuery = null ; // TODO(#124)
50+ final newQuery = widget.autocompleteIntent ()? .query;
5051 // First, tear down the old view-model if necessary.
5152 if (_viewModel != null
5253 && (newQuery == null
@@ -227,8 +228,17 @@ class ComposeAutocomplete extends AutocompleteField<ComposeAutocompleteQuery, Co
227228 // TODO(#1805) language-appropriate space character; check active keyboard?
228229 // (maybe handle centrally in `controller`)
229230 replacementString = '${userGroupMention (userGroup .name , silent : query .silent )} ' ;
230- case ChannelLinkAutocompleteResult ():
231- throw UnimplementedError (); // TODO(#124)
231+ case ChannelLinkAutocompleteResult (: final channelId):
232+ if (query is ! ChannelLinkAutocompleteQuery ) {
233+ return ; // Shrug; similar to `intent == null` case above.
234+ }
235+ final channel = store.streams[channelId];
236+ if (channel == null ) {
237+ // Don't crash on theoretical race between async results-filtering
238+ // and losing data for the channel.
239+ return ;
240+ }
241+ replacementString = '${channelLinkSyntax (channel , store : store )} ' ;
232242 }
233243
234244 controller.value = intent.textEditingValue.replaced (
@@ -246,7 +256,7 @@ class ComposeAutocomplete extends AutocompleteField<ComposeAutocompleteQuery, Co
246256 final child = switch (option) {
247257 MentionAutocompleteResult () => MentionAutocompleteItem (
248258 option: option, narrow: narrow),
249- ChannelLinkAutocompleteResult () => throw UnimplementedError (), // TODO(#124)
259+ ChannelLinkAutocompleteResult () => _ChannelLinkAutocompleteItem (option : option),
250260 EmojiAutocompleteResult () => _EmojiAutocompleteItem (option: option),
251261 };
252262 return InkWell (
@@ -361,6 +371,68 @@ class MentionAutocompleteItem extends StatelessWidget {
361371 }
362372}
363373
374+ class _ChannelLinkAutocompleteItem extends StatelessWidget {
375+ const _ChannelLinkAutocompleteItem ({required this .option});
376+
377+ final ChannelLinkAutocompleteResult option;
378+
379+ @override
380+ Widget build (BuildContext context) {
381+ final store = PerAccountStoreWidget .of (context);
382+ final zulipLocalizations = ZulipLocalizations .of (context);
383+ final designVariables = DesignVariables .of (context);
384+
385+ final channel = store.streams[option.channelId];
386+
387+ // A null [Icon.icon] makes a blank space.
388+ IconData ? icon;
389+ Color ? iconColor;
390+ String label;
391+ String ? subLabel;
392+ if (channel != null ) {
393+ icon = iconDataForStream (channel);
394+ iconColor = colorSwatchFor (context, store.subscriptions[channel.streamId])
395+ .iconOnPlainBackground;
396+ label = channel.name;
397+ subLabel = channel.renderedDescription.isNotEmpty
398+ ? channel.renderedDescription : null ;
399+ } else {
400+ icon = null ;
401+ iconColor = null ;
402+ label = zulipLocalizations.unknownChannelName;
403+ subLabel = null ;
404+ }
405+
406+ final labelWidget = Text (label,
407+ overflow: TextOverflow .ellipsis,
408+ style: TextStyle (
409+ fontSize: 18 , height: 20 / 18 ,
410+ color: designVariables.contextMenuItemLabel,
411+ ).merge (weightVariableTextStyle (context, wght: 600 )));
412+
413+ final subLabelWidget = subLabel == null ? null
414+ // Adapted from [MessageContent].
415+ : DefaultTextStyle (
416+ style: ContentTheme .of (context).textStylePlainParagraph.merge (
417+ TextStyle (fontSize: 14 , height: 16 / 14 ,
418+ overflow: TextOverflow .ellipsis,
419+ color: designVariables.contextMenuItemMeta)),
420+ child: BlockContentList (
421+ nodes: parseContent (channel! .renderedDescription).nodes),
422+ );
423+
424+ return Padding (
425+ padding: EdgeInsetsGeometry .fromSTEB (12 , 4 , 10 , 4 ),
426+ child: Row (spacing: 10 , children: [
427+ SizedBox .square (dimension: 24 ,
428+ child: Icon (size: 18 , color: iconColor, icon)),
429+ Expanded (child: Column (
430+ crossAxisAlignment: CrossAxisAlignment .start,
431+ children: [labelWidget, ? subLabelWidget])),
432+ ]));
433+ }
434+ }
435+
364436class _EmojiAutocompleteItem extends StatelessWidget {
365437 const _EmojiAutocompleteItem ({required this .option});
366438
0 commit comments