-
-
Notifications
You must be signed in to change notification settings - Fork 651
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
action sheet: add and implement delete topic option #3824
Conversation
Flow issue I cant figure out : In line Is this fixable, or is thus a flow bug? |
7e51546
to
d7f26a0
Compare
@ray-kraesig @gnprice please review. |
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.
Flow issue I cant figure out : In line
messageActionSheet.js:88
, flow assumesmessage
is of typeOutbox
even though the definition ismessage: Message | Outbox
. This causes a flow error.
Flow isn't assuming that message
is of type Outbox
; rather, you're assuming that message
is of type Message
. Flow is complaining (correctly, I believe) that you're trying to access a value that doesn't exist on Outbox
.
The fix for this is going to be somewhat more complicated than mere syntax. (For example, it will have to correctly handle the case where the message whose header was selected was an Outbox
, but has been successfully delivered while the user is still looking at the confirmation dialog.)
src/message/messageActionSheet.js
Outdated
{ | ||
text: 'Delete topic', | ||
onPress: () => api.deleteTopic(auth, message.stream_id, message.subject), | ||
}, |
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 option needs to be marked as style: 'destructive'
for iOS.
Also, it needs to report failure somehow (if #3795 is merged before this).
src/message/messageActionSheet.js
Outdated
@@ -79,6 +79,26 @@ const muteTopic = ({ auth, message }) => { | |||
}; | |||
muteTopic.title = 'Mute topic'; | |||
|
|||
const deleteTopic = ({ auth, message }) => { | |||
Alert.alert( | |||
`Are you sure you want to delete the topic '${message.subject}' ?`, |
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 construction, and therefore this wording, won't work as is: it's impossible to localize properly.
(Since the topic is a fixed string that shouldn't be inflected in any language, we might be able to do something like
const alertTitle = _('Deleting topic: $topic').replaceAll('$topic', message.subject);
but that implies a can of worms I'm not willing to casually open. @gnprice?)
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'll wait for @gnprice 's response, before pushing any changes.
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.
Erratum: this JS construct can't be localized, but the i18n library we have can help.
const alertTitle = _.intl.formatMessage(
{ id: "Are you sure you want to delete the topic '{topic}'?" },
{ topic: message.subject },
);
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 value of id
is being taken literally, without substitution for topic. Using defaultMessage
appears to work.
I referred to https://github.com/formatjs/react-intl/blob/master/docs/API.md#formatmessage
static/translations/messages_en.json
Outdated
@@ -50,6 +50,7 @@ | |||
"Copy to clipboard": "Copy to clipboard", | |||
"Link copied to clipboard": "Link copied to clipboard", | |||
"Mute topic": "Mute topic", | |||
"Delete topic":"Delete topic", |
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.
Space after the colon, for consistency.
@ray-kraesig I'm not sure how to proceed with this, with respect to the flow issue and handling the corner cases you mentioned #3824 (review). |
It's not really a Flow issue; it's a functional issue that Flow is recognizing. However, the delete-message action isn't a great template here, as it has the bug I mentioned: if a user long-presses a message before it's sent, then the delete-message callback will still attempt to delete it from the local Outbox (from which it's already been removed), rather than from the server, and no error will be given. Resolving that (for either delete-message or delete-topic) will require some thought and some testing. |
d7f26a0
to
1b112d8
Compare
src/topics/topicActions.js
Outdated
dispatch: Dispatch, | ||
getState: GetState, | ||
) => { | ||
const messages: $ReadOnlyArray<Message | Outbox> = getMessagesForNarrow(getState(), 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.
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.
Posted a message on CZO here: https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/.23M3824/near/815558
1b112d8
to
4580d95
Compare
b366b10
to
620a85c
Compare
@ray-kraesig have pushed some changes. |
620a85c
to
f8cdeaa
Compare
Tests are failing, I will update them if the current change is acceptable. |
sigh |
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.deleteTopic
and deleteMessagesForTopic
are self-contained enough that they could reasonably be moved to their own commit(s), to keep the size of this one down.
Please ensure tests pass before resubmitting for review.
src/message/messageActionSheet.js
Outdated
); | ||
Alert.alert( | ||
alertTitle, | ||
'This will also delete all messages in the topic.', |
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 string also needs localization support.
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.
Done
src/message/messageActionSheet.js
Outdated
{ | ||
id: 'delete_topic_warning', | ||
defaultMessage: "Are you sure you want to delete the topic '{topic}' ?", | ||
}, |
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.
defaultMessage
andid
should be identical.id
should be added to the localization database.- There should be no space before the question mark.
Formally, id
is the localization database key, while defaultMessage
is the message displayed if no message with key id
is found in the localization database for the current language.
However, we require that defaultMessage
be identical to the en
database's entry for id
(and, implicitly, that such an entry must exist). To ease the translation process, it's also our policy that id
should be identical to these wherever feasible.
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.
Changed.
src/message/messageActionSheet.js
Outdated
'This will also delete all messages in the topic.', | ||
[ | ||
{ | ||
text: 'Delete topic', |
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 string also needs localization support.
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.
Done
src/message/messageActionSheet.js
Outdated
onPress: async () => { | ||
dispatch(deleteMessagesForTopic(getNarrowFromMessage(message, ownEmail), message)).catch( | ||
err => { | ||
Alert.alert(`${_(deleteTopic.errorMessage)} '${message.subject}'`, err.message); |
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.
Concatenating localized strings is essentially never safe. (Consider Japanese, where this would need to be something like 「{topic}」というトピックを削除できません
.)
This would need to be a separately localized string... except that there shouldn't be any need for this interior Alert
at all, because failure here should be propagated up to and handled by the new error-reporting mechanism in showActionSheet
.
In order to do that, you'll effectively have to await
on the Alert.alert
– which isn't await
able on its own, so you'll need to promisify it appropriately. See, e.g., here for a partial example.
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.
Fixed.
src/message/messageActionSheet.js
Outdated
style: 'destructive', | ||
}, | ||
{ | ||
text: 'Cancel', |
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 string also needs localization support.
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.
Done.
61d97a5
to
c1243e8
Compare
@ray-kraesig I've pushed some changes. I've also created a new test for the 'Delete topic' button. |
c1243e8
to
0a959bc
Compare
Should tests always pass before requesting a review? |
Almost always, yes. (Although see below.)
That is sometimes the case! However, when it is, the review request should always be accompanied with an explanation of why the tests don't pass, and what questions you need to have answered in order to fix them. Otherwise, I'm probably going to infer that you're still working on the PR, and that it's not actually ready for review. Putting off fixing the tests until you get a first review may be appropriate when those fixes would be a significant and potentially wasteful undertaking in themselves. (Although one would definitely still need a comment stating and explaining this.) However, in this case:
|
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.
If we're going around again, one minor fix to the code itself:
src/message/messageActionSheet.js
Outdated
[ | ||
{ | ||
text: _('Delete topic'), | ||
onPress: async () => { |
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 shouldn't need to be async
. (Compare with the other branch.)
@gnprice I'm having some trouble converting
I think flow is not not using our mockStore defined in
I tried a lot, but couldn't think of how to resolve these :'( |
5ace59f
to
4b1f264
Compare
@ray-kraesig I've removed the |
The lines of code quoted here appear to be from |
Oh, this is hilarious! Thanks! I spent such a long time on trying to fix that file :'( |
d19a024
to
0bf5b44
Compare
@gnprice @ray-kraesig I've made all the changes - the tests are type-checked now. Can you please review this? Thanks! |
Not sure why the ordering of commits is different in GitHub UI, here's the output of
|
Well spotted! 🙂 Greg explained this to me here; it's a bug in GitHub where they're sorting by author date instead of committer date. I think we have this documented somewhere, but I haven't found it in a quick search on https://zulip.readthedocs.io/en/latest/overview/index.html, where most of our git-related documentation lives. |
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.
A few nitpicks about data location. (Also, there are merge conflicts, so a rebase is needed anyway.)
src/__tests__/exampleData.js
Outdated
@@ -137,6 +138,13 @@ const displayRecipientFromUser = (user: User): PmRecipientUser => { | |||
}); | |||
}; | |||
|
|||
export const narrow: 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.
There's probably no benefit in having a Narrow
here; pretty much every test that needs a Narrow
needs to specify what sort of Narrow
it's using.
In particular, this is true of your tests in mAS-test.js
, which actually assume that they're getting a Narrow
with this structure and these values: if this were replaced with, e.g. [{operator: 'is', operand: 'starred'}]
, those tests would break. That makes this not "example data" – it's test-specific data, and should go alongside the tests it's a part of.
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.
You're right - thanks for the explanation!
src/__tests__/exampleData.js
Outdated
historical: {}, | ||
is_me_message: {}, | ||
}, | ||
mute: [['announce', 'stream events']], |
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.
Also test-specific data. (Probably belongs in baseBackgroundData
.)
...eg.streamMessage(), | ||
id: 3, | ||
reactions: [], | ||
avatar_url: '', | ||
client: '', |
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.
Since you're using eg.streamMessage()
now, you can just do const message = eg.streamMessage({ id: 3 })
here (and similarly elsewhere) – there's no need to explicitly specify any properties which aren't relevant to the test.
src/__tests__/exampleData.js
Outdated
}, | ||
mute: [['announce', 'stream events']], | ||
ownUser: selfUser, | ||
subscriptions: [ |
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.
Idem.
0bf5b44
to
5b3acf4
Compare
@ray-kraesig thank you for the review. I have made the changes you requested. Please review it again. |
Oh - that's interesting. Thank's @chrisbobbe ! |
We are moving towards making all our tests type-checked. This commit is a move in that direction.
The 'delete topic' endpoint allows for the deletion of all messages in a stream for a given topic.
'deleteMessagesForTopic' deletes all messages (including those in outbox) for a given topic in a stream.
The webapp offers the feature to delete all messages within a topic for a stream. This feature is missing in the mobile app. Add 'Delete topic' button in message action sheet. Fixes zulip#3806.
5b3acf4
to
b886b3d
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.
To avoid another round-trip (over the weekend, at that) with only trivial changes, I've appended a couple of commits to this PR. The first addresses the issue below; the second was a suggestion from @gnprice when reviewing the former.
src/topics/topicActions.js
Outdated
@@ -34,3 +36,25 @@ export const fetchTopicsForActiveStream = (narrow: Narrow) => async ( | |||
} | |||
dispatch(fetchTopics(stream.stream_id)); | |||
}; | |||
|
|||
export const deleteMessagesForTopic = (narrow: Narrow, message: Message | Outbox) => async ( |
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 should have noticed this during one of the preceding passes, but – why does this function take a Message | Outbox
? All the information it needs is in the Narrow
.
(For that matter, it would be fairly simple just to pass the stream name and topic as parameters – there's no need to wrap them up in a Narrow
object which you then have to check, or assume, to be of the right shape.
Narrow
should be a type we'd want here. Unfortunately it's really a server API type that we've pressed into service for insufficiently-closely-related purposes in the mobile app. 😞 We've been discussing its replacement for some time.)
`deleteMessagesForTopic` currently takes both a `Narrow` and a `Message | Outbox`. However, this is redundant; all the relevant information can be obtained from either one. Furthermore, neither is really necessary: we can just ask the caller to extract the data and hand it to us. Since the caller already knows that it's operating on a `stream` message, it's actually less trouble than constructing the `Narrow`.
Outbox messages of type `private` will not have a comparable `.display_recipient`, and will have no `.subject` at all. While the comparisons currently performed are harmless and will, in this case, yield the right result, it's cleaner conceptually (and slightly safer when refactoring) to actually check the type first.
b4135c7
to
ad1556b
Compare
And merged. Thanks! |
Closes #3806