feat: pull to refresh on chat drawer

This commit is contained in:
cogwheel0
2025-08-28 23:15:55 +05:30
parent f65cf33c59
commit 6dabd108f9

View File

@@ -41,6 +41,74 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
(ref) => {}, (ref) => {},
); );
Future<void> _refreshChats() async {
try {
// Always refresh folders
ref.invalidate(foldersProvider);
if (_query.trim().isEmpty) {
// Refresh main conversations list
ref.invalidate(conversationsProvider);
try {
await ref.read(conversationsProvider.future);
} catch (_) {}
} else {
// Refresh server-side search results
ref.invalidate(serverSearchProvider(_query));
try {
await ref.read(serverSearchProvider(_query).future);
} catch (_) {}
}
// Await folders as well so the list stabilizes
try {
await ref.read(foldersProvider.future);
} catch (_) {}
} catch (_) {}
}
Widget _buildRefreshableScrollable({required List<Widget> children}) {
// Common padding used in both scrollable variants
const padding = EdgeInsets.fromLTRB(
Spacing.md,
Spacing.sm,
Spacing.md,
Spacing.md,
);
if (Platform.isIOS) {
// Use Cupertino-style pull-to-refresh on iOS
final scroll = CustomScrollView(
controller: _listController,
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
const CupertinoSliverRefreshControl(),
SliverPadding(
padding: padding,
sliver: SliverList(
delegate: SliverChildListDelegate(children),
),
),
],
);
return CupertinoScrollbar(controller: _listController, child: scroll);
}
// Material pull-to-refresh elsewhere
return RefreshIndicator(
onRefresh: _refreshChats,
child: Scrollbar(
controller: _listController,
child: ListView(
controller: _listController,
physics: const AlwaysScrollableScrollPhysics(),
padding: padding,
children: children,
),
),
);
}
@override @override
void dispose() { void dispose() {
_debounce?.cancel(); _debounce?.cancel();
@@ -226,17 +294,7 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
final archived = list.where((c) => c.archived == true).toList(); final archived = list.where((c) => c.archived == true).toList();
return Scrollbar( final children = <Widget>[
controller: _listController,
child: ListView(
controller: _listController,
padding: const EdgeInsets.fromLTRB(
Spacing.md,
Spacing.sm,
Spacing.md,
Spacing.md,
),
children: [
if (pinned.isNotEmpty) ...[ if (pinned.isNotEmpty) ...[
_buildSectionHeader( _buildSectionHeader(
AppLocalizations.of(context)!.pinned, AppLocalizations.of(context)!.pinned,
@@ -312,9 +370,8 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
const SizedBox(height: Spacing.md), const SizedBox(height: Spacing.md),
_buildArchivedSection(archived), _buildArchivedSection(archived),
], ],
], ];
), return _buildRefreshableScrollable(children: children);
);
}, },
loading: () => loading: () =>
const Center(child: CircularProgressIndicator(strokeWidth: 2.0)), const Center(child: CircularProgressIndicator(strokeWidth: 2.0)),
@@ -377,17 +434,7 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
final archived = list.where((c) => c.archived == true).toList(); final archived = list.where((c) => c.archived == true).toList();
return Scrollbar( final children = <Widget>[
controller: _listController,
child: ListView(
controller: _listController,
padding: const EdgeInsets.fromLTRB(
Spacing.md,
Spacing.sm,
Spacing.md,
Spacing.md,
),
children: [
_buildSectionHeader('Results', list.length), _buildSectionHeader('Results', list.length),
const SizedBox(height: Spacing.xs), const SizedBox(height: Spacing.xs),
if (pinned.isNotEmpty) ...[ if (pinned.isNotEmpty) ...[
@@ -458,9 +505,8 @@ class _ChatsDrawerState extends ConsumerState<ChatsDrawer> {
const SizedBox(height: Spacing.md), const SizedBox(height: Spacing.md),
_buildArchivedSection(archived), _buildArchivedSection(archived),
], ],
], ];
), return _buildRefreshableScrollable(children: children);
);
}, },
loading: () => loading: () =>
const Center(child: CircularProgressIndicator(strokeWidth: 2.0)), const Center(child: CircularProgressIndicator(strokeWidth: 2.0)),