diff --git a/lib/shared/widgets/copied_text.dart b/lib/shared/widgets/copied_text.dart index 6879905c3b..8194c53178 100644 --- a/lib/shared/widgets/copied_text.dart +++ b/lib/shared/widgets/copied_text.dart @@ -5,7 +5,7 @@ import 'package:web_dex/shared/widgets/truncate_middle_text.dart'; class CopiedText extends StatelessWidget { const CopiedText({ - Key? key, + super.key, required this.copiedValue, this.text, this.maxLines, @@ -18,7 +18,7 @@ class CopiedText extends StatelessWidget { this.fontWeight = FontWeight.w500, this.iconSize = 22, this.height, - }) : super(key: key); + }); final String copiedValue; final String? text; @@ -35,7 +35,6 @@ class CopiedText extends StatelessWidget { @override Widget build(BuildContext context) { - final softWrap = (maxLines ?? 0) > 1; final String showingText = text ?? copiedValue; final Color? background = backgroundColor ?? Theme.of(context).inputDecorationTheme.fillColor; @@ -57,32 +56,30 @@ class CopiedText extends StatelessWidget { Container( key: const Key('coin-details-address-field'), child: isTruncated - ? Flexible( - child: TruncatedMiddleText( - showingText, - style: TextStyle( - fontSize: fontSize, - fontWeight: fontWeight, - color: fontColor, - height: height, + ? Flexible( + child: TruncatedMiddleText( + showingText, + style: TextStyle( + fontSize: fontSize, + fontWeight: fontWeight, + color: fontColor, + height: height, + ), ), - ), - ) - : Flexible( - child: AutoScrollText( - text: showingText, - style: TextStyle( - fontSize: fontSize, - fontWeight: fontWeight, - color: fontColor, - height: height, + ) + : Flexible( + child: AutoScrollText( + text: showingText, + style: TextStyle( + fontSize: fontSize, + fontWeight: fontWeight, + color: fontColor, + height: height, + ), ), ), - ), - ), - const SizedBox( - width: 16, ), + const SizedBox(width: 16), Icon( Icons.copy_rounded, color: Theme.of(context).textTheme.labelLarge?.color, @@ -98,7 +95,7 @@ class CopiedText extends StatelessWidget { class CopiedTextV2 extends StatelessWidget { const CopiedTextV2({ - Key? key, + super.key, required this.copiedValue, this.text, this.maxLines, @@ -108,7 +105,7 @@ class CopiedTextV2 extends StatelessWidget { this.iconSize = 12, this.backgroundColor, this.textColor, - }) : super(key: key); + }); final String copiedValue; final String? text; @@ -124,7 +121,6 @@ class CopiedTextV2 extends StatelessWidget { @override Widget build(BuildContext context) { - final softWrap = (maxLines ?? 0) > 1; final String? showingText = text; return InkWell( @@ -142,26 +138,28 @@ class CopiedTextV2 extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ if (isCopiedValueShown) ...[ - Container( - key: const Key('coin-details-address-field'), - child: isTruncated - ? Flexible( - child: TruncatedMiddleText( + Flexible( + child: Container( + key: const Key('coin-details-address-field'), + child: isTruncated + ? TruncatedMiddleText( copiedValue, level: 4, style: TextStyle( - fontSize: fontSize, - fontWeight: FontWeight.w700, - color: textColor ?? const Color(0xFFADAFC4)), - ), - ) - : AutoScrollText( - text: copiedValue, - style: TextStyle( fontSize: fontSize, fontWeight: FontWeight.w700, - color: textColor ?? const Color(0xFFADAFC4)), - ), + color: textColor ?? const Color(0xFFADAFC4), + ), + ) + : AutoScrollText( + text: copiedValue, + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.w700, + color: textColor ?? const Color(0xFFADAFC4), + ), + ), + ), ), const SizedBox(width: 4), ], @@ -173,7 +171,7 @@ class CopiedTextV2 extends StatelessWidget { if (showingText != null) ...[ const SizedBox(width: 10), Text(showingText), - ] + ], ], ), ), diff --git a/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart b/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart index ade54b8129..33657b0278 100644 --- a/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart +++ b/lib/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart @@ -19,17 +19,20 @@ class DexListHeaderMobile extends StatelessWidget { required this.onFilterPressed, required this.onFilterDataChange, required this.isFilterShown, + this.centerWidget, }) : super(key: key); final DexListType listType; final TradingEntitiesFilter? entitiesFilterData; final bool isFilterShown; final VoidCallback onFilterPressed; final void Function(TradingEntitiesFilter?) onFilterDataChange; + final Widget? centerWidget; @override Widget build(BuildContext context) { - final tradingEntitiesBloc = - RepositoryProvider.of(context); + final tradingEntitiesBloc = RepositoryProvider.of( + context, + ); final List filterElements = _getFilterElements(context); final filterData = entitiesFilterData; return Column( @@ -41,6 +44,11 @@ class DexListHeaderMobile extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildFilterButton(context), + if (centerWidget != null) ...[ + const SizedBox(width: 8), + Expanded(child: centerWidget!), + const SizedBox(width: 8), + ], if (listType == DexListType.orders) UiPrimaryButton( text: LocaleKeys.cancelAll.tr(), @@ -81,10 +89,12 @@ class DexListHeaderMobile extends StatelessWidget { final DateTime? startDate = filterData?.startDate; final DateTime? endDate = filterData?.endDate; - final String? startDateString = - startDate != null ? DateFormat('dd.MM.yyyy').format(startDate) : null; - final String? endDateString = - endDate != null ? DateFormat('dd.MM.yyyy').format(endDate) : null; + final String? startDateString = startDate != null + ? DateFormat('dd.MM.yyyy').format(startDate) + : null; + final String? endDateString = endDate != null + ? DateFormat('dd.MM.yyyy').format(endDate) + : null; final List? statuses = filterData?.statuses; final List? shownSides = filterData?.shownSides; @@ -133,21 +143,22 @@ class DexListHeaderMobile extends StatelessWidget { children.addAll( statuses.map( (s) => _buildManageFilterItem( - LocaleKeys.status.tr(), - s == TradingStatus.successful - ? LocaleKeys.successful.tr() - : LocaleKeys.failed.tr(), - () => onFilterDataChange( - TradingEntitiesFilter( - buyCoin: buyCoin, - sellCoin: sellCoin, - startDate: startDate, - endDate: endDate, - statuses: statuses.where((e) => e != s).toList(), - shownSides: shownSides, - ), - ), - context), + LocaleKeys.status.tr(), + s == TradingStatus.successful + ? LocaleKeys.successful.tr() + : LocaleKeys.failed.tr(), + () => onFilterDataChange( + TradingEntitiesFilter( + buyCoin: buyCoin, + sellCoin: sellCoin, + startDate: startDate, + endDate: endDate, + statuses: statuses.where((e) => e != s).toList(), + shownSides: shownSides, + ), + ), + context, + ), ), ); } @@ -166,8 +177,9 @@ class DexListHeaderMobile extends StatelessWidget { startDate: startDate, endDate: endDate, statuses: statuses, - shownSides: - filterData?.shownSides?.where((e) => e != s).toList(), + shownSides: filterData?.shownSides + ?.where((e) => e != s) + .toList(), ), ), context, @@ -176,38 +188,42 @@ class DexListHeaderMobile extends StatelessWidget { ); } if (startDateString != null) { - children.add(_buildManageFilterItem( - LocaleKeys.fromDate.tr(), - startDateString, - () => onFilterDataChange( - TradingEntitiesFilter( - buyCoin: buyCoin, - sellCoin: sellCoin, - startDate: null, - endDate: endDate, - statuses: statuses, - shownSides: shownSides, + children.add( + _buildManageFilterItem( + LocaleKeys.fromDate.tr(), + startDateString, + () => onFilterDataChange( + TradingEntitiesFilter( + buyCoin: buyCoin, + sellCoin: sellCoin, + startDate: null, + endDate: endDate, + statuses: statuses, + shownSides: shownSides, + ), ), + context, ), - context, - )); + ); } if (endDateString != null) { - children.add(_buildManageFilterItem( - LocaleKeys.toDate.tr(), - endDateString, - () => onFilterDataChange( - TradingEntitiesFilter( - buyCoin: buyCoin, - sellCoin: sellCoin, - startDate: startDate, - endDate: null, - statuses: statuses, - shownSides: shownSides, + children.add( + _buildManageFilterItem( + LocaleKeys.toDate.tr(), + endDateString, + () => onFilterDataChange( + TradingEntitiesFilter( + buyCoin: buyCoin, + sellCoin: sellCoin, + startDate: startDate, + endDate: null, + statuses: statuses, + shownSides: shownSides, + ), ), + context, ), - context, - )); + ); } if (children.length > 1) { @@ -232,40 +248,48 @@ class DexListHeaderMobile extends StatelessWidget { ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - child: Row(children: [ - isFilterShown - ? Icon( - Icons.close, - color: Theme.of(context).textTheme.labelLarge?.color, - size: 14, - ) - : SvgPicture.asset( - '$assetsPath/ui_icons/filters.svg', - colorFilter: ColorFilter.mode( - Theme.of(context).textTheme.labelLarge?.color ?? - Colors.white, - BlendMode.srcIn, + child: Row( + children: [ + isFilterShown + ? Icon( + Icons.close, + color: Theme.of(context).textTheme.labelLarge?.color, + size: 14, + ) + : SvgPicture.asset( + '$assetsPath/ui_icons/filters.svg', + colorFilter: ColorFilter.mode( + Theme.of(context).textTheme.labelLarge?.color ?? + Colors.white, + BlendMode.srcIn, + ), + width: 14, ), - width: 14, + Padding( + padding: const EdgeInsets.only(left: 12.0), + child: Text( + isFilterShown + ? LocaleKeys.close.tr() + : LocaleKeys.filters.tr(), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, ), - Padding( - padding: const EdgeInsets.only(left: 12.0), - child: Text( - isFilterShown ? LocaleKeys.close.tr() : LocaleKeys.filters.tr(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, ), ), - ) - ]), + ], + ), ), ), ); } - Widget _buildManageFilterItem(String text, String value, - VoidCallback removeFilter, BuildContext context) { + Widget _buildManageFilterItem( + String text, + String value, + VoidCallback removeFilter, + BuildContext context, + ) { return Flexible( child: Padding( padding: const EdgeInsets.only(right: 6), diff --git a/lib/views/market_maker_bot/animated_bot_status_indicator.dart b/lib/views/market_maker_bot/animated_bot_status_indicator.dart index 5a6dba2fac..5098486320 100644 --- a/lib/views/market_maker_bot/animated_bot_status_indicator.dart +++ b/lib/views/market_maker_bot/animated_bot_status_indicator.dart @@ -5,9 +5,13 @@ import 'package:web_dex/generated/codegen_loader.g.dart'; class AnimatedBotStatusIndicator extends StatefulWidget { final MarketMakerBotStatus status; + final double widthThreshold; - const AnimatedBotStatusIndicator({Key? key, required this.status}) - : super(key: key); + const AnimatedBotStatusIndicator({ + super.key, + required this.status, + this.widthThreshold = 100, + }); @override State createState() => @@ -44,29 +48,38 @@ class _AnimatedBotStatusIndicatorState extends State @override Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - AnimatedBuilder( - animation: _controller, - builder: (_, child) { - return Container( - width: 16, - height: 16, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: _getStatusColor(widget.status) - .withValues(alpha: _getOpacity(widget.status)), + return LayoutBuilder( + builder: (context, constraints) { + final showText = constraints.maxWidth >= widget.widthThreshold; + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedBuilder( + animation: _controller, + builder: (_, child) { + return Container( + width: 16, + height: 16, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: _getStatusColor( + widget.status, + ).withValues(alpha: _getOpacity(widget.status)), + ), + ); + }, + ), + if (showText) ...[ + const SizedBox(width: 8), + Text( + widget.status.text, + style: Theme.of(context).textTheme.labelMedium, ), - ); - }, - ), - const SizedBox(width: 8), - Text( - widget.status.text, - style: Theme.of(context).textTheme.labelMedium, - ), - ], + ], + ], + ); + }, ); } diff --git a/lib/views/market_maker_bot/market_maker_bot_order_list.dart b/lib/views/market_maker_bot/market_maker_bot_order_list.dart index 5198d63377..f657734c4c 100644 --- a/lib/views/market_maker_bot/market_maker_bot_order_list.dart +++ b/lib/views/market_maker_bot/market_maker_bot_order_list.dart @@ -38,9 +38,9 @@ class _MarketMakerBotOrdersListState extends State { @override void initState() { - context - .read() - .add(const MarketMakerOrderListRequested(Duration(seconds: 3))); + context.read().add( + const MarketMakerOrderListRequested(Duration(seconds: 3)), + ); super.initState(); } @@ -53,9 +53,9 @@ class _MarketMakerBotOrdersListState extends State { @override void didUpdateWidget(MarketMakerBotOrdersList oldWidget) { if (oldWidget.entitiesFilterData != widget.entitiesFilterData) { - context - .read() - .add(MarketMakerOrderListFilterChanged(widget.entitiesFilterData)); + context.read().add( + MarketMakerOrderListFilterChanged(widget.entitiesFilterData), + ); } super.didUpdateWidget(oldWidget); } @@ -84,9 +84,7 @@ class _MarketMakerBotOrdersListState extends State { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ - AnimatedBotStatusIndicator( - status: botState.status, - ), + AnimatedBotStatusIndicator(status: botState.status), const SizedBox(width: 24), UiPrimaryButton( text: botState.isRunning @@ -95,12 +93,13 @@ class _MarketMakerBotOrdersListState extends State { width: 120, height: 32, textStyle: const TextStyle(fontSize: 12), - onPressed: botState.isUpdating || + onPressed: + botState.isUpdating || state.makerBotOrders.isEmpty ? null : botState.isRunning - ? _onStopBotPressed - : _onStartBotPressed, + ? _onStopBotPressed + : _onStartBotPressed, ), const SizedBox(width: 12), UiPrimaryButton( @@ -108,12 +107,14 @@ class _MarketMakerBotOrdersListState extends State { width: 120, height: 32, textStyle: const TextStyle(fontSize: 12), - onPressed: botState.isUpdating || + onPressed: + botState.isUpdating || !botState.isRunning || state.makerBotOrders.isEmpty ? null - : () => widget.onCancelAll - ?.call(state.makerBotOrders), + : () => widget.onCancelAll?.call( + state.makerBotOrders, + ), ), ], ), @@ -193,14 +194,14 @@ class _MarketMakerBotOrdersListState extends State { } void _onStartBotPressed() { - context - .read() - .add(const MarketMakerBotStartRequested()); + context.read().add( + const MarketMakerBotStartRequested(), + ); } void _onSortChange(SortData sortData) { - context - .read() - .add(MarketMakerOrderListSortChanged(sortData)); + context.read().add( + MarketMakerOrderListSortChanged(sortData), + ); } } diff --git a/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart b/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart index e837fad916..bcc6cf1cde 100644 --- a/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart +++ b/lib/views/market_maker_bot/market_maker_bot_tab_content_wrapper.dart @@ -1,10 +1,14 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:web_dex/bloc/dex_tab_bar/dex_tab_bar_bloc.dart'; import 'package:web_dex/bloc/market_maker_bot/market_maker_bot/market_maker_bot_bloc.dart'; +import 'package:web_dex/bloc/market_maker_bot/market_maker_order_list/market_maker_order_list_bloc.dart'; import 'package:web_dex/bloc/market_maker_bot/market_maker_order_list/trade_pair.dart'; import 'package:web_dex/bloc/market_maker_bot/market_maker_trade_form/market_maker_trade_form_bloc.dart'; import 'package:web_dex/common/screen.dart'; +import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/swap.dart'; import 'package:web_dex/model/trading_entities_filter.dart'; import 'package:web_dex/router/state/routing_state.dart'; @@ -13,6 +17,7 @@ import 'package:web_dex/views/dex/dex_list_filter/mobile/dex_list_filter_mobile. import 'package:web_dex/views/dex/dex_list_filter/mobile/dex_list_header_mobile.dart'; import 'package:web_dex/views/dex/entities_list/history/history_list.dart'; import 'package:web_dex/views/dex/entities_list/in_progress/in_progress_list.dart'; +import 'package:web_dex/views/market_maker_bot/animated_bot_status_indicator.dart'; import 'package:web_dex/views/market_maker_bot/market_maker_bot_form.dart'; import 'package:web_dex/views/market_maker_bot/market_maker_bot_order_list.dart'; import 'package:web_dex/views/market_maker_bot/market_maker_bot_tab_type.dart'; @@ -76,20 +81,13 @@ class _MarketMakerBotTabContentWrapperState void _setFilter(TradingEntitiesFilter? filter) { context.read().add( - FilterChanged( - tabType: widget.listType, - filter: filter, - ), - ); + FilterChanged(tabType: widget.listType, filter: filter), + ); } } class _SelectedTabContent extends StatelessWidget { - const _SelectedTabContent({ - this.filter, - required this.type, - super.key, - }); + const _SelectedTabContent({this.filter, required this.type, super.key}); // TODO: get the current filter and type from BLoC state final TradingEntitiesFilter? filter; @@ -104,10 +102,8 @@ class _SelectedTabContent extends StatelessWidget { return MarketMakerBotOrdersList( entitiesFilterData: filter, onEdit: (order) => _editTradingBotOrder(context, order), - onCancel: (order) => _deleteTradingBotOrders( - [order], - marketMakerBotBloc, - ), + onCancel: (order) => + _deleteTradingBotOrders([order], marketMakerBotBloc), onCancelAll: (orders) { _deleteTradingBotOrders(orders, marketMakerBotBloc); }, @@ -141,9 +137,9 @@ class _SelectedTabContent extends StatelessWidget { } void _editTradingBotOrder(BuildContext context, TradePair order) { - context - .read() - .add(MarketMakerTradeFormEditOrderRequested(order)); + context.read().add( + MarketMakerTradeFormEditOrderRequested(order), + ); context.read().add(const TabChanged(0)); } @@ -176,11 +172,7 @@ class _MobileWidget extends StatelessWidget { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Flexible( - child: child, - ), - ], + children: [Flexible(child: child)], ); } else { return Column( @@ -193,6 +185,9 @@ class _MobileWidget extends StatelessWidget { isFilterShown: isFilterShown, onFilterDataChange: onApplyFilter, onFilterPressed: onFilterTap, + centerWidget: type == MarketMakerBotTabType.orders + ? _buildBotControls(context) + : null, ), const SizedBox(height: 6), Flexible( @@ -208,6 +203,48 @@ class _MobileWidget extends StatelessWidget { ); } } + + Widget _buildBotControls(BuildContext context) { + return BlocBuilder( + builder: (context, botState) { + return BlocBuilder( + builder: (context, orderListState) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: AnimatedBotStatusIndicator( + status: botState.status, + widthThreshold: 100, + ), + ), + const Spacer(), + UiPrimaryButton( + text: botState.isRunning + ? LocaleKeys.mmBotStop.tr() + : LocaleKeys.mmBotStart.tr(), + width: 100, + height: 30, + textStyle: const TextStyle(fontSize: 12), + onPressed: + botState.isUpdating || + orderListState.makerBotOrders.isEmpty + ? null + : botState.isRunning + ? () => context.read().add( + const MarketMakerBotStopRequested(), + ) + : () => context.read().add( + const MarketMakerBotStartRequested(), + ), + ), + ], + ); + }, + ); + }, + ); + } } class _DesktopWidget extends StatelessWidget { @@ -229,9 +266,7 @@ class _DesktopWidget extends StatelessWidget { return Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Flexible(child: child), - ], + children: [Flexible(child: child)], ); } else { return Column(