mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-10-31 03:57:27 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			163 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'dart:async';
 | |
| 
 | |
| import 'package:adaptive_dialog/adaptive_dialog.dart';
 | |
| import 'package:famedlysdk/famedlysdk.dart';
 | |
| import 'package:fluffychat/components/matrix.dart';
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:adaptive_page_layout/adaptive_page_layout.dart';
 | |
| import '../utils/client_presence_extension.dart';
 | |
| import '../utils/presence_extension.dart';
 | |
| import 'package:flutter_gen/gen_l10n/l10n.dart';
 | |
| import 'avatar.dart';
 | |
| 
 | |
| class HorizontalStoriesList extends StatefulWidget {
 | |
|   final String searchQuery;
 | |
| 
 | |
|   const HorizontalStoriesList({Key key, this.searchQuery = ''})
 | |
|       : super(key: key);
 | |
|   @override
 | |
|   _HorizontalStoriesListState createState() => _HorizontalStoriesListState();
 | |
| }
 | |
| 
 | |
| class _HorizontalStoriesListState extends State<HorizontalStoriesList> {
 | |
|   StreamSubscription _onSync;
 | |
| 
 | |
|   @override
 | |
|   void dispose() {
 | |
|     _onSync?.cancel();
 | |
|     super.dispose();
 | |
|   }
 | |
| 
 | |
|   DateTime _lastSetState = DateTime.now();
 | |
|   Timer _coolDown;
 | |
| 
 | |
|   void _updateView() {
 | |
|     _lastSetState = DateTime.now();
 | |
|     setState(() => null);
 | |
|   }
 | |
| 
 | |
|   static const double height = 68.0;
 | |
| 
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     _onSync ??= Matrix.of(context).client.onSync.stream.listen((_) {
 | |
|       if (DateTime.now().millisecondsSinceEpoch -
 | |
|               _lastSetState.millisecondsSinceEpoch <
 | |
|           1000) {
 | |
|         _coolDown?.cancel();
 | |
|         _coolDown = Timer(Duration(seconds: 1), _updateView);
 | |
|       } else {
 | |
|         _updateView();
 | |
|       }
 | |
|     });
 | |
|     final contactList = Matrix.of(context)
 | |
|         .client
 | |
|         .contactList
 | |
|         .where((p) =>
 | |
|             p.senderId.toLowerCase().contains(widget.searchQuery.toLowerCase()))
 | |
|         .toList();
 | |
|     return AnimatedContainer(
 | |
|       height: contactList.isEmpty ? 0 : height,
 | |
|       duration: Duration(milliseconds: 300),
 | |
|       child: contactList.isEmpty
 | |
|           ? null
 | |
|           : ListView.builder(
 | |
|               scrollDirection: Axis.horizontal,
 | |
|               itemCount: contactList.length,
 | |
|               itemBuilder: (context, i) =>
 | |
|                   _StoriesListTile(story: contactList[i]),
 | |
|             ),
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| class _StoriesListTile extends StatelessWidget {
 | |
|   final Presence story;
 | |
| 
 | |
|   const _StoriesListTile({
 | |
|     Key key,
 | |
|     @required this.story,
 | |
|   }) : super(key: key);
 | |
|   @override
 | |
|   Widget build(BuildContext context) {
 | |
|     final hasStatusMessage = story.presence.statusMsg?.isNotEmpty ?? false;
 | |
|     return FutureBuilder<Profile>(
 | |
|         future: Matrix.of(context).client.getProfileFromUserId(story.senderId),
 | |
|         builder: (context, snapshot) {
 | |
|           final displayname =
 | |
|               snapshot.data?.displayname ?? story.senderId.localpart;
 | |
|           final avatarUrl = snapshot.data?.avatarUrl;
 | |
|           return Container(
 | |
|             width: Avatar.defaultSize + 32,
 | |
|             height: _HorizontalStoriesListState.height,
 | |
|             child: InkWell(
 | |
|               borderRadius: BorderRadius.circular(8),
 | |
|               onTap: () async {
 | |
|                 if (story.senderId == Matrix.of(context).client.userID) {
 | |
|                   await showOkAlertDialog(
 | |
|                     context: context,
 | |
|                     title: displayname,
 | |
|                     message: story.presence.statusMsg,
 | |
|                     okLabel: L10n.of(context).close,
 | |
|                   );
 | |
|                   return;
 | |
|                 }
 | |
|                 if (hasStatusMessage) {
 | |
|                   if (OkCancelResult.ok !=
 | |
|                       await showOkCancelAlertDialog(
 | |
|                         context: context,
 | |
|                         title: displayname,
 | |
|                         message: story.presence.statusMsg,
 | |
|                         okLabel: L10n.of(context).sendAMessage,
 | |
|                         cancelLabel: L10n.of(context).close,
 | |
|                       )) {
 | |
|                     return;
 | |
|                   }
 | |
|                 }
 | |
|                 final roomId = await Matrix.of(context)
 | |
|                     .client
 | |
|                     .startDirectChat(story.senderId);
 | |
|                 await AdaptivePageLayout.of(context)
 | |
|                     .pushNamedAndRemoveUntilIsFirst('/rooms/${roomId}');
 | |
|               },
 | |
|               child: Column(
 | |
|                 crossAxisAlignment: CrossAxisAlignment.center,
 | |
|                 mainAxisAlignment: MainAxisAlignment.center,
 | |
|                 children: [
 | |
|                   Container(
 | |
|                     width: Avatar.defaultSize,
 | |
|                     height: Avatar.defaultSize,
 | |
|                     child: Stack(
 | |
|                       children: [
 | |
|                         Center(child: Avatar(avatarUrl, displayname)),
 | |
|                         Align(
 | |
|                           alignment: Alignment.bottomRight,
 | |
|                           child: Icon(
 | |
|                             Icons.circle,
 | |
|                             color: story.color,
 | |
|                             size: 12,
 | |
|                           ),
 | |
|                         ),
 | |
|                       ],
 | |
|                     ),
 | |
|                   ),
 | |
|                   Padding(
 | |
|                     padding:
 | |
|                         const EdgeInsets.only(left: 4.0, right: 4.0, top: 4.0),
 | |
|                     child: Text(displayname.split(' ').first,
 | |
|                         maxLines: 1,
 | |
|                         style: TextStyle(
 | |
|                           fontWeight: hasStatusMessage ? FontWeight.bold : null,
 | |
|                           color: hasStatusMessage
 | |
|                               ? Theme.of(context).accentColor
 | |
|                               : null,
 | |
|                         )),
 | |
|                   ),
 | |
|                 ],
 | |
|               ),
 | |
|             ),
 | |
|           );
 | |
|         });
 | |
|   }
 | |
| }
 | 
