-
Notifications
You must be signed in to change notification settings - Fork 309
api: Mark messages as read (updateMessageFlags and updateMessageFlagsForNarrow) #361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @sirpengi! Comments below on the first three commits:
ba5e3dd api [nfc]: Refactor out logic that supports serializing legacy DmNarrow
58be9c9 api [nfc]: Pull out a method Anchor.toJson
346e94c api: Add updateMessageFlags and updateMessageFlagsForNarrow routes
Let's split out the remaining commit to a separate followup PR:
eabae44 api: Add markAllAsRead, markStreamAsRead, and markTopicAsRead routes
just to keep each PR simpler, since GitHub's UI doesn't do great as threads get longer.
lib/api/model/narrow.dart
Outdated
typedef ApiNarrow = List<ApiNarrowElement>; | ||
|
||
/// Convenience function to resolve legacy elements of a narrow. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description "resolve legacy elements" doesn't sound quite right to me — the input isn't expected to have legacy elements (rather it may have unresolved ApiNarrowDm
elements), and the resolution is just as likely or more so to produce modern elements as to produce legacy elements.
Here's a version:
/// Convenience function to resolve legacy elements of a narrow. | |
/// Resolve any [ApiNarrowDm] elements appropriately. |
And then resolveDmElements
would work as a name.
(If we later have a second legacy/modern split to resolve, this name and description won't cover that, but we can handle that if it comes up.)
lib/api/model/narrow.dart
Outdated
ApiNarrow resolveLegacyElements(ApiNarrow narrow, ApiConnection connection) { | ||
final supportsOperatorDm = connection.zulipFeatureLevel! >= 177; // TODO(server-7) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have it take an int zulipFeatureLevel
instead — that feels more like the right layer for this to be operating at.
if (narrow.any((element) => element is ApiNarrowDm)) { | ||
final supportsOperatorDm = connection.zulipFeatureLevel! >= 177; // TODO(server-7) | ||
narrow = narrow.map((element) => switch (element) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The narrow.any
conditional here is an optimization that avoids unnecessarily copying the list when there's nothing to change. Let's preserve that in the refactor.
lib/api/route/messages.dart
Outdated
AnchorCode.oldest => RawParameter('oldest'), | ||
AnchorCode.firstUnread => RawParameter('first_unread'), | ||
}, | ||
'narrow': resolveLegacyElements(narrow, connection), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
api [nfc]: Refactor out logic that supports serializing legacy DmNarrow
s/Refactor/Factor/
For me at least "refactor out" isn't a term; there's "refactor" for a general category of things you might do, one of which is to "factor out" something, but no "refactor out".
Also "legacy DmNarrow" doesn't quite make sense; DmNarrow itself isn't legacy, and nor is there such a thing as a legacy value of DmNarrow vs. a non-legacy value. (If talking about ApiNarrowDm, then there are modern and legacy versions of that — but because "DmNarrow" is spelled in a way that isn't normal English, it can only refer to the identifier with that name, not to the general concept of a "DM narrow".)
I think "supports serializing ApiNarrowDm" covers it. A necessary part of serializing one of those is to resolve it to modern or legacy.
lib/api/route/messages.dart
Outdated
}); | ||
} | ||
|
||
/// The type of [updateMessageFlags.op] and [updateMessageFlagsForNarrow.op]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// The type of [updateMessageFlags.op] and [updateMessageFlagsForNarrow.op]. | |
/// An `op` value for [updateMessageFlags] or [updateMessageFlagsForNarrow]. |
The dotted syntax doesn't work for referring to a function parameter — basically because there's no such syntax in Dart itself for referring to such a thing.
test/api/route/messages_test.dart
Outdated
return result; | ||
} | ||
final fakeResult = UpdateMessageFlagsForNarrowResult( | ||
processedCount: 11, updatedCount: 11, foundOldest: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: make updatedCount
less than processedCount
, because it often might be
connection.prepare(json: fakeResult.toJson()); | ||
await checkUpdateMessageFlagsForNarrow(connection, | ||
anchor: const NumericAnchor(42), | ||
numBefore: 0, numAfter: 20, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if numBefore
is 0, then the real server behavior will be for foundOldest
to be false (because it won't look to see if there are any older); so the test data should reflect that
(I'm not sure if that's still true when anchor
is oldest
, as above. Could do a quick experiment to find out. If not, then fakeResult
could become a function taking {required bool foundOldest}
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed by both a quick experiment and looking in zerver source that anchor=oldest
does send true
for foundOldest
.
test/api/route/messages_test.dart
Outdated
}); | ||
}); | ||
|
||
test('narrow uses resolveLegacyElements to encode', () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: put the "numeric anchor" and "narrow uses …" tests in the same order here as for getMessages
, for ease of comparison
test/api/route/messages_test.dart
Outdated
'op': 'add', | ||
'flag': 'read', | ||
}); | ||
connection.zulipFeatureLevel = eg.futureZulipFeatureLevel; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's take this line out; I feel like it's likely to just confuse someone about the semantics of FakeApiConnection.with_
, which are that it's a fresh connection that's thrown away.
test/api/route/messages_test.dart
Outdated
return FakeApiConnection.with_((connection) async { | ||
connection.zulipFeatureLevel = 176; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh in fact better yet: pass the zulipFeatureLevel
to with_
, rather than mutating it later. See the getMessageCompat
tests for examples.
eabae44
to
4d2c4bd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the revision! Generally looks good — just a few small comments below.
test/api/route/messages_test.dart
Outdated
{'operator': 'pm-with', 'operand': [123, 234]}, | ||
])); | ||
connection.zulipFeatureLevel = eg.futureZulipFeatureLevel; | ||
test('narrow uses resolveLegacyElements to encode', () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test name no longer matches code
lib/api/model/narrow.dart
Outdated
final supportsOperatorDm = zulipFeatureLevel >= 177; // TODO(server-7) | ||
return (narrow.any((element) => element is ApiNarrowDm)) | ||
? narrow.map((element) => switch (element) { | ||
ApiNarrowDm() => element.resolve(legacy: !supportsOperatorDm), | ||
_ => element, | ||
}).toList() | ||
: narrow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a bit tangly to read, as I think often happens with the ?:
operator when one or both branches are more than a line or two long. Let's leave it as full statement-level control flow, like it is in the existing code:
final supportsOperatorDm = zulipFeatureLevel >= 177; // TODO(server-7) | |
return (narrow.any((element) => element is ApiNarrowDm)) | |
? narrow.map((element) => switch (element) { | |
ApiNarrowDm() => element.resolve(legacy: !supportsOperatorDm), | |
_ => element, | |
}).toList() | |
: narrow; | |
if (!narrow.any((element) => element is ApiNarrowDm)) { | |
return narrow; | |
} | |
final supportsOperatorDm = zulipFeatureLevel >= 177; // TODO(server-7) | |
return narrow.map((element) => switch (element) { | |
ApiNarrowDm() => element.resolve(legacy: !supportsOperatorDm), | |
_ => element, | |
}).toList(); |
test/api/route/messages_test.dart
Outdated
return result; | ||
} | ||
|
||
mkResult({required bool foundOldest}) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function definition needs explicit return type
…icit This came up in a code review here: zulip#361 (comment) and it'd be good to have written down in a more accessible place. There are some existing parts of the API bindings -- particularly the parts written in the very first week or two of the prototype, before I thought through this pattern -- that don't follow this. I'll fix those up next.
…icit This came up in a code review here: #361 (comment) and it'd be good to have written down in a more accessible place. There are some existing parts of the API bindings -- particularly the parts written in the very first week or two of the prototype, before I thought through this pattern -- that don't follow this. I'll fix those up next.
Also pull out narrow.toJson tests.
4d2c4bd
to
0d16270
Compare
@gnprice this is ready again! |
0d16270
to
dfcc6ca
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Looks like I had a small followup comment pending which I'd meant to post.)
lib/api/route/messages.dart
Outdated
this.firstProcessedId, | ||
this.lastProcessedId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the existing API bindings aren't 100% consistent about this
→ #367.
Thanks for the revision! Looks good; merging. |
This PR adds all the api routes related to marking messages as read.
The complexities around the response of https://zulip.com/api/mark-all-as-read are ignored as it is deprecated (it is also ignored in zulip-mobile). On an examination of
lib/api/core.dart
it doesn't look like we check the value of theresult
field (where the status code is 200) so if the server returnspartially_completed
for the affected FLs it doesn't raise any errors (although we would treat the operation as completed).Fixes part of #130