feat: add web search availability provider and enhance feature toggles in chat and tools modal

This commit is contained in:
cogwheel0
2025-08-24 20:55:51 +05:30
parent cc46799e20
commit 4cd00e9193
5 changed files with 77 additions and 47 deletions

View File

@@ -80,12 +80,10 @@ class ThemeModeNotifier extends StateNotifier<ThemeMode> {
} }
// Locale provider // Locale provider
final localeProvider = StateNotifierProvider<LocaleNotifier, Locale?>( final localeProvider = StateNotifierProvider<LocaleNotifier, Locale?>((ref) {
(ref) { final storage = ref.watch(optimizedStorageServiceProvider);
final storage = ref.watch(optimizedStorageServiceProvider); return LocaleNotifier(storage);
return LocaleNotifier(storage); });
},
);
class LocaleNotifier extends StateNotifier<Locale?> { class LocaleNotifier extends StateNotifier<Locale?> {
final OptimizedStorageService _storage; final OptimizedStorageService _storage;
@@ -957,6 +955,22 @@ final imageGenerationAvailableProvider = Provider<bool>((ref) {
); );
}); });
final webSearchAvailableProvider = Provider<bool>((ref) {
final perms = ref.watch(userPermissionsProvider);
return perms.maybeWhen(
data: (data) {
final features = data['features'];
if (features is Map<String, dynamic>) {
final value = features['web_search'];
if (value is bool) return value;
if (value is String) return value.toLowerCase() == 'true';
}
return false;
},
orElse: () => false,
);
});
// Folders provider // Folders provider
final foldersProvider = FutureProvider<List<Folder>>((ref) async { final foldersProvider = FutureProvider<List<Folder>>((ref) async {
final api = ref.watch(apiServiceProvider); final api = ref.watch(apiServiceProvider);

View File

@@ -657,8 +657,10 @@ Future<void> _sendMessageInternal(
} }
} }
// Check feature toggles for API // Check feature toggles for API (gated by server availability)
final webSearchEnabled = ref.read(webSearchEnabledProvider); final webSearchEnabled =
ref.read(webSearchEnabledProvider) &&
ref.read(webSearchAvailableProvider);
final imageGenerationEnabled = ref.read(imageGenerationEnabledProvider); final imageGenerationEnabled = ref.read(imageGenerationEnabledProvider);
// Prepare tools list - pass tool IDs directly // Prepare tools list - pass tool IDs directly

View File

@@ -65,7 +65,12 @@ class _EnhancedImageAttachmentState
parent: _animationController, parent: _animationController,
curve: Curves.easeInOut, curve: Curves.easeInOut,
); );
_loadImage(); // Defer loading until after first frame to avoid accessing inherited widgets
// (e.g., Localizations) during initState
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
_loadImage();
});
} }
@override @override

View File

@@ -382,14 +382,13 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
)!.addAttachment, )!.addAttachment,
), ),
const SizedBox(width: Spacing.sm), const SizedBox(width: Spacing.sm),
// Quick pills: wrap in horizontal scroller to prevent overflow // Quick pills: no scroll, clip text within fixed max width
Expanded( Expanded(
child: SingleChildScrollView( child: Row(
scrollDirection: Axis.horizontal, children: [
physics: const BouncingScrollPhysics(), Flexible(
child: Row( fit: FlexFit.loose,
children: [ child: _buildPillButton(
_buildPillButton(
icon: Platform.isIOS icon: Platform.isIOS
? CupertinoIcons.search ? CupertinoIcons.search
: Icons.search, : Icons.search,
@@ -409,9 +408,12 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
} }
: null, : null,
), ),
if (imageGenAvailable) ...[ ),
const SizedBox(width: Spacing.sm), if (imageGenAvailable) ...[
_buildPillButton( const SizedBox(width: Spacing.sm),
Flexible(
fit: FlexFit.loose,
child: _buildPillButton(
icon: Platform.isIOS icon: Platform.isIOS
? CupertinoIcons.photo ? CupertinoIcons.photo
: Icons.image, : Icons.image,
@@ -431,9 +433,9 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
} }
: null, : null,
), ),
], ),
], ],
), ],
), ),
), ),
const SizedBox(width: Spacing.sm), const SizedBox(width: Spacing.sm),
@@ -705,20 +707,25 @@ class _ModernChatInputState extends ConsumerState<ModernChatInput>
alignment: Alignment.center, alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: Spacing.md), padding: const EdgeInsets.symmetric(horizontal: Spacing.md),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isActive // Align with unified tools modal: keep subtle card background even when active
? context.conduitTheme.buttonPrimary color: context.conduitTheme.cardBackground,
: context.conduitTheme.cardBackground,
borderRadius: BorderRadius.circular(AppBorderRadius.xl), borderRadius: BorderRadius.circular(AppBorderRadius.xl),
// Reduce perceived height variance: only show shadow when active // No elevation to match modal chips
boxShadow: isActive ? ConduitShadows.button : null, boxShadow: null,
), ),
child: Center( child: Center(
child: Text( child: ConstrainedBox(
label, constraints: const BoxConstraints(maxWidth: 140),
style: AppTypography.labelStyle.copyWith( child: Text(
color: isActive label,
? context.conduitTheme.buttonPrimaryText maxLines: 1,
: context.conduitTheme.textPrimary, overflow: TextOverflow.ellipsis,
softWrap: false,
style: AppTypography.labelStyle.copyWith(
color: isActive
? context.conduitTheme.buttonPrimary
: context.conduitTheme.textPrimary,
),
), ),
), ),
), ),

View File

@@ -25,6 +25,7 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
final webSearchEnabled = ref.watch(webSearchEnabledProvider); final webSearchEnabled = ref.watch(webSearchEnabledProvider);
final imageGenEnabled = ref.watch(imageGenerationEnabledProvider); final imageGenEnabled = ref.watch(imageGenerationEnabledProvider);
final imageGenAvailable = ref.watch(imageGenerationAvailableProvider); final imageGenAvailable = ref.watch(imageGenerationAvailableProvider);
final webSearchAvailable = ref.watch(webSearchAvailableProvider);
final selectedToolIds = ref.watch(selectedToolIdsProvider); final selectedToolIds = ref.watch(selectedToolIdsProvider);
final toolsAsync = ref.watch(toolsListProvider); final toolsAsync = ref.watch(toolsListProvider);
@@ -59,21 +60,22 @@ class _UnifiedToolsModalState extends ConsumerState<UnifiedToolsModal> {
// Full tiles for Web and Image features // Full tiles for Web and Image features
Column( Column(
children: [ children: [
_buildFeatureTile( if (webSearchAvailable)
title: AppLocalizations.of(context)!.webSearch, _buildFeatureTile(
description: AppLocalizations.of( title: AppLocalizations.of(context)!.webSearch,
context, description: AppLocalizations.of(
)!.webSearchDescription, context,
icon: Platform.isIOS )!.webSearchDescription,
? CupertinoIcons.search icon: Platform.isIOS
: Icons.search, ? CupertinoIcons.search
isActive: webSearchEnabled, : Icons.search,
onTap: () { isActive: webSearchEnabled,
HapticFeedback.lightImpact(); onTap: () {
ref.read(webSearchEnabledProvider.notifier).state = HapticFeedback.lightImpact();
!webSearchEnabled; ref.read(webSearchEnabledProvider.notifier).state =
}, !webSearchEnabled;
), },
),
if (imageGenAvailable) if (imageGenAvailable)
_buildFeatureTile( _buildFeatureTile(
title: AppLocalizations.of(context)!.imageGeneration, title: AppLocalizations.of(context)!.imageGeneration,