-
Notifications
You must be signed in to change notification settings - Fork 306
compose: Add narrowLink
helper, to write links to Narrows
#189
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
66983a7
to
e7d0e85
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! Generally looks great; small comments.
lib/model/compose.dart
Outdated
}; | ||
|
||
// Corresponds to encodeHashComponent in Zulip web; | ||
// see web/shared/src/internal_url.js. |
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.
// see web/shared/src/internal_url.js. | |
// see web/shared/src/internal_url.ts. |
since the source file is in TS now
lib/model/compose.dart
Outdated
// see web/shared/src/internal_url.js. | ||
String encodeHashComponent(String str) { | ||
return Uri.encodeComponent(str) | ||
.replaceAllMapped(RegExp(r'[%().]'), (Match m) => hashReplacements[m[0]!]!); |
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: pull out the RegExp object as a static final
variable (so a final
at top level of the file), so that its construction is memoized
This probably isn't a big deal for this particular function as we shouldn't be calling it in a hot path, but best to stick to as a pattern.
lib/model/compose.dart
Outdated
resultBuffer.write('/near/$nearMessageId'); | ||
} | ||
|
||
return store.account.realmUrl.resolve(resultBuffer.toString()); |
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 relative URL in resultBuffer
always goes purely in the fragment — it starts with #
— so an equivalent version that's a bit simpler to know what it does is:
return store.account.realmUrl.resolve(resultBuffer.toString()); | |
return store.account.realmUrl.replace(fragment: resultBuffer.toString()); |
together with
- final resultBuffer = StringBuffer('#narrow');
+ final resultBuffer = StringBuffer('narrow');
(And perhaps also rename the local to fragmentBuffer
, or just fragment
.)
lib/model/compose.dart
Outdated
final operand = resolved.operand; | ||
final operandSuffix = operand.length >= 3 | ||
? 'group' | ||
: (supportsOperatorDm ? 'dm' : 'pm'); // TODO(?) CZO actually gives a slugified full_name here…?? |
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.
Huh. Looks like there's two implementations of generating a URL for a DM narrow: pm_perma_link
and pm_with_url
, both in web/src/people.js
. The former does what this code does, and the latter does what you're seeing:
if (user_ids.length > 1) {
suffix = "group";
} else {
const person = get_by_user_id(user_ids[0]);
if (person && person.full_name) {
suffix = person.full_name.replaceAll(/[ "%/<>`\p{C}]+/gu, "-");
I'm fine with just "dm". I think the fancier thing is most helpful for putting a URL in the location bar, which the user may see all the time when they're looking at that 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.
Ohh, I think I see why there's both. The thing that's happening is that pm_with_url
includes only the other people in the thread, while pm_perma_link
includes all including self. Which is necessary for a context like quote-and-reply, where the point of the link is likely for other people to be able to use it.
But then once it's listing all recipients and not just others, that means that in a 1:1 thread it's listing both people. So to use names, you'd have to write both names. Which we don't do even in the location bar for 3-person group DM threads, and so don't do for these permalinks either.
(A shame web is so thin on jsdoc or other comments.)
Anyway, so that just confirms the conclusion: stick with plain dm
/pm
.
lib/model/compose.dart
Outdated
// TODO(server-7) remove special-casing | ||
if (element is ApiNarrowDm) { | ||
final supportsOperatorDm = store.connection.zulipFeatureLevel! >= 177; // TODO(server-7) | ||
final resolved = element.resolve(legacy: !supportsOperatorDm); |
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 this logic can actually be made somewhat cleaner by accepting a bit of mutation:
// TODO(server-7) remove special-casing | |
if (element is ApiNarrowDm) { | |
final supportsOperatorDm = store.connection.zulipFeatureLevel! >= 177; // TODO(server-7) | |
final resolved = element.resolve(legacy: !supportsOperatorDm); | |
if (element is ApiNarrowDm) { | |
final supportsOperatorDm = store.connection.zulipFeatureLevel! >= 177; // TODO(server-7) | |
element = element.resolve(legacy: !supportsOperatorDm); | |
} |
Then the rest of the logic fits into the same structure as the other narrow-element types. The operator gets written the same way as always, and the operand can look like:
case ApiNarrowDmModern():
final suffix = element.operand.length >= 3 ? 'group' : 'dm';
resultBuffer.write('${element.operand.join(',')}-$suffix');
case ApiNarrowPmWith():
final suffix = element.operand.length >= 3 ? 'group' : 'pm';
resultBuffer.write('${element.operand.join(',')}-$suffix');
case ApiNarrowDm():
assert(false, 'ApiNarrowDm should have been resolved');
check(narrowLink(store, const AllMessagesNarrow())) | ||
.equals(store.account.realmUrl.resolve('#narrow')); | ||
check(narrowLink(store, const AllMessagesNarrow(), nearMessageId: 1)) | ||
.equals(store.account.realmUrl.resolve('#narrow/near/1')); |
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.
(resolve
is good in these tests, though, because it lets the expected string start with a #
which is a helpful cue to the reader.)
e7d0e85
to
1f208c3
Compare
Thanks for the review! Pushing to save my work, but no need to re-review until until I've revised #179 and rebased this atop it. |
1f208c3
to
23f5f47
Compare
(Done.) |
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! All looks good with small nits below, and pending #179.
lib/model/compose.dart
Outdated
return resultBuffer.toString(); | ||
} | ||
|
||
const hashReplacements = { |
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: should be private
lib/model/compose.dart
Outdated
".": ".2E", | ||
}; | ||
|
||
final encodeHashComponentRegex = RegExp(r'[%().]'); |
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: should be private
lib/model/compose.dart
Outdated
|
||
// Corresponds to encodeHashComponent in Zulip web; | ||
// see web/shared/src/internal_url.ts. | ||
String encodeHashComponent(String str) { |
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.
should probably also be private
23f5f47
to
7128b94
Compare
Thanks! Revision pushed. |
7128b94
to
26b693a
Compare
We'll use this for quote-and-reply #116.
This revision sits atop my current revision of #179, which is another quote-and-reply subtask.