Skip to content

Commit e7d0e85

Browse files
committed
compose: Have narrowLink take an optional nearMessageId
1 parent 626be61 commit e7d0e85

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
@@ -118,7 +118,22 @@ String encodeHashComponent(String str) {
118118
}
119119

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

test/model/compose_test.dart

+18-4
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,27 @@ hello
195195
group('narrowLink', () {
196196
test('AllMessagesNarrow', () {
197197
final store = eg.store();
198-
check(narrowLink(store, const AllMessagesNarrow())).equals(store.account.realmUrl.resolve('#narrow'));
198+
check(narrowLink(store, const AllMessagesNarrow()))
199+
.equals(store.account.realmUrl.resolve('#narrow'));
200+
check(narrowLink(store, const AllMessagesNarrow(), nearMessageId: 1))
201+
.equals(store.account.realmUrl.resolve('#narrow/near/1'));
199202
});
200203

201204
test('StreamNarrow / TopicNarrow', () {
202205
void checkNarrow(String expectedFragment, {
203206
required int streamId,
204207
required String name,
205208
String? topic,
209+
int? nearMessageId,
206210
}) {
207211
assert(expectedFragment.startsWith('#'), 'wrong-looking expectedFragment');
208212
final store = eg.store();
209213
store.addStream(eg.stream(streamId: streamId, name: name));
210214
final narrow = topic == null
211215
? StreamNarrow(streamId)
212216
: TopicNarrow(streamId, topic);
213-
check(narrowLink(store, narrow)).equals(store.account.realmUrl.resolve(expectedFragment));
217+
check(narrowLink(store, narrow, nearMessageId: nearMessageId))
218+
.equals(store.account.realmUrl.resolve(expectedFragment));
214219
}
215220

216221
checkNarrow(streamId: 1, name: 'announce', '#narrow/stream/1-announce');
@@ -219,26 +224,32 @@ hello
219224
checkNarrow(streamId: 415, name: 'chat.zulip.org', '#narrow/stream/415-chat.2Ezulip.2Eorg');
220225
checkNarrow(streamId: 419, name: 'français', '#narrow/stream/419-fran.C3.A7ais');
221226
checkNarrow(streamId: 403, name: 'Hshs[™~}(.', '#narrow/stream/403-Hshs.5B.E2.84.A2~.7D.28.2E');
227+
checkNarrow(streamId: 60, name: 'twitter', nearMessageId: 1570686, '#narrow/stream/60-twitter/near/1570686');
222228

223229
checkNarrow(streamId: 48, name: 'mobile', topic: 'Welcome screen UI',
224230
'#narrow/stream/48-mobile/topic/Welcome.20screen.20UI');
225231
checkNarrow(streamId: 243, name: 'mobile-team', topic: 'Podfile.lock clash #F92',
226232
'#narrow/stream/243-mobile-team/topic/Podfile.2Elock.20clash.20.23F92');
227233
checkNarrow(streamId: 377, name: 'translation/zh_tw', topic: '翻譯 "stream"',
228234
'#narrow/stream/377-translation.2Fzh_tw/topic/.E7.BF.BB.E8.AD.AF.20.22stream.22');
235+
checkNarrow(streamId: 42, name: 'Outreachy 2016-2017', topic: '2017-18 Stream?', nearMessageId: 302690,
236+
'#narrow/stream/42-Outreachy-2016-2017/topic/2017-18.20Stream.3F/near/302690');
229237
});
230238

231239
test('DmNarrow', () {
232240
void checkNarrow(String expectedFragment, String legacyExpectedFragment, {
233241
required List<int> allRecipientIds,
234242
required int selfUserId,
243+
int? nearMessageId,
235244
}) {
236245
assert(expectedFragment.startsWith('#'), 'wrong-looking expectedFragment');
237246
final store = eg.store();
238247
final narrow = DmNarrow(allRecipientIds: allRecipientIds, selfUserId: selfUserId);
239-
check(narrowLink(store, narrow)).equals(store.account.realmUrl.resolve(expectedFragment));
248+
check(narrowLink(store, narrow, nearMessageId: nearMessageId))
249+
.equals(store.account.realmUrl.resolve(expectedFragment));
240250
store.connection.zulipFeatureLevel = 176;
241-
check(narrowLink(store, narrow)).equals(store.account.realmUrl.resolve(legacyExpectedFragment));
251+
check(narrowLink(store, narrow, nearMessageId: nearMessageId))
252+
.equals(store.account.realmUrl.resolve(legacyExpectedFragment));
242253
}
243254

244255
checkNarrow(allRecipientIds: [1], selfUserId: 1,
@@ -253,6 +264,9 @@ hello
253264
checkNarrow(allRecipientIds: [1, 2, 3, 4], selfUserId: 4,
254265
'#narrow/dm/1,2,3,4-group',
255266
'#narrow/pm-with/1,2,3,4-group');
267+
checkNarrow(allRecipientIds: [1, 2], selfUserId: 1, nearMessageId: 12345,
268+
'#narrow/dm/1,2-dm/near/12345',
269+
'#narrow/pm-with/1,2-pm/near/12345');
256270
});
257271

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

0 commit comments

Comments
 (0)