Skip to content

Commit cdf4a9a

Browse files
chrisbobbegnprice
authored andcommitted
msglist: Give error alert when link parses to Narrow we don't have data for
For PM narrows, we've been showing a really busted-looking screen when we parse the link to a Narrow but we don't have data for the user IDs; that's #5702. For stream narrows, we've been taking the user out to a browser, which is likely to be futile; that's #5698. Fix both issues by, when we don't have data, preventing any navigation and showing an error alert instead. In the streams case (#5698), getNarrowFromLink wasn't distinguishing the case of an unknown/nonexistent stream from the case of an unparseable link. To change the behavior for the former but not the latter case, we relax `parseStreamOperand` so that it still parses a "new"-format operand even if it points to data we don't know about. (Like we already do for PM operands.) Fixes: #5698 Fixes: #5702
1 parent 37d2a39 commit cdf4a9a

File tree

5 files changed

+41
-12
lines changed

5 files changed

+41
-12
lines changed

src/chat/narrowsSelectors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ export const getStreamInNarrow: Selector<Subscription | {| ...Stream, in_home_vi
211211
},
212212
);
213213

214+
/**
215+
* Whether PerAccountState has all data mentioned in `narrow` (user IDs etc.)
216+
*/
214217
export const isNarrowValid: Selector<boolean, Narrow> = createSelector(
215218
(state, narrow) => narrow,
216219
state => getStreamsById(state),

src/message/messagesActions.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import {
1919
topicOfNarrow,
2020
streamNarrow,
2121
caseNarrowDefault,
22+
isConversationNarrow,
2223
} from '../utils/narrow';
2324
import { hasMessageEverBeenInStream, hasMessageEverHadTopic } from './messageSelectors';
2425
import { showErrorAlert } from '../utils/info';
26+
import { isNarrowValid } from '../chat/narrowsSelectors';
2527

2628
/**
2729
* Navigate to the given narrow.
@@ -232,14 +234,22 @@ export const messageLinkPress =
232234

233235
const narrow = getNarrowFromLink(parsedUrl, auth.realm, streamsById, streamsByName, ownUserId);
234236

235-
// TODO(#5698): In some cases getNarrowFromLink successfully parses the link, but
236-
// finds it points somewhere we can't see: in particular, to a stream
237-
// that's hidden from our user (perhaps doesn't exist.) For those,
238-
// perhaps give an error instead of falling back to opening in browser,
239-
// which should be futile.
240237
if (narrow) {
241238
// This call is OK: `narrow` is truthy, so isNarrowLink(…) was true.
242239
const nearOperand = getNearOperandFromLink(parsedUrl, auth.realm);
240+
241+
if (!isNarrowValid(state, narrow)) {
242+
// E.g., a stream that's hidden from our user (perhaps doesn't exist).
243+
const msg =
244+
nearOperand !== null
245+
? 'That message could not be found.'
246+
: isConversationNarrow(narrow)
247+
? 'That conversation could not be found.'
248+
: 'Those messages could not be found.';
249+
showErrorAlert(_('Cannot open link'), _(msg));
250+
return;
251+
}
252+
243253
if (nearOperand === null) {
244254
dispatch(doNarrow(narrow));
245255
return;

src/utils/__tests__/internalLinks-test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ describe('getNarrowFromLink (part 2)', () => {
263263
expectStream(`${streamGeneral.stream_id}-general`, [streamGeneral], streamGeneral);
264264
});
265265

266+
test('if stream not found, use stream ID anyway', () => {
267+
expectStream(`${streamGeneral.stream_id}-general`, [], streamGeneral);
268+
});
269+
266270
test('on stream link with wrong name: ID wins', () => {
267271
expectStream(`${streamGeneral.stream_id}-nonsense`, [streamGeneral], streamGeneral);
268272
expectStream(`${streamGeneral.stream_id}-`, [streamGeneral], streamGeneral);

src/utils/internalLinks.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,14 @@ export const decodeHashComponent = (string: string): string => {
6868
/**
6969
* Parse the operand of a `stream` operator, returning a stream ID.
7070
*
71-
* Return null if the operand doesn't match any known stream.
71+
* The ID might point to a stream that's hidden from our user (perhaps
72+
* doesn't exist). If so, most likely the user doesn't have permission to
73+
* see the stream's existence -- like with a guest user for any stream
74+
* they're not in, or any non-admin with a private stream they're not in.
75+
* Could be that whoever wrote the link just made something up.
76+
*
77+
* Returns null if the operand has an unexpected shape, or has the old shape
78+
* (stream name but no ID) and we don't know of a stream by the given name.
7279
*/
7380
// Why does this parser need stream data? Because the operand formats
7481
// ("new" and "old") collide, and in choosing which format to apply, we
@@ -91,12 +98,14 @@ const parseStreamOperand = (operand, streamsById, streamsByName): null | number
9198
return stream.stream_id;
9299
}
93100

94-
// Not any stream we know. (Most likely this means a stream the user
95-
// doesn't have permission to see the existence of -- like with a guest
96-
// user for any stream they're not in, or any non-admin with a private
97-
// stream they're not in. Could also be an old-format link to a stream
98-
// that's since been renamed… or whoever wrote the link could always have
99-
// just made something up.)
101+
if (newFormatStreamId != null) {
102+
// Neither format found a Stream, so it's hidden or doesn't exist. But
103+
// at least we have a stream ID; give that to the caller. (See jsdoc.)
104+
return newFormatStreamId;
105+
}
106+
107+
// Unexpected shape, or the old shape and we don't know of a stream with
108+
// the given name.
100109
return null;
101110
};
102111

static/translations/messages_en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"Invalid image URL.": "Invalid image URL.",
2323
"Cannot open link": "Cannot open link",
2424
"Invalid URL.": "Invalid URL.",
25+
"That message could not be found.": "That message could not be found.",
26+
"That conversation could not be found.": "That conversation could not be found.",
27+
"Those messages could not be found.": "Those messages could not be found.",
2528
"Invisible mode": "Invisible mode",
2629
"{num_of_people, plural,\n one {This message has been <z-link>read</z-link> by {num_of_people} person:}\n other {This message has been <z-link>read</z-link> by {num_of_people} people:}}": "{num_of_people, plural,\n one {This message has been <z-link>read</z-link> by {num_of_people} person:}\n other {This message has been <z-link>read</z-link> by {num_of_people} people:}}",
2730
"View read receipts": "View read receipts",

0 commit comments

Comments
 (0)