Skip to content

Commit 6e0e1f0

Browse files
committed
home: Sort leading emoji first in channel names
Update the channel sorting logic to ensure streams with leading emojis in their names are listed above those without emojis. The updated sorting respects pinned, muted, and unmuted streams while handling emoji precedence and maintaining alphabetical order for ties. Fixes: zulip#1202
1 parent 3ff7096 commit 6e0e1f0

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

lib/widgets/subscription_list.dart

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,30 @@ class _SubscriptionList extends StatelessWidget {
175175

176176
@override
177177
Widget build(BuildContext context) {
178+
179+
final sortedSubscriptions = List<Subscription>.from(subscriptions)
180+
..sort((a, b) {
181+
if (a.pinToTop != b.pinToTop) {
182+
return a.pinToTop ? -1 : 1;
183+
}
184+
185+
if (a.isMuted != b.isMuted) {
186+
return a.isMuted ? 1 : -1;
187+
}
188+
189+
final isEmojiA = _startsWithEmoji(a.name);
190+
final isEmojiB = _startsWithEmoji(b.name);
191+
if (isEmojiA != isEmojiB) {
192+
return isEmojiA ? -1 : 1;
193+
}
194+
195+
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
196+
});
197+
178198
return SliverList.builder(
179199
itemCount: subscriptions.length,
180200
itemBuilder: (BuildContext context, int index) {
181-
final subscription = subscriptions[index];
201+
final subscription = sortedSubscriptions[index];
182202
final unreadCount = unreadsModel!.countInChannel(subscription.streamId);
183203
final showMutedUnreadBadge = unreadCount == 0
184204
&& unreadsModel!.countInChannelNarrow(subscription.streamId) > 0;
@@ -189,6 +209,23 @@ class _SubscriptionList extends StatelessWidget {
189209
}
190210
}
191211

212+
bool _startsWithEmoji(String name) {
213+
final firstChar = name.characters.first;
214+
final int firstCharCode = firstChar.runes.first;
215+
216+
return (firstCharCode >= 0x1F600 && firstCharCode <= 0x1F64F) || // Emoticons
217+
(firstCharCode >= 0x1F300 && firstCharCode <= 0x1F5FF) || // Misc Symbols and Pictographs
218+
(firstCharCode >= 0x1F680 && firstCharCode <= 0x1F6FF) || // Transport and Map
219+
(firstCharCode >= 0x1F700 && firstCharCode <= 0x1F77F) || // Alchemical Symbols
220+
(firstCharCode >= 0x2600 && firstCharCode <= 0x26FF) || // Misc Symbols
221+
(firstCharCode >= 0x2700 && firstCharCode <= 0x27BF) || // Dingbats
222+
(firstCharCode >= 0xFE00 && firstCharCode <= 0xFE0F) || // Variation Selectors
223+
(firstCharCode >= 0x1F900 && firstCharCode <= 0x1F9FF) || // Supplemental Symbols and Pictographs
224+
(firstCharCode >= 0x1FA70 && firstCharCode <= 0x1FAFF) || // Symbols and Pictographs Extended-A
225+
(firstCharCode >= 0x1F1E6 && firstCharCode <= 0x1F1FF) || // Flags
226+
(firstCharCode >= 0x1F000 && firstCharCode <= 0x1FFFF); // Supplementary Multilingual Plane
227+
}
228+
192229
@visibleForTesting
193230
class SubscriptionItem extends StatelessWidget {
194231
const SubscriptionItem({

test/widgets/subscription_list_test.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,32 @@ void main() {
150150
]);
151151
check(listedStreamIds(tester)).deepEquals([2, 1, 3, 4, 6, 5]);
152152
});
153+
154+
testWidgets('channels with emoji in name are listed above non-emoji names', (tester) async {
155+
await setupStreamListPage(tester, subscriptions: [
156+
eg.subscription(eg.stream(streamId: 1, name: '😊 Happy Stream')),
157+
eg.subscription(eg.stream(streamId: 2, name: 'Alpha Stream')),
158+
eg.subscription(eg.stream(streamId: 3, name: '🚀 Rocket Stream')),
159+
eg.subscription(eg.stream(streamId: 4, name: 'Beta Stream')),
160+
]);
161+
162+
check(listedStreamIds(tester)).deepEquals([1, 3, 2, 4]);
163+
});
164+
165+
testWidgets('channels with emoji in name, pinned, unpinned, muted, and unmuted are sorted correctly', (tester) async {
166+
await setupStreamListPage(tester, subscriptions: [
167+
eg.subscription(eg.stream(streamId: 1, name: '😊 Happy Stream'), pinToTop: true, isMuted: false),
168+
eg.subscription(eg.stream(streamId: 2, name: '🚀 Rocket Stream'), pinToTop: true, isMuted: true),
169+
eg.subscription(eg.stream(streamId: 3, name: 'Alpha Stream'), pinToTop: true, isMuted: false),
170+
eg.subscription(eg.stream(streamId: 4, name: 'Beta Stream'), pinToTop: true, isMuted: true),
171+
eg.subscription(eg.stream(streamId: 5, name: '🌟 Star Stream'), pinToTop: false, isMuted: false),
172+
eg.subscription(eg.stream(streamId: 6, name: '🔥 Fire Stream'), pinToTop: false, isMuted: true),
173+
eg.subscription(eg.stream(streamId: 7, name: 'Gamma Stream'), pinToTop: false, isMuted: false),
174+
eg.subscription(eg.stream(streamId: 8, name: 'Delta Stream'), pinToTop: false, isMuted: true),
175+
]);
176+
177+
check(listedStreamIds(tester)).deepEquals([1,3,2,4,5,7,6,8]);
178+
});
153179
});
154180

155181
testWidgets('unread badge shows with unreads', (tester) async {

0 commit comments

Comments
 (0)