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
7d1747e
commit 0b71040
Showing
5 changed files
with
494 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
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. | ||
}); | ||
} | ||
|
||
@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) => RecentDmConversationsItem(narrow: sorted[index]))); | ||
} | ||
} | ||
|
||
class RecentDmConversationsItem extends StatelessWidget { | ||
const RecentDmConversationsItem({super.key, required this.narrow}); | ||
|
||
final DmNarrow narrow; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
final store = PerAccountStoreWidget.of(context); | ||
final selfUser = store.users[store.account.userId]!; | ||
|
||
final String title; | ||
final Widget avatar; | ||
switch (narrow.otherRecipientIds) { | ||
case []: | ||
title = selfUser.fullName; | ||
avatar = AvatarImage(userId: selfUser.userId); | ||
case [var otherUserId]: | ||
final otherUser = store.users[otherUserId]; | ||
title = otherUser?.fullName ?? '(unknown user)'; | ||
avatar = AvatarImage(userId: otherUserId); | ||
default: | ||
// TODO(i18n): List formatting, like you can do in JavaScript: | ||
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya']) | ||
// // 'Chris、Greg、Alya' | ||
title = narrow.otherRecipientIds.map((id) => store.users[id]?.fullName ?? '(unknown user)').join(', '); | ||
avatar = ColoredBox(color: const Color(0x33808080), | ||
child: Center( | ||
child: Icon(ZulipIcons.group_dm, color: Colors.black.withOpacity(0.5)))); | ||
} | ||
|
||
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: AvatarShape(size: 32, borderRadius: 3, child: avatar)), | ||
const SizedBox(width: 8), | ||
Expanded(child: Padding( | ||
padding: const EdgeInsets.symmetric(vertical: 4), | ||
child: Text( | ||
style: const TextStyle( | ||
fontFamily: 'Source Sans 3', | ||
fontSize: 17, | ||
height: (20 / 17), | ||
color: Color(0xFF222222), | ||
).merge(weightVariableTextStyle(context)), | ||
maxLines: 2, | ||
overflow: TextOverflow.ellipsis, | ||
title))), | ||
const SizedBox(width: 8), | ||
// TODO(#253): Unread count | ||
]))); | ||
} | ||
} |
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,39 @@ | ||
import 'package:flutter/widgets.dart'; | ||
|
||
// Inspired by test code in the Flutter tree: | ||
// https://github.com/flutter/flutter/blob/53082f65b/packages/flutter/test/widgets/observer_tester.dart | ||
// https://github.com/flutter/flutter/blob/53082f65b/packages/flutter/test/widgets/navigator_test.dart | ||
|
||
/// A trivial observer for testing the navigator. | ||
class TestNavigatorObserver extends NavigatorObserver { | ||
void Function(Route<dynamic> route, Route<dynamic>? previousRoute)? onPushed; | ||
void Function(Route<dynamic> route, Route<dynamic>? previousRoute)? onPopped; | ||
void Function(Route<dynamic> route, Route<dynamic>? previousRoute)? onRemoved; | ||
void Function(Route<dynamic>? route, Route<dynamic>? previousRoute)? onReplaced; | ||
void Function(Route<dynamic> route, Route<dynamic>? previousRoute)? onStartUserGesture; | ||
|
||
@override | ||
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { | ||
onPushed?.call(route, previousRoute); | ||
} | ||
|
||
@override | ||
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { | ||
onPopped?.call(route, previousRoute); | ||
} | ||
|
||
@override | ||
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) { | ||
onRemoved?.call(route, previousRoute); | ||
} | ||
|
||
@override | ||
void didReplace({ Route<dynamic>? oldRoute, Route<dynamic>? newRoute }) { | ||
onReplaced?.call(newRoute, oldRoute); | ||
} | ||
|
||
@override | ||
void didStartUserGesture(Route<dynamic> route, Route<dynamic>? previousRoute) { | ||
onStartUserGesture?.call(route, previousRoute); | ||
} | ||
} |
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 |
---|---|---|
@@ -1,7 +1,19 @@ | ||
import 'package:checks/checks.dart'; | ||
import 'package:flutter/widgets.dart'; | ||
|
||
import 'package:zulip/widgets/content.dart'; | ||
|
||
extension RealmContentNetworkImageChecks on Subject<RealmContentNetworkImage> { | ||
Subject<Uri> get src => has((i) => i.src, 'src'); | ||
// TODO others | ||
} | ||
|
||
extension AvatarImageChecks on Subject<AvatarImage> { | ||
Subject<int> get userId => has((i) => i.userId, 'userId'); | ||
} | ||
|
||
extension AvatarShapeChecks on Subject<AvatarShape> { | ||
Subject<double> get size => has((i) => i.size, 'size'); | ||
Subject<double> get borderRadius => has((i) => i.borderRadius, 'borderRadius'); | ||
Subject<Widget> get child => has((i) => i.child, 'child'); | ||
} |
Oops, something went wrong.