@@ -378,10 +378,8 @@ class BlockContentList extends StatelessWidget {
378378 return const SizedBox .shrink ();
379379 }(),
380380 WebsitePreviewNode () => WebsitePreview (node: node),
381- UnimplementedBlockContentNode () =>
382- Text .rich (_errorUnimplemented (node, context: context)),
381+ UnimplementedBlockContentNode () => ErrorUnimplemented (node: node),
383382 };
384-
385383 }),
386384 ]);
387385 }
@@ -528,16 +526,29 @@ class ListNodeWidget extends StatelessWidget {
528526 }
529527}
530528
531- class Spoiler extends StatefulWidget {
532- const Spoiler ({super .key, required this .node});
529+ class Modal extends StatefulWidget {
530+ const Modal ({
531+ super .key,
532+ required this .header,
533+ required this .content,
534+ required this .borderColor,
535+ required this .expandIconColor,
536+ this .bgColor,
537+ this .textColor,
538+ });
533539
534- final SpoilerNode node;
540+ final List <BlockContentNode > header;
541+ final List <BlockContentNode > content;
542+ final Color borderColor;
543+ final Color expandIconColor;
544+ final Color ? bgColor;
545+ final Color ? textColor;
535546
536547 @override
537- State <Spoiler > createState () => _SpoilerState ();
548+ State <Modal > createState () => _ModalState ();
538549}
539550
540- class _SpoilerState extends State <Spoiler > with TickerProviderStateMixin {
551+ class _ModalState extends State <Modal > with TickerProviderStateMixin {
541552 bool expanded = false ;
542553
543554 late final AnimationController _controller = AnimationController (
@@ -565,56 +576,73 @@ class _SpoilerState extends State<Spoiler> with TickerProviderStateMixin {
565576
566577 @override
567578 Widget build (BuildContext context) {
568- final zulipLocalizations = ZulipLocalizations .of (context);
569- final header = widget.node.header;
570- final effectiveHeader = header.isNotEmpty
571- ? header
572- : [ParagraphNode (links: null ,
573- nodes: [TextNode (zulipLocalizations.spoilerDefaultHeaderText)])];
574579 return Padding (
575580 padding: const EdgeInsets .fromLTRB (0 , 5 , 0 , 15 ),
576581 child: DecoratedBox (
577582 decoration: BoxDecoration (
578- // Web has the same color in light and dark mode.
579- border : Border . all ( color: const Color ( 0xff808080 )) ,
583+ border : Border . all ( color: widget.borderColor),
584+ color: widget.bgColor ,
580585 borderRadius: BorderRadius .circular (10 ),
581586 ),
582- child: Padding (padding: const EdgeInsetsDirectional .fromSTEB (10 , 2 , 8 , 2 ),
583- child: Column (
584- children: [
585- GestureDetector (
586- behavior: HitTestBehavior .translucent,
587- onTap: _handleTap,
588- child: Padding (
589- padding: const EdgeInsets .all (5 ),
590- child: Row (crossAxisAlignment: CrossAxisAlignment .end, children: [
591- Expanded (
592- child: DefaultTextStyle .merge (
593- style: weightVariableTextStyle (context, wght: 700 ),
594- child: BlockContentList (
595- nodes: effectiveHeader))),
596- RotationTransition (
597- turns: _animation.drive (Tween (begin: 0 , end: 0.5 )),
598- // Web has the same color in light and dark mode.
599- child: const Icon (color: Color (0xffd4d4d4 ), size: 25 ,
600- Icons .expand_more)),
601- ]))),
602- FadeTransition (
603- opacity: _animation,
604- child: const SizedBox (height: 0 , width: double .infinity,
605- child: DecoratedBox (
606- decoration: BoxDecoration (
607- border: Border (
608- // Web has the same color in light and dark mode.
609- bottom: BorderSide (width: 1 , color: Color (0xff808080 ))))))),
610- SizeTransition (
611- sizeFactor: _animation,
612- axis: Axis .vertical,
613- axisAlignment: - 1 ,
614- child: Padding (
615- padding: const EdgeInsets .all (5 ),
616- child: BlockContentList (nodes: widget.node.content))),
617- ]))));
587+ child: DefaultTextStyle .merge (
588+ style: TextStyle (color: widget.textColor),
589+ child: Padding (padding: const EdgeInsetsDirectional .fromSTEB (10 , 2 , 8 , 2 ),
590+ child: Column (
591+ children: [
592+ GestureDetector (
593+ behavior: HitTestBehavior .translucent,
594+ onTap: _handleTap,
595+ child: Padding (
596+ padding: const EdgeInsets .all (5 ),
597+ child: Row (crossAxisAlignment: CrossAxisAlignment .end, children: [
598+ Expanded (
599+ child: DefaultTextStyle .merge (
600+ style: weightVariableTextStyle (context, wght: 700 ),
601+ child: BlockContentList (
602+ nodes: widget.header))),
603+ RotationTransition (
604+ turns: _animation.drive (Tween (begin: 0 , end: 0.5 )),
605+ child: Icon (color: widget.expandIconColor, size: 25 ,
606+ Icons .expand_more)),
607+ ]))),
608+ FadeTransition (
609+ opacity: _animation,
610+ child: SizedBox (height: 0 , width: double .infinity,
611+ child: DecoratedBox (
612+ decoration: BoxDecoration (
613+ border: Border (
614+ bottom: BorderSide (width: 1 , color: widget.borderColor)))))),
615+ SizeTransition (
616+ sizeFactor: _animation,
617+ axis: Axis .vertical,
618+ axisAlignment: - 1 ,
619+ child: Padding (
620+ padding: const EdgeInsets .all (5 ),
621+ child: BlockContentList (nodes: widget.content))),
622+ ])),
623+ )));
624+ }
625+ }
626+
627+ class Spoiler extends StatelessWidget {
628+ const Spoiler ({super .key, required this .node});
629+
630+ final SpoilerNode node;
631+
632+ @override
633+ Widget build (BuildContext context) {
634+ final zulipLocalizations = ZulipLocalizations .of (context);
635+ final header = node.header;
636+ final effectiveHeader = header.isNotEmpty
637+ ? header
638+ : [ParagraphNode (links: null ,
639+ nodes: [TextNode (zulipLocalizations.spoilerDefaultHeaderText)])];
640+ return Modal (
641+ header: effectiveHeader,
642+ content: node.content,
643+ borderColor: const Color (0xff808080 ), // Web has the same color in light and dark mode.
644+ expandIconColor: const Color (0xffd4d4d4 ), // Web has the same color in light and dark mode.
645+ );
618646 }
619647}
620648
@@ -1272,7 +1300,8 @@ class _InlineContentBuilder {
12721300 child: GlobalTime (node: node, ambientTextStyle: widget.style));
12731301
12741302 case UnimplementedInlineContentNode ():
1275- return _errorUnimplemented (node, context: _context! );
1303+ return WidgetSpan (alignment: PlaceholderAlignment .middle,
1304+ child: ErrorUnimplemented (node: node));
12761305 }
12771306 }
12781307
@@ -1899,35 +1928,52 @@ class _PresenceCircleState extends State<PresenceCircle> with PerAccountStoreAwa
18991928 }
19001929}
19011930
1902- //
1903- // Small helpers.
1904- //
1931+ class ErrorUnimplemented extends StatelessWidget {
1932+ const ErrorUnimplemented ({
1933+ super .key,
1934+ required this .node,
1935+ });
19051936
1906- InlineSpan _errorUnimplemented (UnimplementedNode node, {required BuildContext context}) {
1907- final contentTheme = ContentTheme .of (context);
1908- final errorStyle = contentTheme.textStyleError;
1909- final errorCodeStyle = contentTheme.textStyleErrorCode;
1910- // For now this shows error-styled HTML code even in release mode,
1911- // because release mode isn't yet about general users but developer demos,
1912- // and we want to keep the demos honest.
1913- // TODO(#194) think through UX for general release
1914- // TODO(#1285) translate this
1915- final htmlNode = node.htmlNode;
1916- if (htmlNode is dom.Element ) {
1917- return TextSpan (children: [
1918- TextSpan (text: "(unimplemented:" , style: errorStyle),
1919- TextSpan (text: htmlNode.outerHtml, style: errorCodeStyle),
1920- TextSpan (text: ")" , style: errorStyle),
1921- ]);
1922- } else if (htmlNode is dom.Text ) {
1923- return TextSpan (children: [
1924- TextSpan (text: "(unimplemented: text «" , style: errorStyle),
1925- TextSpan (text: htmlNode.text, style: errorCodeStyle),
1926- TextSpan (text: "»)" , style: errorStyle),
1927- ]);
1928- } else {
1929- return TextSpan (
1930- text: "(unimplemented: DOM node type ${htmlNode .nodeType })" ,
1931- style: errorStyle);
1937+ final UnimplementedNode node;
1938+
1939+ @override
1940+ Widget build (BuildContext context) {
1941+ final zulipLocalizations = ZulipLocalizations .of (context);
1942+ final htmlNode = node.htmlNode;
1943+ final text = htmlNode is dom.Element ? htmlNode.outerHtml : htmlNode.text ?? '' ;
1944+ final header = [
1945+ ParagraphNode (
1946+ links: null ,
1947+ nodes: [TextNode (zulipLocalizations.errorUnimplementedHeader)],
1948+ ),
1949+ ];
1950+ final content = [
1951+ HeadingNode (
1952+ links: null ,
1953+ nodes: [TextNode (zulipLocalizations.errorUnimplementedWhatHappened)],
1954+ level: HeadingLevel .h3,
1955+ ),
1956+ ParagraphNode (
1957+ links: null ,
1958+ nodes: [TextNode (zulipLocalizations.errorUnimplementedDescription)],
1959+ ),
1960+ HeadingNode (
1961+ links: null ,
1962+ nodes: [TextNode (zulipLocalizations.errorUnimplementedHtmlHeading)],
1963+ level: HeadingLevel .h3,
1964+ ),
1965+ ParagraphNode (
1966+ links: null ,
1967+ nodes: [InlineCodeNode (nodes: [TextNode (text)])],
1968+ ),
1969+ ];
1970+ return Modal (
1971+ borderColor: const Color (0xffbb0000 ),
1972+ expandIconColor: const Color (0xffffff00 ),
1973+ textColor: const Color (0xffffff00 ),
1974+ bgColor: const Color (0xffff0000 ),
1975+ header: header,
1976+ content: content,
1977+ );
19321978 }
19331979}
0 commit comments