Skip to content

Commit 23f5f47

Browse files
committed
compose: Have narrowLink take an optional nearMessageId
1 parent 2561775 commit 23f5f47

File tree

2 files changed

+39
-8
lines changed

2 files changed

+39
-8
lines changed

lib/model/compose.dart

+21-4
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,22 @@ String encodeHashComponent(String str) {
117117
}
118118

119119
/// A URL to the given [Narrow], on `store`'s realm.
120-
Uri narrowLink(PerAccountStore store, Narrow narrow) {
120+
///
121+
/// To include /near/{messageId} in the link, pass a non-null [nearMessageId].
122+
// Why take [nearMessageId] in a param, instead of looking for it in [narrow]?
123+
//
124+
// A reasonable question: after all, the "near" part of a near link (e.g., for
125+
// quote-and-reply) does take the same form as other operator/operand pairs
126+
// that we represent with [ApiNarrowElement]s, like "/stream/48-mobile".
127+
//
128+
// But unlike those other elements, we choose not to give the "near" element
129+
// an [ApiNarrowElement] representation, because it doesn't have quite that role:
130+
// it says where to look in a list of messages, but it doesn't filter the list down.
131+
// In fact, from a brief look at server code, it seems to be *ignored*
132+
// if you include it in the `narrow` param in get-messages requests.
133+
// When you want to point the server to a location in a message list, you
134+
// you do so by passing the `anchor` param.
135+
Uri narrowLink(PerAccountStore store, Narrow narrow, {int? nearMessageId}) {
121136
final apiNarrow = narrow.apiEncode();
122137
final fragment = StringBuffer('narrow');
123138
for (ApiNarrowElement element in apiNarrow) {
@@ -153,8 +168,10 @@ Uri narrowLink(PerAccountStore store, Narrow narrow) {
153168
fragment.write(element.operand.toString());
154169
}
155170
}
171+
172+
if (nearMessageId != null) {
173+
fragment.write('/near/$nearMessageId');
174+
}
175+
156176
return store.account.realmUrl.replace(fragment: fragment.toString());
157177
}
158-
159-
// TODO more, like /near links to messages in conversations
160-
// (also to be used in quote-and-reply)

test/model/compose_test.dart

+18-4
Original file line numberDiff line numberDiff line change
@@ -233,22 +233,27 @@ hello
233233
group('narrowLink', () {
234234
test('AllMessagesNarrow', () {
235235
final store = eg.store();
236-
check(narrowLink(store, const AllMessagesNarrow())).equals(store.account.realmUrl.resolve('#narrow'));
236+
check(narrowLink(store, const AllMessagesNarrow()))
237+
.equals(store.account.realmUrl.resolve('#narrow'));
238+
check(narrowLink(store, const AllMessagesNarrow(), nearMessageId: 1))
239+
.equals(store.account.realmUrl.resolve('#narrow/near/1'));
237240
});
238241

239242
test('StreamNarrow / TopicNarrow', () {
240243
void checkNarrow(String expectedFragment, {
241244
required int streamId,
242245
required String name,
243246
String? topic,
247+
int? nearMessageId,
244248
}) {
245249
assert(expectedFragment.startsWith('#'), 'wrong-looking expectedFragment');
246250
final store = eg.store();
247251
store.addStream(eg.stream(streamId: streamId, name: name));
248252
final narrow = topic == null
249253
? StreamNarrow(streamId)
250254
: TopicNarrow(streamId, topic);
251-
check(narrowLink(store, narrow)).equals(store.account.realmUrl.resolve(expectedFragment));
255+
check(narrowLink(store, narrow, nearMessageId: nearMessageId))
256+
.equals(store.account.realmUrl.resolve(expectedFragment));
252257
}
253258

254259
checkNarrow(streamId: 1, name: 'announce', '#narrow/stream/1-announce');
@@ -257,26 +262,32 @@ hello
257262
checkNarrow(streamId: 415, name: 'chat.zulip.org', '#narrow/stream/415-chat.2Ezulip.2Eorg');
258263
checkNarrow(streamId: 419, name: 'français', '#narrow/stream/419-fran.C3.A7ais');
259264
checkNarrow(streamId: 403, name: 'Hshs[™~}(.', '#narrow/stream/403-Hshs.5B.E2.84.A2~.7D.28.2E');
265+
checkNarrow(streamId: 60, name: 'twitter', nearMessageId: 1570686, '#narrow/stream/60-twitter/near/1570686');
260266

261267
checkNarrow(streamId: 48, name: 'mobile', topic: 'Welcome screen UI',
262268
'#narrow/stream/48-mobile/topic/Welcome.20screen.20UI');
263269
checkNarrow(streamId: 243, name: 'mobile-team', topic: 'Podfile.lock clash #F92',
264270
'#narrow/stream/243-mobile-team/topic/Podfile.2Elock.20clash.20.23F92');
265271
checkNarrow(streamId: 377, name: 'translation/zh_tw', topic: '翻譯 "stream"',
266272
'#narrow/stream/377-translation.2Fzh_tw/topic/.E7.BF.BB.E8.AD.AF.20.22stream.22');
273+
checkNarrow(streamId: 42, name: 'Outreachy 2016-2017', topic: '2017-18 Stream?', nearMessageId: 302690,
274+
'#narrow/stream/42-Outreachy-2016-2017/topic/2017-18.20Stream.3F/near/302690');
267275
});
268276

269277
test('DmNarrow', () {
270278
void checkNarrow(String expectedFragment, String legacyExpectedFragment, {
271279
required List<int> allRecipientIds,
272280
required int selfUserId,
281+
int? nearMessageId,
273282
}) {
274283
assert(expectedFragment.startsWith('#'), 'wrong-looking expectedFragment');
275284
final store = eg.store();
276285
final narrow = DmNarrow(allRecipientIds: allRecipientIds, selfUserId: selfUserId);
277-
check(narrowLink(store, narrow)).equals(store.account.realmUrl.resolve(expectedFragment));
286+
check(narrowLink(store, narrow, nearMessageId: nearMessageId))
287+
.equals(store.account.realmUrl.resolve(expectedFragment));
278288
store.connection.zulipFeatureLevel = 176;
279-
check(narrowLink(store, narrow)).equals(store.account.realmUrl.resolve(legacyExpectedFragment));
289+
check(narrowLink(store, narrow, nearMessageId: nearMessageId))
290+
.equals(store.account.realmUrl.resolve(legacyExpectedFragment));
280291
}
281292

282293
checkNarrow(allRecipientIds: [1], selfUserId: 1,
@@ -291,6 +302,9 @@ hello
291302
checkNarrow(allRecipientIds: [1, 2, 3, 4], selfUserId: 4,
292303
'#narrow/dm/1,2,3,4-group',
293304
'#narrow/pm-with/1,2,3,4-group');
305+
checkNarrow(allRecipientIds: [1, 2], selfUserId: 1, nearMessageId: 12345,
306+
'#narrow/dm/1,2-dm/near/12345',
307+
'#narrow/pm-with/1,2-pm/near/12345');
294308
});
295309

296310
// TODO other Narrow subclasses as we add them:

0 commit comments

Comments
 (0)