refactor: remove offline indicator and streamline chat page logic
- Deleted the OfflineIndicator widget and its associated logic to simplify the codebase. - Removed offline checks from the ChatPage, enhancing performance and reducing unnecessary rebuilds. - Adjusted the ChatsDrawer to change icon size for better visual consistency. - Cleaned up unused imports related to offline handling across multiple files.
This commit is contained in:
@@ -29,8 +29,6 @@ import 'package:path/path.dart' as path;
|
|||||||
import '../../../shared/services/tasks/task_queue.dart';
|
import '../../../shared/services/tasks/task_queue.dart';
|
||||||
import '../../tools/providers/tools_providers.dart';
|
import '../../tools/providers/tools_providers.dart';
|
||||||
import '../../navigation/widgets/chats_drawer.dart';
|
import '../../navigation/widgets/chats_drawer.dart';
|
||||||
import '../../../shared/widgets/offline_indicator.dart';
|
|
||||||
import '../../../core/services/connectivity_service.dart';
|
|
||||||
import '../../../core/models/chat_message.dart';
|
import '../../../core/models/chat_message.dart';
|
||||||
import '../../../core/models/model.dart';
|
import '../../../core/models/model.dart';
|
||||||
import '../../../shared/widgets/loading_states.dart';
|
import '../../../shared/widgets/loading_states.dart';
|
||||||
@@ -334,13 +332,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
|||||||
if (selectedModel == null) return;
|
if (selectedModel == null) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isOnline = ref.read(isOnlineProvider);
|
|
||||||
final isReviewerMode = ref.read(reviewerModeProvider);
|
|
||||||
|
|
||||||
if (!isOnline && !isReviewerMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get attached files and collect uploaded file IDs (including data URLs for images)
|
// Get attached files and collect uploaded file IDs (including data URLs for images)
|
||||||
final attachedFiles = ref.read(attachedFilesProvider);
|
final attachedFiles = ref.read(attachedFilesProvider);
|
||||||
@@ -1039,8 +1030,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
// Use select to watch only connectivity status to reduce rebuilds
|
|
||||||
final isOnline = ref.watch(isOnlineProvider.select((status) => status));
|
|
||||||
// Use select to watch only the selected model to reduce rebuilds
|
// Use select to watch only the selected model to reduce rebuilds
|
||||||
final selectedModel = ref.watch(
|
final selectedModel = ref.watch(
|
||||||
selectedModelProvider.select((model) => model),
|
selectedModelProvider.select((model) => model),
|
||||||
@@ -1537,9 +1526,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
|||||||
// File attachments
|
// File attachments
|
||||||
const FileAttachmentWidget(),
|
const FileAttachmentWidget(),
|
||||||
|
|
||||||
// Offline indicator
|
|
||||||
const ChatOfflineOverlay(),
|
|
||||||
|
|
||||||
// Modern Input (root matches input background including safe area)
|
// Modern Input (root matches input background including safe area)
|
||||||
RepaintBoundary(
|
RepaintBoundary(
|
||||||
child: MeasureSize(
|
child: MeasureSize(
|
||||||
@@ -1551,8 +1537,6 @@ class _ChatPageState extends ConsumerState<ChatPage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ModernChatInput(
|
child: ModernChatInput(
|
||||||
enabled:
|
|
||||||
(isOnline || ref.watch(reviewerModeProvider)),
|
|
||||||
onSendMessage: (text) =>
|
onSendMessage: (text) =>
|
||||||
_handleMessageSend(text, selectedModel),
|
_handleMessageSend(text, selectedModel),
|
||||||
onVoiceInput: null,
|
onVoiceInput: null,
|
||||||
|
|||||||
@@ -1406,7 +1406,7 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
|
|||||||
? CupertinoIcons.settings
|
? CupertinoIcons.settings
|
||||||
: Icons.settings_rounded,
|
: Icons.settings_rounded,
|
||||||
color: theme.iconSecondary,
|
color: theme.iconSecondary,
|
||||||
size: IconSize.small,
|
size: IconSize.medium,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import 'core/persistence/persistence_migrator.dart';
|
|||||||
import 'core/persistence/persistence_providers.dart';
|
import 'core/persistence/persistence_providers.dart';
|
||||||
import 'core/services/self_signed_certificate_manager.dart';
|
import 'core/services/self_signed_certificate_manager.dart';
|
||||||
import 'core/router/app_router.dart';
|
import 'core/router/app_router.dart';
|
||||||
import 'shared/widgets/offline_indicator.dart';
|
|
||||||
import 'features/auth/providers/unified_auth_providers.dart';
|
import 'features/auth/providers/unified_auth_providers.dart';
|
||||||
import 'core/auth/auth_state_manager.dart';
|
import 'core/auth/auth_state_manager.dart';
|
||||||
import 'core/utils/debug_logger.dart';
|
import 'core/utils/debug_logger.dart';
|
||||||
@@ -218,7 +217,7 @@ class _ConduitAppState extends ConsumerState<ConduitApp> {
|
|||||||
maxScaleFactor: 3.0,
|
maxScaleFactor: 3.0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: OfflineIndicator(child: child ?? const SizedBox.shrink()),
|
child: child ?? const SizedBox.shrink(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,269 +0,0 @@
|
|||||||
import 'dart:io' show Platform;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
|
||||||
|
|
||||||
import '../../core/services/connectivity_service.dart';
|
|
||||||
import '../../core/providers/app_providers.dart';
|
|
||||||
import '../theme/theme_extensions.dart';
|
|
||||||
import 'package:conduit/l10n/app_localizations.dart';
|
|
||||||
|
|
||||||
part 'offline_indicator.g.dart';
|
|
||||||
|
|
||||||
class OfflineIndicator extends ConsumerWidget {
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
const OfflineIndicator({super.key, required this.child});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final connectivityStatus = ref.watch(connectivityStatusProvider);
|
|
||||||
final socketConnection = ref.watch(socketConnectionStreamProvider);
|
|
||||||
final wasOffline = ref.watch(_wasOfflineProvider);
|
|
||||||
final socketOffline = socketConnection.maybeWhen(
|
|
||||||
data: (state) => state == SocketConnectionState.disconnected,
|
|
||||||
orElse: () => false,
|
|
||||||
);
|
|
||||||
|
|
||||||
final overlay = () {
|
|
||||||
if ((connectivityStatus == ConnectivityStatus.offline || socketOffline) &&
|
|
||||||
!wasOffline) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
if (wasOffline) {
|
|
||||||
return const _BackOnlineToast();
|
|
||||||
}
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}();
|
|
||||||
|
|
||||||
return Stack(children: [child, overlay]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracks if the app was recently offline to enable a one-shot back-online toast
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
class _WasOffline extends _$WasOffline {
|
|
||||||
@override
|
|
||||||
bool build() {
|
|
||||||
// Initialize based on current connectivity (assume online until proven otherwise)
|
|
||||||
ref.listen<ConnectivityStatus>(connectivityStatusProvider, (
|
|
||||||
prev,
|
|
||||||
next,
|
|
||||||
) {
|
|
||||||
if (next == ConnectivityStatus.offline) {
|
|
||||||
state = true; // mark that we have been offline
|
|
||||||
} else if (next == ConnectivityStatus.online && state) {
|
|
||||||
// After we emit the toast once, clear flag shortly after
|
|
||||||
Future.microtask(() => state = false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BackOnlineToast extends StatelessWidget {
|
|
||||||
const _BackOnlineToast();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Positioned(
|
|
||||||
top: kToolbarHeight + 8,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
child: SafeArea(
|
|
||||||
bottom: false,
|
|
||||||
child: Semantics(
|
|
||||||
container: true,
|
|
||||||
liveRegion: true,
|
|
||||||
label: AppLocalizations.of(context)!.checkConnection,
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.topCenter,
|
|
||||||
child:
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.symmetric(
|
|
||||||
horizontal: Spacing.md,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: Spacing.md,
|
|
||||||
vertical: Spacing.xs,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: context.conduitTheme.success,
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
AppBorderRadius.round,
|
|
||||||
),
|
|
||||||
boxShadow: ConduitShadows.low(context),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
// Reuse existing l10n; otherwise add a dedicated "Back online" key later
|
|
||||||
AppLocalizations.of(context)!.loadingContent,
|
|
||||||
style: TextStyle(
|
|
||||||
color: context.conduitTheme.textInverse,
|
|
||||||
fontSize: AppTypography.labelLarge,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.animate(onPlay: (c) => c.forward())
|
|
||||||
.fadeIn(duration: const Duration(milliseconds: 200))
|
|
||||||
.then(delay: const Duration(milliseconds: 1200))
|
|
||||||
.fadeOut(duration: const Duration(milliseconds: 250)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inline offline indicator for specific features
|
|
||||||
class InlineOfflineIndicator extends ConsumerWidget {
|
|
||||||
final String message;
|
|
||||||
final IconData? icon;
|
|
||||||
final Color? backgroundColor;
|
|
||||||
|
|
||||||
const InlineOfflineIndicator({
|
|
||||||
super.key,
|
|
||||||
this.message = '',
|
|
||||||
this.icon,
|
|
||||||
this.backgroundColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final isOnline = ref.watch(isOnlineProvider);
|
|
||||||
|
|
||||||
if (isOnline) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
final theme = context.conduitTheme;
|
|
||||||
final surfaceColor = backgroundColor ?? theme.warningBackground;
|
|
||||||
final borderAlpha = Theme.of(context).brightness == Brightness.dark
|
|
||||||
? 0.45
|
|
||||||
: 0.3;
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.all(Spacing.md),
|
|
||||||
padding: const EdgeInsets.all(Spacing.md),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: surfaceColor,
|
|
||||||
borderRadius: BorderRadius.circular(AppBorderRadius.md),
|
|
||||||
border: Border.all(
|
|
||||||
color: theme.warning.withValues(alpha: borderAlpha),
|
|
||||||
width: BorderWidth.regular,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon ??
|
|
||||||
(Platform.isIOS ? CupertinoIcons.wifi_slash : Icons.wifi_off),
|
|
||||||
color: theme.warning,
|
|
||||||
size: Spacing.lg,
|
|
||||||
),
|
|
||||||
const SizedBox(width: Spacing.xs),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
message.isNotEmpty
|
|
||||||
? message
|
|
||||||
: AppLocalizations.of(context)!.featureRequiresInternet,
|
|
||||||
style: TextStyle(
|
|
||||||
color: theme.warning,
|
|
||||||
fontSize: AppTypography.labelLarge,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
).animate().fadeIn(duration: const Duration(milliseconds: 300));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Offline-aware button that disables when offline
|
|
||||||
class OfflineAwareButton extends ConsumerWidget {
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
final Widget child;
|
|
||||||
final bool requiresConnection;
|
|
||||||
final String? offlineTooltip;
|
|
||||||
|
|
||||||
const OfflineAwareButton({
|
|
||||||
super.key,
|
|
||||||
required this.onPressed,
|
|
||||||
required this.child,
|
|
||||||
this.requiresConnection = true,
|
|
||||||
this.offlineTooltip,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final isOnline = ref.watch(isOnlineProvider);
|
|
||||||
final enabled = !requiresConnection || isOnline;
|
|
||||||
|
|
||||||
return Tooltip(
|
|
||||||
message: !enabled
|
|
||||||
? (offlineTooltip ??
|
|
||||||
AppLocalizations.of(context)!.featureRequiresInternet)
|
|
||||||
: '',
|
|
||||||
child: FilledButton(onPressed: enabled ? onPressed : null, child: child),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chat-specific offline indicator
|
|
||||||
class ChatOfflineOverlay extends ConsumerWidget {
|
|
||||||
const ChatOfflineOverlay({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final isOnline = ref.watch(isOnlineProvider);
|
|
||||||
|
|
||||||
if (isOnline) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
|
|
||||||
final theme = context.conduitTheme;
|
|
||||||
final surfaceColor = theme.warningBackground;
|
|
||||||
final borderAlpha = Theme.of(context).brightness == Brightness.dark
|
|
||||||
? 0.5
|
|
||||||
: 0.35;
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: Spacing.md,
|
|
||||||
vertical: Spacing.sm,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: surfaceColor,
|
|
||||||
border: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
color: theme.warning.withValues(alpha: borderAlpha),
|
|
||||||
width: BorderWidth.regular,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Platform.isIOS ? CupertinoIcons.wifi_slash : Icons.wifi_off,
|
|
||||||
color: theme.warning,
|
|
||||||
size: Spacing.md,
|
|
||||||
),
|
|
||||||
const SizedBox(width: Spacing.sm),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.messagesWillSendWhenOnline,
|
|
||||||
style: TextStyle(
|
|
||||||
color: theme.warning,
|
|
||||||
fontSize: AppTypography.bodySmall,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
).animate().fadeIn(duration: const Duration(milliseconds: 300));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user