@@ -33,15 +33,15 @@ export const doNarrow =
3333 } ;
3434
3535/**
36- * Narrow to a /near/ link, possibly after reinterpreting it for a message move .
36+ * Adjust a /near/ link as needed to account for message moves .
3737 *
3838 * It feels quite broken when a link is clearly meant to get you to a
3939 * specific message, but tapping it brings you to a narrow where the message
4040 * *used* to be but isn't anymore because it was moved to a new stream or
4141 * topic. This was #5306.
4242 *
4343 * This action, when it can, recognizes when that's about to happen and
44- * instead narrows you to the message's current stream/topic.
44+ * instead finds the narrow for the message's current stream/topic.
4545 *
4646 * To do so, it obviously needs to know the message's current stream/topic.
4747 * If those can't be gotten from Redux, we ask the server. If the server
@@ -51,8 +51,8 @@ export const doNarrow =
5151 * N.B.: Gives a bad experience when the request takes a long time. We
5252 * should fix that; see TODOs.
5353 */
54- const doNarrowNearLink =
55- ( narrow : Narrow , nearOperand : number ) : ThunkAction < Promise < void >> =>
54+ const adjustNarrowForMoves =
55+ ( narrow : Narrow , nearOperand : number ) : ThunkAction < Promise < Narrow >> =>
5656 async ( dispatch , getState ) => {
5757 const state = getState ( ) ;
5858
@@ -61,46 +61,40 @@ const doNarrowNearLink =
6161 const zulipFeatureLevel = getZulipFeatureLevel ( state ) ;
6262 const allowEditHistory = getRealm ( state ) . allowEditHistory ;
6363
64- /**
65- * Narrow to the /near/ link without reinterpreting it for a message move.
66- *
67- * Use this when the link is meant to find the specific message
68- * identified by nearOperand, and:
69- * - nearOperand refers to a message that wasn't moved outside the
70- * narrow specified by the link, or
71- * - nearOperand *might* refer to a message that was moved, but we don't
72- * know; we've tried and failed to find out.
73- *
74- * Or, use this to insist on the traditional meaning of "near" before
75- * the message-move feature: take the narrow's stream/topic/etc.
76- * literally, and open to the message "nearest" the given ID (sent
77- * around the same time), even if the message with that ID isn't
78- * actually in the narrow [1].
79- *
80- * User docs on moving messages:
81- * https://zulip.com/help/move-content-to-another-stream
82- * https://zulip.com/help/move-content-to-another-topic
83- *
84- * [1] Tim points out, at
85- * https://chat.zulip.org/#narrow/stream/101-design/topic/redirects.20from.20near.20links/near/1343095 :
86- * "[…] useful for situations where you might replace an existing
87- * search for `stream: 1/topic: 1/near: 15` with
88- * `stream: 2/topic: 2/near: 15` in order to view what was happening
89- * in another conversation at the same time as an existing
90- * conversation."
91- */
92- const noMove = ( ) => {
93- dispatch ( doNarrow ( narrow , nearOperand ) ) ;
94- } ;
64+ // Many control-flow paths here simply return the original narrow.
65+ //
66+ // We do this when the link is meant to find the specific message
67+ // identified by nearOperand, and:
68+ // - nearOperand refers to a message that wasn't moved outside the
69+ // narrow specified by the link, or
70+ // - nearOperand *might* refer to a message that was moved, but we don't
71+ // know; we've tried and failed to find out.
72+ //
73+ // We also do this to insist on the traditional meaning of "near" before
74+ // the message-move feature: take the narrow's stream/topic/etc.
75+ // literally, and open to the message "nearest" the given ID (sent
76+ // around the same time), even if the message with that ID isn't
77+ // actually in the narrow [1].
78+ //
79+ // User docs on moving messages:
80+ // https://zulip.com/help/move-content-to-another-stream
81+ // https://zulip.com/help/move-content-to-another-topic
82+ //
83+ // [1] Tim points out, at
84+ // https://chat.zulip.org/#narrow/stream/101-design/topic/redirects.20from.20near.20links/near/1343095 :
85+ // "[…] useful for situations where you might replace an existing
86+ // search for `stream: 1/topic: 1/near: 15` with
87+ // `stream: 2/topic: 2/near: 15` in order to view what was happening
88+ // in another conversation at the same time as an existing
89+ // conversation."
9590
9691 const streamIdOperand =
9792 isStreamNarrow ( narrow ) || isTopicNarrow ( narrow ) ? streamIdOfNarrow ( narrow ) : null ;
9893 const topicOperand = isTopicNarrow ( narrow ) ? topicOfNarrow ( narrow ) : null ;
9994
10095 if ( streamIdOperand === null && topicOperand === null ) {
10196 // Message moves only happen by changing the stream and/or topic.
102- noMove ( ) ;
103- return ;
97+ return narrow ;
10498 }
10599
106100 // Grab the message and see if it was moved, so we can follow the move
@@ -122,8 +116,7 @@ const doNarrowNearLink =
122116 if ( zulipFeatureLevel < 120 ) {
123117 // api.getSingleMessage won't give us the message's stream and
124118 // topic; see there. Hopefully the message wasn't moved.
125- noMove ( ) ;
126- return ;
119+ return narrow ;
127120 }
128121 try {
129122 message = await api . getSingleMessage (
@@ -134,8 +127,7 @@ const doNarrowNearLink =
134127 ) ;
135128 } catch {
136129 // Hopefully the message, if it exists or ever existed, wasn't moved.
137- noMove ( ) ;
138- return ;
130+ return narrow ;
139131 }
140132 }
141133
@@ -144,23 +136,20 @@ const doNarrowNearLink =
144136 // TODO(server-5.0): Simplify away.
145137 if ( ! message ) {
146138 logging . error ( '`message` from api.getSingleMessage unexpectedly falsy' ) ;
147- noMove ( ) ;
148- return ;
139+ return narrow ;
149140 }
150141
151142 if ( message . type === 'private' ) {
152143 // A PM could never have been moved.
153- noMove ( ) ;
154- return ;
144+ return narrow ;
155145 }
156146
157147 if (
158148 ( topicOperand === null || topicOperand === message . subject )
159149 && ( streamIdOperand === null || streamIdOperand === message . stream_id )
160150 ) {
161151 // The message is still in the stream and/or topic in the link.
162- noMove ( ) ;
163- return ;
152+ return narrow ;
164153 }
165154
166155 if (
@@ -169,10 +158,9 @@ const doNarrowNearLink =
169158 ) {
170159 // The message was never in the narrow specified by the link. That'd
171160 // be an odd link to put in a message…anyway, perhaps we're meant to
172- // use the traditional meaning of "near"; see noMove's jsdoc
173- // for what that is.
174- noMove ( ) ;
175- return ;
161+ // use the traditional meaning of "near"; see large block comment at
162+ // top of function.
163+ return narrow ;
176164 }
177165 // If we couldn't access the edit history in the checks above, assume
178166 // the message was moved. It's the likeliest explanation why its topic
@@ -182,21 +170,31 @@ const doNarrowNearLink =
182170
183171 // Reinterpret the link's narrow with the message's current stream
184172 // and/or topic.
185- dispatch (
186- doNarrow (
187- caseNarrowDefault (
188- narrow ,
189- {
190- stream : ( ) => streamNarrow ( stream_id ) ,
191- topic : ( ) => topicNarrow ( stream_id , subject ) ,
192- } ,
193- ( ) => narrow ,
194- ) ,
195- nearOperand ,
196- ) ,
173+ return caseNarrowDefault (
174+ narrow ,
175+ {
176+ stream : ( ) => streamNarrow ( stream_id ) ,
177+ topic : ( ) => topicNarrow ( stream_id , subject ) ,
178+ } ,
179+ ( ) => narrow ,
197180 ) ;
198181 } ;
199182
183+ /**
184+ * Narrow to a /near/ link, possibly after reinterpreting it for a message move.
185+ *
186+ * See `adjustNarrowForMoves` for more discussion.
187+ *
188+ * N.B.: Gives a bad experience when the request takes a long time. We
189+ * should fix that; see `adjustNarrowForMoves`.
190+ */
191+ const doNarrowNearLink =
192+ ( narrow : Narrow , nearOperand : number ) : ThunkAction < Promise < void >> =>
193+ async ( dispatch , getState ) => {
194+ const adjustedNarrow = await dispatch ( adjustNarrowForMoves ( narrow , nearOperand ) ) ;
195+ dispatch ( doNarrow ( adjustedNarrow , nearOperand ) ) ;
196+ } ;
197+
200198export const messageLinkPress =
201199 ( href : string ) : ThunkAction < Promise < void >> =>
202200 async ( dispatch , getState , { getGlobalSettings } ) => {
0 commit comments