refactor: migrate to riverpod 3

This commit is contained in:
cogwheel0
2025-09-21 22:31:44 +05:30
parent 37e5633c5c
commit 462bf4cde2
20 changed files with 834 additions and 453 deletions

View File

@@ -208,12 +208,29 @@ class AnimationService {
enum PageTransitionType { fade, slide, scale }
/// Provider for reduced motion preference
final reducedMotionProvider = StateProvider<bool>((ref) => false);
final reducedMotionProvider = NotifierProvider<ReducedMotionNotifier, bool>(
ReducedMotionNotifier.new,
);
/// Provider for animation performance settings
final animationPerformanceProvider = StateProvider<AnimationPerformance>((ref) {
return AnimationPerformance.adaptive;
});
final animationPerformanceProvider =
NotifierProvider<AnimationPerformanceNotifier, AnimationPerformance>(
AnimationPerformanceNotifier.new,
);
class ReducedMotionNotifier extends Notifier<bool> {
@override
bool build() => false;
void set(bool value) => state = value;
}
class AnimationPerformanceNotifier extends Notifier<AnimationPerformance> {
@override
AnimationPerformance build() => AnimationPerformance.adaptive;
void set(AnimationPerformance performance) => state = performance;
}
/// Animation performance levels
enum AnimationPerformance {
@@ -225,8 +242,8 @@ enum AnimationPerformance {
/// Provider for managing animation settings
final animationSettingsProvider =
StateNotifierProvider<AnimationSettingsNotifier, AnimationSettings>(
(ref) => AnimationSettingsNotifier(),
NotifierProvider<AnimationSettingsNotifier, AnimationSettings>(
AnimationSettingsNotifier.new,
);
class AnimationSettings {
@@ -253,8 +270,9 @@ class AnimationSettings {
}
}
class AnimationSettingsNotifier extends StateNotifier<AnimationSettings> {
AnimationSettingsNotifier() : super(const AnimationSettings());
class AnimationSettingsNotifier extends Notifier<AnimationSettings> {
@override
AnimationSettings build() => const AnimationSettings();
void setReduceMotion(bool reduce) {
state = state.copyWith(reduceMotion: reduce);

View File

@@ -20,9 +20,11 @@ class SettingsService {
static const String _voiceHoldToTalkKey = 'voice_hold_to_talk';
static const String _voiceAutoSendKey = 'voice_auto_send_final';
// Realtime transport preference
static const String _socketTransportModeKey = 'socket_transport_mode'; // 'auto' or 'ws'
static const String _socketTransportModeKey =
'socket_transport_mode'; // 'auto' or 'ws'
// Quick pill visibility selections (max 2)
static const String _quickPillsKey = 'quick_pills'; // StringList of identifiers e.g. ['web','image','tools']
static const String _quickPillsKey =
'quick_pills'; // StringList of identifiers e.g. ['web','image','tools']
// Chat input behavior
static const String _sendOnEnterKey = 'send_on_enter';
@@ -335,9 +337,14 @@ class AppSettings {
highContrast: highContrast ?? this.highContrast,
largeText: largeText ?? this.largeText,
darkMode: darkMode ?? this.darkMode,
defaultModel: defaultModel is _DefaultValue ? this.defaultModel : defaultModel as String?,
omitProviderInModelName: omitProviderInModelName ?? this.omitProviderInModelName,
voiceLocaleId: voiceLocaleId is _DefaultValue ? this.voiceLocaleId : voiceLocaleId as String?,
defaultModel: defaultModel is _DefaultValue
? this.defaultModel
: defaultModel as String?,
omitProviderInModelName:
omitProviderInModelName ?? this.omitProviderInModelName,
voiceLocaleId: voiceLocaleId is _DefaultValue
? this.voiceLocaleId
: voiceLocaleId as String?,
voiceHoldToTalk: voiceHoldToTalk ?? this.voiceHoldToTalk,
voiceAutoSendFinal: voiceAutoSendFinal ?? this.voiceAutoSendFinal,
socketTransportMode: socketTransportMode ?? this.socketTransportMode,
@@ -363,7 +370,7 @@ class AppSettings {
other.voiceAutoSendFinal == voiceAutoSendFinal &&
other.sendOnEnter == sendOnEnter &&
_listEquals(other.quickPills, quickPills);
// socketTransportMode intentionally not included in == to avoid frequent rebuilds
// socketTransportMode intentionally not included in == to avoid frequent rebuilds
}
@override
@@ -397,18 +404,27 @@ bool _listEquals(List<String> a, List<String> b) {
}
/// Provider for app settings
final appSettingsProvider =
StateNotifierProvider<AppSettingsNotifier, AppSettings>(
(ref) => AppSettingsNotifier(),
);
final appSettingsProvider = NotifierProvider<AppSettingsNotifier, AppSettings>(
AppSettingsNotifier.new,
);
class AppSettingsNotifier extends StateNotifier<AppSettings> {
AppSettingsNotifier() : super(const AppSettings()) {
_loadSettings();
class AppSettingsNotifier extends Notifier<AppSettings> {
bool _initialized = false;
@override
AppSettings build() {
if (!_initialized) {
_initialized = true;
Future.microtask(_loadSettings);
}
return const AppSettings();
}
Future<void> _loadSettings() async {
final settings = await SettingsService.loadSettings();
if (!ref.mounted) {
return;
}
state = settings;
}

View File

@@ -25,7 +25,17 @@ class SharedPayload {
}
/// Holds a pending shared payload until the app is ready (e.g., authed + model loaded)
final pendingSharedPayloadProvider = StateProvider<SharedPayload?>((_) => null);
final pendingSharedPayloadProvider =
NotifierProvider<PendingSharedPayloadNotifier, SharedPayload?>(
PendingSharedPayloadNotifier.new,
);
class PendingSharedPayloadNotifier extends Notifier<SharedPayload?> {
@override
SharedPayload? build() => null;
void set(SharedPayload? payload) => state = payload;
}
/// Initializes listening to OS share intents and handles them
final shareReceiverInitializerProvider = Provider<void>((ref) {
@@ -45,7 +55,7 @@ final shareReceiverInitializerProvider = Provider<void>((ref) {
model != null &&
isOnChatRoute) {
_processPayload(ref, pending);
ref.read(pendingSharedPayloadProvider.notifier).state = null;
ref.read(pendingSharedPayloadProvider.notifier).set(null);
}
}
@@ -70,7 +80,7 @@ final shareReceiverInitializerProvider = Provider<void>((ref) {
final dynamic media = await handler.getInitialSharedMedia();
final payload = _toPayload(media);
if (payload.hasAnything) {
ref.read(pendingSharedPayloadProvider.notifier).state = payload;
ref.read(pendingSharedPayloadProvider.notifier).set(payload);
maybeProcessPending();
}
} catch (e) {
@@ -83,7 +93,7 @@ final shareReceiverInitializerProvider = Provider<void>((ref) {
try {
final payload = _toPayload(media);
if (payload.hasAnything) {
ref.read(pendingSharedPayloadProvider.notifier).state = payload;
ref.read(pendingSharedPayloadProvider.notifier).set(payload);
maybeProcessPending();
}
} catch (e) {
@@ -178,10 +188,10 @@ Future<void> _processPayload(Ref ref, SharedPayload payload) async {
// Prefill text in the composer (do not auto-send) and request focus
final text = payload.text?.trim();
if (text != null && text.isNotEmpty) {
ref.read(prefilledInputTextProvider.notifier).state = text;
ref.read(prefilledInputTextProvider.notifier).set(text);
// Bump focus trigger to ensure input focuses after navigation/build
final current = ref.read(inputFocusTriggerProvider);
ref.read(inputFocusTriggerProvider.notifier).state = current + 1;
ref.read(inputFocusTriggerProvider.notifier).set(current + 1);
}
// Do NOT create a server chat here. The chat is created on first send
// (with server syncing + title generation) in chat_providers.dart.