forked from zulip/zulip-flutter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The screen's content area (so, the list of conversations, but not the app bar at the top) is built against Vlad's design: https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/design.3A.20DM-conversation.20list/near/1594654 except that some features that appear in that design are left unimplemented for now, since we don't have data structures for them yet: - unread counts - user presence Fixes: zulip#119
- Loading branch information
1 parent
a1f931a
commit 257af55
Showing
3 changed files
with
153 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import 'package:collection/collection.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
import '../model/narrow.dart'; | ||
import '../model/recent_dm_conversations.dart'; | ||
import 'content.dart'; | ||
import 'icons.dart'; | ||
import 'message_list.dart'; | ||
import 'page.dart'; | ||
import 'store.dart'; | ||
import 'text.dart'; | ||
|
||
class RecentDmConversationsPage extends StatefulWidget { | ||
const RecentDmConversationsPage({super.key}); | ||
|
||
static Route<void> buildRoute({required BuildContext context}) { | ||
return MaterialAccountPageRoute(context: context, | ||
builder: (context) => const RecentDmConversationsPage()); | ||
} | ||
|
||
@override | ||
State<RecentDmConversationsPage> createState() => _RecentDmConversationsPageState(); | ||
} | ||
|
||
class _RecentDmConversationsPageState extends State<RecentDmConversationsPage> with PerAccountStoreAwareStateMixin<RecentDmConversationsPage> { | ||
RecentDmConversationsView? model; | ||
|
||
@override | ||
void onNewStore() { | ||
model?.removeListener(_modelChanged); | ||
model = PerAccountStoreWidget.of(context).recentDmConversationsView | ||
..addListener(_modelChanged); | ||
} | ||
|
||
void _modelChanged() { | ||
setState(() { | ||
// The actual state lives in [model]. | ||
// This method was called because that just changed. | ||
}); | ||
} | ||
|
||
Widget _buildItem(BuildContext context, DmNarrow narrow) { | ||
final colorScheme = Theme.of(context).colorScheme; | ||
|
||
final allRecipientIds = narrow.allRecipientIds; | ||
final store = PerAccountStoreWidget.of(context); | ||
final selfUser = store.users[store.account.userId]!; | ||
final recipientsSansSelf = allRecipientIds | ||
.whereNot((id) => id == selfUser.userId) | ||
.map((id) => store.users[id]!) | ||
.toList(); | ||
|
||
final Widget title; | ||
final Widget avatar; | ||
switch (recipientsSansSelf.length) { | ||
case 0: { | ||
title = Text(selfUser.fullName); | ||
avatar = Avatar(userId: selfUser.userId); | ||
break; | ||
} | ||
case 1: { | ||
final otherUser = recipientsSansSelf.single; | ||
title = Text(otherUser.fullName); | ||
avatar = Avatar(userId: otherUser.userId); | ||
break; | ||
} | ||
default: { | ||
// TODO(i18n): List formatting, like you can do in JavaScript: | ||
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya']) | ||
// // 'Chris、Greg、Alya' | ||
title = Text(recipientsSansSelf.map((r) => r.fullName).join(', ')); | ||
avatar = ColoredBox(color: const Color(0xFF808080).withOpacity(0.2), | ||
child: Center( | ||
child: Icon(ZulipIcons.group_dm, color: Colors.black.withOpacity(0.5)))); | ||
break; | ||
} | ||
} | ||
|
||
return InkWell( | ||
onTap: () { | ||
Navigator.push(context, | ||
MessageListPage.buildRoute(context: context, narrow: narrow)); | ||
}, | ||
child: ConstrainedBox(constraints: const BoxConstraints(minHeight: 48), | ||
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ | ||
Padding(padding: const EdgeInsets.fromLTRB(12, 8, 0, 8), | ||
child: _AvatarWrapper(child: avatar)), | ||
const SizedBox(width: 8), | ||
Expanded(child: DefaultTextStyle( | ||
style: const TextStyle( | ||
fontFamily: 'Source Sans 3', | ||
fontSize: 17, | ||
height: (20 / 17), | ||
color: Color(0xFF222222), | ||
).merge(weightVariableTextStyle(context)), | ||
maxLines: 2, | ||
overflow: TextOverflow.ellipsis, | ||
child: title)), | ||
const SizedBox(width: 8), | ||
// TODO: Unread count | ||
]))); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final sorted = model!.sorted; | ||
return Scaffold( | ||
appBar: AppBar(title: const Text('Direct messages')), | ||
body: ListView.builder( | ||
itemCount: sorted.length, | ||
itemBuilder: (context, index) => _buildItem(context, sorted[index]))); | ||
} | ||
} | ||
|
||
/// Clips and sizes an avatar for a list item in [RecentDmConversationsPage]. | ||
class _AvatarWrapper extends StatelessWidget { | ||
const _AvatarWrapper({required this.child}); | ||
|
||
final Widget child; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return SizedBox.square( | ||
dimension: 32, | ||
child: ClipRRect( | ||
borderRadius: const BorderRadius.all(Radius.circular(3)), | ||
clipBehavior: Clip.antiAlias, | ||
child: child)); | ||
} | ||
} |