From 3545c75cd5ecea74663e6842fae5124b574482b6 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 28 Sep 2025 13:55:32 +0530 Subject: [PATCH] Refactor terminal and remove duplicate code. --- lib/consts.dart | 13 +- lib/models/models.dart | 1 - lib/models/terminal_models.dart | 164 ------------------ lib/providers/collection_providers.dart | 1 + lib/providers/terminal_providers.dart | 3 +- lib/screens/terminal/terminal_page.dart | 20 +-- lib/terminal/enums.dart | 8 + lib/terminal/models/body_chunk.dart | 7 + .../models}/js_log_data.dart | 0 .../terminal => terminal/models}/models.dart | 3 +- .../models}/network_log_data.dart | 11 +- .../models}/system_log_data.dart | 0 .../models}/terminal_entry.dart | 2 +- lib/terminal/terminal.dart | 3 + .../widgets/terminal_level_filter_menu.dart | 2 +- .../widgets/terminal_tiles.dart | 9 +- lib/terminal/widgets/widgets.dart | 2 + lib/widgets/empty_message.dart | 44 ----- ....dart => text_highlighted_selectable.dart} | 0 lib/widgets/text_simple.dart | 47 +++++ lib/widgets/widgets.dart | 4 +- 21 files changed, 91 insertions(+), 253 deletions(-) delete mode 100644 lib/models/terminal_models.dart create mode 100644 lib/terminal/enums.dart create mode 100644 lib/terminal/models/body_chunk.dart rename lib/{models/terminal => terminal/models}/js_log_data.dart (100%) rename lib/{models/terminal => terminal/models}/models.dart (82%) rename lib/{models/terminal => terminal/models}/network_log_data.dart (90%) rename lib/{models/terminal => terminal/models}/system_log_data.dart (100%) rename lib/{models/terminal => terminal/models}/terminal_entry.dart (97%) create mode 100644 lib/terminal/terminal.dart rename lib/{ => terminal}/widgets/terminal_level_filter_menu.dart (99%) rename lib/{ => terminal}/widgets/terminal_tiles.dart (98%) create mode 100644 lib/terminal/widgets/widgets.dart delete mode 100644 lib/widgets/empty_message.dart rename lib/widgets/{highlight_text.dart => text_highlighted_selectable.dart} (100%) create mode 100644 lib/widgets/text_simple.dart diff --git a/lib/consts.dart b/lib/consts.dart index cf8e5fe6..8ed97003 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -3,7 +3,6 @@ import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -// Terminal enums moved here from models to avoid circular imports and simplify usage const kDiscordUrl = "https://bit.ly/heyfoss"; const kGitUrl = "https://github.com/foss42/apidash"; @@ -86,15 +85,6 @@ enum HistoryRetentionPeriod { final IconData icon; } -/// Source category of a terminal entry -enum TerminalSource { network, js, system } - -/// Severity level of a terminal entry -enum TerminalLevel { debug, info, warn, error } - -/// Phase of a network log lifecycle -enum NetworkPhase { started, progress, completed, failed } - enum ItemMenuOption { edit("Rename"), delete("Delete"), @@ -521,3 +511,6 @@ const kMsgClearHistorySuccess = 'History cleared successfully'; const kMsgClearHistoryError = 'Error clearing history'; const kMsgShareError = "Unable to share"; const kLabelGenerateUI = "Generate UI"; +// Terminal Page +const kMsgNoLogs = 'No logs yet'; +const kMsgSendToView = 'Send a request to view its details in the console.'; diff --git a/lib/models/models.dart b/lib/models/models.dart index cfc7c58d..5d17479a 100644 --- a/lib/models/models.dart +++ b/lib/models/models.dart @@ -2,4 +2,3 @@ export 'history_meta_model.dart'; export 'history_request_model.dart'; export 'request_model.dart'; export 'settings_model.dart'; -export 'terminal/models.dart'; diff --git a/lib/models/terminal_models.dart b/lib/models/terminal_models.dart deleted file mode 100644 index 8c42874a..00000000 --- a/lib/models/terminal_models.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'package:apidash_core/apidash_core.dart' show APIType, HTTPVerb; - -/// Source category of a terminal entry -enum TerminalSource { network, js, system } - -/// Severity level of a terminal entry -enum TerminalLevel { debug, info, warn, error } - -/// Phase of a network log lifecycle -enum NetworkPhase { started, progress, completed, failed } - -class BodyChunk { - BodyChunk({required this.ts, required this.text, required this.sizeBytes}); - - final DateTime ts; - final String text; // preview text (could be partial) - final int sizeBytes; -} - -class NetworkLogData { - NetworkLogData({ - required this.phase, - required this.apiType, - required this.method, - required this.url, - this.requestHeaders, - this.requestBodyPreview, - this.responseStatus, - this.responseHeaders, - this.responseBodyPreview, - this.duration, - this.isStreaming = false, - this.sentAt, - this.completedAt, - this.errorMessage, - List? chunks, - }) : chunks = chunks ?? []; - - final NetworkPhase phase; - final APIType apiType; - final HTTPVerb method; - final String url; - final Map? requestHeaders; - final String? requestBodyPreview; - final int? responseStatus; - final Map? responseHeaders; - final String? responseBodyPreview; - final Duration? duration; - final bool isStreaming; - final DateTime? sentAt; - final DateTime? completedAt; - final String? errorMessage; - final List chunks; - - NetworkLogData copyWith({ - NetworkPhase? phase, - APIType? apiType, - HTTPVerb? method, - String? url, - Map? requestHeaders, - String? requestBodyPreview, - int? responseStatus, - Map? responseHeaders, - String? responseBodyPreview, - Duration? duration, - bool? isStreaming, - DateTime? sentAt, - DateTime? completedAt, - String? errorMessage, - List? chunks, - }) { - return NetworkLogData( - phase: phase ?? this.phase, - apiType: apiType ?? this.apiType, - method: method ?? this.method, - url: url ?? this.url, - requestHeaders: requestHeaders ?? this.requestHeaders, - requestBodyPreview: requestBodyPreview ?? this.requestBodyPreview, - responseStatus: responseStatus ?? this.responseStatus, - responseHeaders: responseHeaders ?? this.responseHeaders, - responseBodyPreview: responseBodyPreview ?? this.responseBodyPreview, - duration: duration ?? this.duration, - isStreaming: isStreaming ?? this.isStreaming, - sentAt: sentAt ?? this.sentAt, - completedAt: completedAt ?? this.completedAt, - errorMessage: errorMessage ?? this.errorMessage, - chunks: chunks ?? this.chunks, - ); - } -} - -class JsLogData { - JsLogData({ - required this.level, - required this.args, - this.stack, - this.context, - this.contextRequestId, - }); - - final String level; // log | warn | error | fatal - final List args; - final String? stack; - final String? context; // preRequest | postResponse | global - final String? contextRequestId; -} - -class SystemLogData { - SystemLogData({required this.category, required this.message, this.stack}); - final String category; // ui | provider | io | storage | unknown - final String message; - final String? stack; -} - -class TerminalEntry { - TerminalEntry({ - required this.id, - required this.ts, - required this.source, - required this.level, - this.requestId, - this.correlationId, - this.tags = const [], - this.network, - this.js, - this.system, - }); - - final String id; - final DateTime ts; - final TerminalSource source; - final TerminalLevel level; - final String? requestId; // App request id for correlation - final String? correlationId; // Additional correlation if any - final List tags; - final NetworkLogData? network; - final JsLogData? js; - final SystemLogData? system; - - TerminalEntry copyWith({ - DateTime? ts, - TerminalSource? source, - TerminalLevel? level, - String? requestId, - String? correlationId, - List? tags, - NetworkLogData? network, - JsLogData? js, - SystemLogData? system, - }) { - return TerminalEntry( - id: id, - ts: ts ?? this.ts, - source: source ?? this.source, - level: level ?? this.level, - requestId: requestId ?? this.requestId, - correlationId: correlationId ?? this.correlationId, - tags: tags ?? this.tags, - network: network ?? this.network, - js: js ?? this.js, - system: system ?? this.system, - ); - } -} diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index 83f97560..51de409b 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -3,6 +3,7 @@ import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/consts.dart'; +import 'package:apidash/terminal/terminal.dart'; import 'providers.dart'; import '../models/models.dart'; import '../services/services.dart'; diff --git a/lib/providers/terminal_providers.dart b/lib/providers/terminal_providers.dart index 5e6050d2..52980980 100644 --- a/lib/providers/terminal_providers.dart +++ b/lib/providers/terminal_providers.dart @@ -1,8 +1,7 @@ +import 'package:apidash/terminal/terminal.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:uuid/uuid.dart'; import 'package:apidash_core/apidash_core.dart'; -import '../models/terminal/models.dart'; -import '../consts.dart'; final terminalStateProvider = StateNotifierProvider((ref) { diff --git a/lib/screens/terminal/terminal_page.dart b/lib/screens/terminal/terminal_page.dart index 65488013..7cd1f29c 100644 --- a/lib/screens/terminal/terminal_page.dart +++ b/lib/screens/terminal/terminal_page.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../models/terminal/models.dart'; -import '../../consts.dart'; -import '../../providers/terminal_providers.dart'; -import '../../providers/collection_providers.dart'; -import '../../widgets/button_copy.dart'; -import '../../widgets/field_search.dart'; -import '../../widgets/terminal_tiles.dart'; -import '../../widgets/empty_message.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; -import '../../widgets/terminal_level_filter_menu.dart'; +import 'package:apidash/terminal/terminal.dart'; +import 'package:apidash/widgets/widgets.dart'; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/consts.dart'; class TerminalPage extends ConsumerStatefulWidget { const TerminalPage({super.key}); @@ -109,10 +104,9 @@ class _TerminalPageState extends ConsumerState { Expanded( child: filtered.isEmpty ? const Center( - child: EmptyMessage( - title: 'No logs yet', - subtitle: - 'Send a request to see details here in the console', + child: SimpleText( + title: kMsgNoLogs, + subtitle: kMsgSendToView, ), ) : ListView.separated( diff --git a/lib/terminal/enums.dart b/lib/terminal/enums.dart new file mode 100644 index 00000000..9fc5ee86 --- /dev/null +++ b/lib/terminal/enums.dart @@ -0,0 +1,8 @@ +/// Source category of a terminal entry +enum TerminalSource { network, js, system } + +/// Severity level of a terminal entry +enum TerminalLevel { debug, info, warn, error } + +/// Phase of a network log lifecycle +enum NetworkPhase { started, progress, completed, failed } diff --git a/lib/terminal/models/body_chunk.dart b/lib/terminal/models/body_chunk.dart new file mode 100644 index 00000000..e890196d --- /dev/null +++ b/lib/terminal/models/body_chunk.dart @@ -0,0 +1,7 @@ +class BodyChunk { + BodyChunk({required this.ts, required this.text, required this.sizeBytes}); + + final DateTime ts; + final String text; // preview text (could be partial) + final int sizeBytes; +} diff --git a/lib/models/terminal/js_log_data.dart b/lib/terminal/models/js_log_data.dart similarity index 100% rename from lib/models/terminal/js_log_data.dart rename to lib/terminal/models/js_log_data.dart diff --git a/lib/models/terminal/models.dart b/lib/terminal/models/models.dart similarity index 82% rename from lib/models/terminal/models.dart rename to lib/terminal/models/models.dart index 82c1977b..9e579082 100644 --- a/lib/models/terminal/models.dart +++ b/lib/terminal/models/models.dart @@ -1,4 +1,5 @@ -export 'network_log_data.dart'; +export 'body_chunk.dart'; export 'js_log_data.dart'; +export 'network_log_data.dart'; export 'system_log_data.dart'; export 'terminal_entry.dart'; diff --git a/lib/models/terminal/network_log_data.dart b/lib/terminal/models/network_log_data.dart similarity index 90% rename from lib/models/terminal/network_log_data.dart rename to lib/terminal/models/network_log_data.dart index ae6e55b1..1ec99472 100644 --- a/lib/models/terminal/network_log_data.dart +++ b/lib/terminal/models/network_log_data.dart @@ -1,5 +1,6 @@ -import 'package:apidash/consts.dart'; import 'package:apidash_core/apidash_core.dart' show APIType, HTTPVerb; +import '../enums.dart'; +import 'body_chunk.dart'; class NetworkLogData { NetworkLogData({ @@ -72,11 +73,3 @@ class NetworkLogData { ); } } - -class BodyChunk { - BodyChunk({required this.ts, required this.text, required this.sizeBytes}); - - final DateTime ts; - final String text; // preview text (could be partial) - final int sizeBytes; -} diff --git a/lib/models/terminal/system_log_data.dart b/lib/terminal/models/system_log_data.dart similarity index 100% rename from lib/models/terminal/system_log_data.dart rename to lib/terminal/models/system_log_data.dart diff --git a/lib/models/terminal/terminal_entry.dart b/lib/terminal/models/terminal_entry.dart similarity index 97% rename from lib/models/terminal/terminal_entry.dart rename to lib/terminal/models/terminal_entry.dart index 8c1a8e74..4fae9eb4 100644 --- a/lib/models/terminal/terminal_entry.dart +++ b/lib/terminal/models/terminal_entry.dart @@ -1,4 +1,4 @@ -import 'package:apidash/consts.dart'; +import '../enums.dart'; import 'network_log_data.dart'; import 'js_log_data.dart'; import 'system_log_data.dart'; diff --git a/lib/terminal/terminal.dart b/lib/terminal/terminal.dart new file mode 100644 index 00000000..87fa1ea2 --- /dev/null +++ b/lib/terminal/terminal.dart @@ -0,0 +1,3 @@ +export 'models/models.dart'; +export 'widgets/widgets.dart'; +export 'enums.dart'; diff --git a/lib/widgets/terminal_level_filter_menu.dart b/lib/terminal/widgets/terminal_level_filter_menu.dart similarity index 99% rename from lib/widgets/terminal_level_filter_menu.dart rename to lib/terminal/widgets/terminal_level_filter_menu.dart index bb232178..7e8b8151 100644 --- a/lib/widgets/terminal_level_filter_menu.dart +++ b/lib/terminal/widgets/terminal_level_filter_menu.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import '../consts.dart'; +import '../enums.dart'; class TerminalLevelFilterMenu extends StatelessWidget { const TerminalLevelFilterMenu({ diff --git a/lib/widgets/terminal_tiles.dart b/lib/terminal/widgets/terminal_tiles.dart similarity index 98% rename from lib/widgets/terminal_tiles.dart rename to lib/terminal/widgets/terminal_tiles.dart index cc58616f..0a93ddb3 100644 --- a/lib/widgets/terminal_tiles.dart +++ b/lib/terminal/widgets/terminal_tiles.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash_core/apidash_core.dart'; -import '../consts.dart'; -import '../models/terminal/models.dart'; -import '../utils/ui_utils.dart'; -import 'expandable_section.dart'; -import 'highlight_text.dart'; +import 'package:apidash/utils/utils.dart'; +import 'package:apidash/widgets/widgets.dart'; +import '../enums.dart'; +import '../models/models.dart'; class SystemLogTile extends StatelessWidget { const SystemLogTile({ diff --git a/lib/terminal/widgets/widgets.dart b/lib/terminal/widgets/widgets.dart new file mode 100644 index 00000000..bcdb92d4 --- /dev/null +++ b/lib/terminal/widgets/widgets.dart @@ -0,0 +1,2 @@ +export 'terminal_level_filter_menu.dart'; +export 'terminal_tiles.dart'; diff --git a/lib/widgets/empty_message.dart b/lib/widgets/empty_message.dart deleted file mode 100644 index cdcb3c34..00000000 --- a/lib/widgets/empty_message.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; - -class EmptyMessage extends StatelessWidget { - const EmptyMessage({ - super.key, - this.title = 'No logs yet', - this.subtitle = 'Send a request to view its details in the console.', - this.icon, - }); - - final String title; - final String subtitle; - final IconData? icon; - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (icon != null) ...[ - Icon(icon, size: 36, color: theme.colorScheme.outline), - const SizedBox(height: 8), - ], - Text( - title, - style: theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - ), - const SizedBox(height: 6), - Text( - subtitle, - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.outline, - ), - textAlign: TextAlign.center, - ), - ], - ), - ); - } -} diff --git a/lib/widgets/highlight_text.dart b/lib/widgets/text_highlighted_selectable.dart similarity index 100% rename from lib/widgets/highlight_text.dart rename to lib/widgets/text_highlighted_selectable.dart diff --git a/lib/widgets/text_simple.dart b/lib/widgets/text_simple.dart new file mode 100644 index 00000000..104a7622 --- /dev/null +++ b/lib/widgets/text_simple.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class SimpleText extends StatelessWidget { + const SimpleText({ + super.key, + this.title, + this.subtitle, + this.icon, + }); + + final String? title; + final String? subtitle; + final IconData? icon; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (icon != null) ...[ + Icon(icon, size: 36, color: theme.colorScheme.outline), + const SizedBox(height: 8), + ], + if (title != null) ...[ + Text( + title!, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 6), + ], + if (subtitle != null) + Text( + subtitle!, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.outline, + ), + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index e48f8517..e4e9c049 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -35,8 +35,6 @@ export 'field_read_only.dart'; export 'field_text_bounded.dart'; export 'field_search.dart'; export 'expandable_section.dart'; -export 'empty_message.dart'; -export 'terminal_tiles.dart'; export 'field_url.dart'; export 'intro_message.dart'; export 'markdown.dart'; @@ -71,6 +69,8 @@ export 'table_map.dart'; export 'table_request_form.dart'; export 'table_request.dart'; export 'tab_label.dart'; +export 'text_highlighted_selectable.dart'; +export 'text_simple.dart'; export 'texts.dart'; export 'uint8_audio_player.dart'; export 'widget_not_sent.dart';