Improve message list visuals

This commit is contained in:
Nutcake 2023-05-25 20:19:03 +02:00
parent c43a93d574
commit c368fb6fe5
3 changed files with 71 additions and 42 deletions

View file

@ -72,6 +72,7 @@ class MessagingClient extends ChangeNotifier {
box.delete(_lastUpdateKey); box.delete(_lastUpdateKey);
await refreshFriendsListWithErrorHandler(); await refreshFriendsListWithErrorHandler();
await _refreshUnreads(); await _refreshUnreads();
_unreadSafeguard = Timer.periodic(_unreadSafeguardDuration, (timer) => _refreshUnreads());
}); });
_startWebsocket(); _startWebsocket();
_notifyOnlineTimer = Timer.periodic(const Duration(seconds: 60), (timer) async { _notifyOnlineTimer = Timer.periodic(const Duration(seconds: 60), (timer) async {
@ -85,6 +86,7 @@ class MessagingClient extends ChangeNotifier {
void dispose() { void dispose() {
_autoRefresh?.cancel(); _autoRefresh?.cancel();
_notifyOnlineTimer?.cancel(); _notifyOnlineTimer?.cancel();
_unreadSafeguard?.cancel();
_wsChannel?.close(); _wsChannel?.close();
super.dispose(); super.dispose();
} }
@ -217,12 +219,10 @@ class MessagingClient extends ChangeNotifier {
} }
Future<void> _refreshUnreads() async { Future<void> _refreshUnreads() async {
_unreadSafeguard?.cancel();
try { try {
final unreadMessages = await MessageApi.getUserMessages(_apiClient, unreadOnly: true); final unreadMessages = await MessageApi.getUserMessages(_apiClient, unreadOnly: true);
updateAllUnreads(unreadMessages.toList()); updateAllUnreads(unreadMessages.toList());
} catch (_) {} } catch (_) {}
_unreadSafeguard = Timer(_unreadSafeguardDuration, _refreshUnreads);
} }
void _sortFriendsCache() { void _sortFriendsCache() {

View file

@ -252,6 +252,7 @@ class _FriendsListState extends State<FriendsList> {
friends.sort((a, b) => a.username.length.compareTo(b.username.length)); friends.sort((a, b) => a.username.length.compareTo(b.username.length));
} }
return ListView.builder( return ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: friends.length, itemCount: friends.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final friend = friends[index]; final friend = friends[index];

View file

@ -1,4 +1,3 @@
import 'package:contacts_plus_plus/auxiliary.dart';
import 'package:contacts_plus_plus/clients/audio_cache_client.dart'; import 'package:contacts_plus_plus/clients/audio_cache_client.dart';
import 'package:contacts_plus_plus/clients/messaging_client.dart'; import 'package:contacts_plus_plus/clients/messaging_client.dart';
import 'package:contacts_plus_plus/models/friend.dart'; import 'package:contacts_plus_plus/models/friend.dart';
@ -24,6 +23,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
final ScrollController _sessionListScrollController = ScrollController(); final ScrollController _sessionListScrollController = ScrollController();
bool _showSessionListScrollChevron = false; bool _showSessionListScrollChevron = false;
bool _sessionListOpen = false;
double get _shevronOpacity => _showSessionListScrollChevron ? 1.0 : 0.0; double get _shevronOpacity => _showSessionListScrollChevron ? 1.0 : 0.0;
@ -51,7 +51,6 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
}); });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sessions = widget.friend.userStatus.activeSessions; final sessions = widget.friend.userStatus.activeSessions;
@ -81,50 +80,78 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
), ),
], ],
), ),
bottom: sessions.isNotEmpty && _sessionListOpen ? null : PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(
height: 1,
color: Colors.black,
),
),
actions: [
if (sessions.isNotEmpty) AnimatedRotation(
turns: _sessionListOpen ? -1/4 : 1/4,
duration: const Duration(milliseconds: 200),
child: IconButton(
onPressed: () {
setState(() {
_sessionListOpen = !_sessionListOpen;
});
},
icon: const Icon(Icons.chevron_right),
),
),
const SizedBox(width: 4,)
],
scrolledUnderElevation: 0.0, scrolledUnderElevation: 0.0,
backgroundColor: appBarColor, backgroundColor: appBarColor,
surfaceTintColor: Colors.transparent,
shadowColor: Colors.transparent,
), ),
body: Column( body: Column(
children: [ children: [
if (sessions.isNotEmpty) Container( if (sessions.isNotEmpty) AnimatedSwitcher(
constraints: const BoxConstraints(maxHeight: 64), duration: const Duration(milliseconds: 200),
decoration: BoxDecoration( transitionBuilder: (child, animation) => SizeTransition(sizeFactor: animation, axis: Axis.vertical, child: child),
color: appBarColor, child: sessions.isEmpty || !_sessionListOpen ? null : Container(
border: const Border(bottom: BorderSide(width: 1, color: Colors.black),) constraints: const BoxConstraints(maxHeight: 64),
), decoration: BoxDecoration(
child: Stack( color: appBarColor,
children: [ border: const Border(bottom: BorderSide(width: 1, color: Colors.black),)
ListView.builder( ),
controller: _sessionListScrollController, child: Stack(
scrollDirection: Axis.horizontal, children: [
itemCount: sessions.length, ListView.builder(
itemBuilder: (context, index) => SessionTile(session: sessions[index]), controller: _sessionListScrollController,
), scrollDirection: Axis.horizontal,
AnimatedOpacity( itemCount: sessions.length,
opacity: _shevronOpacity, itemBuilder: (context, index) => SessionTile(session: sessions[index]),
curve: Curves.easeOut,
duration: const Duration(milliseconds: 200),
child: Align(
alignment: Alignment.centerRight,
child: Container(
padding: const EdgeInsets.only(left: 16, right: 4, top: 1, bottom: 1),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
appBarColor.withOpacity(0),
appBarColor,
appBarColor,
],
),
),
height: double.infinity,
child: const Icon(Icons.chevron_right),
),
), ),
) AnimatedOpacity(
], opacity: _shevronOpacity,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 200),
child: Align(
alignment: Alignment.centerRight,
child: Container(
padding: const EdgeInsets.only(left: 16, right: 4, top: 1, bottom: 1),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
appBarColor.withOpacity(0),
appBarColor,
appBarColor,
],
),
),
height: double.infinity,
child: const Icon(Icons.chevron_right),
),
),
)
],
),
), ),
), ),
Expanded( Expanded(
@ -176,6 +203,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
create: (BuildContext context) => AudioCacheClient(), create: (BuildContext context) => AudioCacheClient(),
child: ListView.builder( child: ListView.builder(
reverse: true, reverse: true,
physics: const BouncingScrollPhysics(),
itemCount: cache.messages.length, itemCount: cache.messages.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final entry = cache.messages[index]; final entry = cache.messages[index];