feat: pull to refresh on chat drawer
This commit is contained in:
@@ -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)),
|
||||||
|
|||||||
Reference in New Issue
Block a user