@@ -378,10 +378,8 @@ class BlockContentList extends StatelessWidget {
378
378
return const SizedBox .shrink ();
379
379
}(),
380
380
WebsitePreviewNode () => WebsitePreview (node: node),
381
- UnimplementedBlockContentNode () =>
382
- Text .rich (_errorUnimplemented (node, context: context)),
381
+ UnimplementedBlockContentNode () => ErrorUnimplemented (node: node),
383
382
};
384
-
385
383
}),
386
384
]);
387
385
}
@@ -528,16 +526,29 @@ class ListNodeWidget extends StatelessWidget {
528
526
}
529
527
}
530
528
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
+ });
533
539
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;
535
546
536
547
@override
537
- State <Spoiler > createState () => _SpoilerState ();
548
+ State <Modal > createState () => _ModalState ();
538
549
}
539
550
540
- class _SpoilerState extends State <Spoiler > with TickerProviderStateMixin {
551
+ class _ModalState extends State <Modal > with TickerProviderStateMixin {
541
552
bool expanded = false ;
542
553
543
554
late final AnimationController _controller = AnimationController (
@@ -565,56 +576,73 @@ class _SpoilerState extends State<Spoiler> with TickerProviderStateMixin {
565
576
566
577
@override
567
578
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)])];
574
579
return Padding (
575
580
padding: const EdgeInsets .fromLTRB (0 , 5 , 0 , 15 ),
576
581
child: DecoratedBox (
577
582
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 ,
580
585
borderRadius: BorderRadius .circular (10 ),
581
586
),
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
+ );
618
646
}
619
647
}
620
648
@@ -1272,7 +1300,8 @@ class _InlineContentBuilder {
1272
1300
child: GlobalTime (node: node, ambientTextStyle: widget.style));
1273
1301
1274
1302
case UnimplementedInlineContentNode ():
1275
- return _errorUnimplemented (node, context: _context! );
1303
+ return WidgetSpan (alignment: PlaceholderAlignment .middle,
1304
+ child: ErrorUnimplemented (node: node));
1276
1305
}
1277
1306
}
1278
1307
@@ -1899,35 +1928,52 @@ class _PresenceCircleState extends State<PresenceCircle> with PerAccountStoreAwa
1899
1928
}
1900
1929
}
1901
1930
1902
- //
1903
- // Small helpers.
1904
- //
1931
+ class ErrorUnimplemented extends StatelessWidget {
1932
+ const ErrorUnimplemented ({
1933
+ super .key,
1934
+ required this .node,
1935
+ });
1905
1936
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
+ );
1932
1978
}
1933
1979
}
0 commit comments