Skip to content

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

Merged
merged 4 commits into from
Jun 16, 2023

Conversation

chrisbobbe
Copy link
Collaborator

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.

@chrisbobbe chrisbobbe added the a-compose Compose box, autocomplete, attaching files/images label Jun 14, 2023
@chrisbobbe chrisbobbe requested a review from gnprice June 14, 2023 23:07
@chrisbobbe chrisbobbe force-pushed the pr-compose-narrow-link branch 2 times, most recently from 66983a7 to e7d0e85 Compare June 15, 2023 18:51
Copy link
Member

@gnprice gnprice left a 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.

};

// Corresponds to encodeHashComponent in Zulip web;
// see web/shared/src/internal_url.js.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// see web/shared/src/internal_url.js.
// see web/shared/src/internal_url.ts.

since the source file is in TS now

// see web/shared/src/internal_url.js.
String encodeHashComponent(String str) {
return Uri.encodeComponent(str)
.replaceAllMapped(RegExp(r'[%().]'), (Match m) => hashReplacements[m[0]!]!);
Copy link
Member

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.

resultBuffer.write('/near/$nearMessageId');
}

return store.account.realmUrl.resolve(resultBuffer.toString());
Copy link
Member

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:

Suggested change
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.)

final operand = resolved.operand;
final operandSuffix = operand.length >= 3
? 'group'
: (supportsOperatorDm ? 'dm' : 'pm'); // TODO(?) CZO actually gives a slugified full_name here…??
Copy link
Member

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.

Copy link
Member

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.

Comment on lines 144 to 147
// 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);
Copy link
Member

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:

Suggested change
// 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'));
Copy link
Member

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.)

@chrisbobbe chrisbobbe force-pushed the pr-compose-narrow-link branch from e7d0e85 to 1f208c3 Compare June 15, 2023 20:23
@chrisbobbe
Copy link
Collaborator Author

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.

@chrisbobbe chrisbobbe force-pushed the pr-compose-narrow-link branch from 1f208c3 to 23f5f47 Compare June 15, 2023 21:01
@chrisbobbe
Copy link
Collaborator Author

[…] until I've revised #179 and rebased this atop it.

(Done.)

Copy link
Member

@gnprice gnprice left a 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.

return resultBuffer.toString();
}

const hashReplacements = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should be private

".": ".2E",
};

final encodeHashComponentRegex = RegExp(r'[%().]');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should be private


// Corresponds to encodeHashComponent in Zulip web;
// see web/shared/src/internal_url.ts.
String encodeHashComponent(String str) {
Copy link
Member

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

@chrisbobbe chrisbobbe force-pushed the pr-compose-narrow-link branch from 23f5f47 to 7128b94 Compare June 16, 2023 21:12
@chrisbobbe
Copy link
Collaborator Author

Thanks! Revision pushed.

@gnprice gnprice force-pushed the pr-compose-narrow-link branch from 7128b94 to 26b693a Compare June 16, 2023 22:33
@gnprice gnprice merged commit 26b693a into zulip:main Jun 16, 2023
@chrisbobbe chrisbobbe deleted the pr-compose-narrow-link branch June 16, 2023 22:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a-compose Compose box, autocomplete, attaching files/images
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants