-
-
Notifications
You must be signed in to change notification settings - Fork 668
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
Allow viewing message edit history #4173
base: main
Are you sure you want to change the base?
Conversation
685401d
to
89b89f4
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, @agrawal-d!
I'm not sure why, but I'm just getting a blank page in the WebView on my iOS device; it just says about:blank
. Did you run into this on Android, and was there something special you had to do to get it to work?
src/webview/static/base.css
Outdated
background-color: #fcdcd8; | ||
text-decoration: line-through; | ||
word-break: break-all; | ||
} |
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: newline at end of file
|
||
/** Docs unclear but suggest absent if only content edited. */ | ||
topic?: string, |
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.
by experimenting it's possible to determine which fields are always present.
It sounds like you've tested at least the common cases (a topic was edited but not the content, or vice versa) and seen that these fields fields were there in those cases.
I suppose, to make this entirely true, we have to also be sure that there won't be any surprising cases we haven't tested yet. It might be easier to post in #mobile-team with any questions about the docs, and that may have the side-effect of improving the docs for the next person who has the same questions. 🙂
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.
That discussion here:
https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/get-message-history.20docs/near/921587
Whenever we find a place where the API docs seem to be wrong, I always want to discuss it in chat and bring it to Tim's attention there. Reasons include:
- I want the API docs to converge on being right too 🙂 -- if we have a steady state where our types say one thing and the API docs say another, that'll be confusing to a later reader
- If for whatever reason the API docs don't get updated promptly, I'll want the comments in our code to be super explicit that the API docs say X but the truth is Y, and here's how we've determined the truth is Y. That gives the future reader a hope of not being confused by the contradiction, because at least they can see that the author of the types was aware of what the API docs said, and wasn't just misreading them, or working from what at that future time is an out-of-date version
- The answer might be that the documented API is as intended, and the server's behavior that disagrees is a bug!
As of right now (since a few minutes ago), the status is that Tim confirms the current behavior is what you're seeing empirically -- but potentially wants to fix it by changing the behavior to match the docs instead of vice versa.
Oh no, that's bad! Just tested this again, and it worked. |
The API Guide says:
@chrisbobbe Does changing |
src/webview/html/editHistoryHtml.js
Outdated
|
||
default: | ||
// eslint-disable-next-line no-console | ||
console.error('Invalid edit_flag'); |
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.
Let's get this logged to Sentry, with logging.error
, and include the value of edit_flag
in the second argument, extras
.
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.
Ok
(It's actually impossible to reach here :p)
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.
(It's actually impossible to reach here :p)
Quick tip: you can add ensureUnreachable(edit_flag);
here to assert this to the type-checker (and ask it to verify that it can prove it.) That helps prevent errors before they even reach users 🙂 if say a new case is added here in the future.
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'm getting the following error on adding that:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/webview/html/editHistoryHtml.js:76:25
Cannot call ensureUnreachable with edit_flag bound to x because:
• string [1] is incompatible with empty [2].
• string [3] is incompatible with empty [2].
• string [4] is incompatible with empty [2].
• string [5] is incompatible with empty [2].
src/webview/html/editHistoryHtml.js
[1] 10│ const TOPIC_EDITED = 'TOPIC_EDITED';
[3] 11│ const CONTENT_EDITED = 'CONTENT_EDITED';
[4] 12│ const BOTH_EDITED = 'BOTH_EDITED';
[5] 13│ const NOT_EDITED = 'NOT_EDITED';
:
73│ }
74│
75│ default:
76│ ensureUnreachable(edit_flag);
77│ logging.error('Invalid edit_flag', { editFlag: edit_flag });
78│ break;
79│ }
src/types.js
[2] 42│ export function ensureUnreachable(x: empty) {}
Found 4 errors
I think Flow is not able to understand that this case is unreachable?
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.
Ah -- it's that the type of edit_flag
is simply string
, instead of being an enum 'TOPIC_EDITED' | 'CONTENT_EDITED' | 'BOTH_EDITED' | 'NOT_EDITED'
.
You could make it an enum by annotating it with that type (and then probably doing something to avoid initializing to undefined
and mutating.)
Or, looking a bit more at the code: this flag is so short-lived, just from an if/else-if chain to a switch
right after it. I think it'd simplify things to just fuse those together: have an if/else-if chain with those conditions, and with the cases' bodies as their bodies. Or, a bit further: a two-level tree of if/else statements:
if (snapshot.prev_content === undefined) {
if (snapshot.prev_topic === undefined) {
…
} else {
…
}
} else {
…
}
Thanks for the review, @chrisbobbe, and for the detailed investigation on CZO. I've pushed some changes, and I think it may work on iOS too, for now. |
(For the record: the comments I made on CZO were here). |
src/webview/MessageList.js
Outdated
// Paranoia^WSecurity: only load `baseUrl`, and only load it once. Any other | ||
// requests should be handed off to the OS, not loaded inside the WebView. |
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.
No need to have three copies of this comment 🙂
I think the jsdoc on the newly-factored-out function is the right place to keep it -- it's describing the function's interface. (Potentially we could have some other WebView in the future that is actually meant to load subsequent pages, and then this paragraph would explain why this function would no longer be the right logic to use for that WebView.)
* Paranoia^WSecurity: only load `baseUrl`, and only load it once. Any other | ||
* requests should be handed off to the OS, not loaded inside the WebView. | ||
*/ | ||
export default (baseUrl: string) => { |
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.
Let's move the assetsPath
logic out to this new file too. Both that logic and this are basically gritty details of how the webview works -- we're interested in one at the same times we're interested in the other -- and both are distractions, where they currently live in MessageList, from the main job of that component.
|
||
/** Docs unclear but suggest absent if only content edited. */ | ||
topic?: string, |
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.
That discussion here:
https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/get-message-history.20docs/near/921587
Whenever we find a place where the API docs seem to be wrong, I always want to discuss it in chat and bring it to Tim's attention there. Reasons include:
- I want the API docs to converge on being right too 🙂 -- if we have a steady state where our types say one thing and the API docs say another, that'll be confusing to a later reader
- If for whatever reason the API docs don't get updated promptly, I'll want the comments in our code to be super explicit that the API docs say X but the truth is Y, and here's how we've determined the truth is Y. That gives the future reader a hope of not being confused by the contradiction, because at least they can see that the author of the types was aware of what the API docs said, and wasn't just misreading them, or working from what at that future time is an out-of-date version
- The answer might be that the documented API is as intended, and the server's behavior that disagrees is a bug!
As of right now (since a few minutes ago), the status is that Tim confirms the current behavior is what you're seeing empirically -- but potentially wants to fix it by changing the behavior to match the docs instead of vice versa.
src/api/modelTypes.js
Outdated
// The prev_* field is only present if the respective attribute | ||
// of the message was changed. | ||
prev_content?: string, | ||
prev_rendered_content?: string, | ||
content_html_diff?: string, | ||
prev_topic?: string, |
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 summary is concise in a similar way to the corresponding bit of the API docs -- but I think it would actually be clearer (and not even many more words, if more at all) to be less abstract, more concrete and explicit:
// These are present just if the content was edited.
prev_content?: string,
 prev_rendered_content?: string,
 content_html_diff?: string,
// Present just if the topic was edited.
prev_topic?: string,
(... OK, and I just counted: that's 16 words instead of 15. 😁 )
src/webview/html/editHistoryHtml.js
Outdated
|
||
default: | ||
// eslint-disable-next-line no-console | ||
console.error('Invalid edit_flag'); |
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.
Ah -- it's that the type of edit_flag
is simply string
, instead of being an enum 'TOPIC_EDITED' | 'CONTENT_EDITED' | 'BOTH_EDITED' | 'NOT_EDITED'
.
You could make it an enum by annotating it with that type (and then probably doing something to avoid initializing to undefined
and mutating.)
Or, looking a bit more at the code: this flag is so short-lived, just from an if/else-if chain to a switch
right after it. I think it'd simplify things to just fuse those together: have an if/else-if chain with those conditions, and with the cases' bodies as their bodies. Or, a bit further: a two-level tree of if/else statements:
if (snapshot.prev_content === undefined) {
if (snapshot.prev_topic === undefined) {
…
} else {
…
}
} else {
…
}
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 @agrawal-d ! This will be great.
Some comments below, and also above because GitHub sometimes feels like doing that 😝 (Specifically, it feels like doing that when I reply to an existing comment thread while partway through preparing a review.)
src/webview/html/editHistoryHtml.js
Outdated
let displayedContent: string = ''; | ||
let tag: string = ''; | ||
let edit_flag; |
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 is a lot of mutable let
bindings. In general we prefer const
-- it makes the code simpler to reason about both for us human readers, and for the type-checker.
Looking at how they're used, each of these ends up always being mutated exactly once before it's used. So that suggests that what's "really" going on is we're initializing each of these bindings to a value and then never mutating it thereafter -- which is what const
expresses. I think we can pretty naturally rearrange this logic so that it works that way explicitly.
Specifically, one natural way to refactor this that expresses the real structure more directly would be to pull out a helper function that returns tag
and displayedContent
. (And that in turn could use a helper returning edit_flag
, or the logic that produces and consumes that flag could just be fused together as I mentioned at #4173 (comment) .)
src/webview/html/editHistoryHtml.js
Outdated
user ? user.full_name : 'unknown user' | ||
}</span>`; | ||
|
||
// prev_topic is always present for this case. $FlowFixMe. |
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.
Hmm, I missed these fixmes the first couple of times I looked at this code!
Please always put fixme comments at the start of the comment line. (Ditto for TODO, and for any other special keywords that are meant to jump out at the reader to draw scrutiny.)
src/webview/html/editHistoryHtml.js
Outdated
// prev_topic and content_html_diff are always present for this case. $FlowFixMe. | ||
displayedContent = template`Topic: | ||
<span class="highlight_text_inserted">${snapshot.topic}</span> | ||
<span class="highlight_text_deleted">${snapshot.prev_topic}</span> | ||
<br/>$!${snapshot.content_html_diff}`; |
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.
And now that I look at the fixmes, I definitely prefer the plan where these cases just get pushed into the if
and else
bodies. 😄
If this were inside the if
or else
, Flow would immediately follow this logic around prev_topic
. It doesn't know the correlation between prev_content
and content_html_diff
... but we could simply use the latter in the conditional, since that's what we're actually relying on.
(Alternatively, in some other circumstances it can certainly make sense to have this flavor of "if this condition, then that property is present / non-nullish" logic that's twistier than the type-checker is able to follow. When we do, instead of a fixme what I'd much rather have is an assert, throwing an exception if the impossible happens -- that way (a) it's explicit in the actual code exactly what assumption we're making, (b) Flow can check that we're relying only on those assumptions we say we're making, and (c) if we're wrong (perhaps after future changes to the code by someone else), we get an exception so we can learn about the problem.)
src/webview/html/editHistoryHtml.js
Outdated
return template` | ||
$!${script(null, auth)} | ||
$!${css(theme)} | ||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> | ||
<body style="overflow-x: hidden;"> | ||
$!${html} | ||
</body>`; |
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 are some subtle bits here 🙂. Particularly that meta
element -- I can only guess at what it does, and would have to look it up.
And this sure has a lot in common with the code in html.js
! In particular, the subtle bits are exactly the same. (Then the latter has one additional subtle bit, webkitBugWorkaround
, which is likely unnecessary here but would be harmless.)
Anything that's subtle, I am keen on not duplicating. When code is duplicated, future bugfixes and incidental changes tend to go on just one or the other -- and then it's self-reinforcing: as they diverge, it gets harder to make the same change on both of them or be sure the change would do the same thing on both, so more changes get applied to just the one where the bug happened to be spotted. So because we need this in two places, let's factor it out into one place we can call from both of them.
src/webview/html/editHistoryHtml.js
Outdated
return template` | ||
<div class="edit-history-block"> | ||
<div class="static-timestamp">${editedTime}</div> | ||
<span class="edit-history-content">$!${displayedContent}</span> | ||
<div class="message-tags">$!${tag}</div> |
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 appreciate the careful use of template`…`
, ${…}
, and $!${…}
! You may already know that this is an important surface area for preventing HTML injections.
Which we actually had, until a couple of years ago -- the day I discovered that, I fixed it and then built this little templating/escaping system to help us prevent it ever happening again.
I've looked closely at these templates, and I believe everything gets escaped correctly.
A couple of things I try to do to help keep it that way, as people make changes in the future:
- Background ideas:
- Conceptually there are two types here: text string, and HTML string. Sadly we haven't taught the type-checker the difference -- they're both
string
-- so we have to keep track of the distinction ourselves. - The ultimate return value is an HTML string. Most of the inputs are text strings, and in general any string that isn't explicitly meant to be used as raw HTML is a text string. The only way to get an HTML string from a text string is to hand it to
template
; and an interpolation intemplate
should have$!$
if it's an HTML string and plain$
if it's a text string. - So the goal is to be able to look at a use of
$
or$!$
-- especially a use of$!$
-- and immediately tell, without a lot of reading through other code, whether it looks right, or should be the other way instead.
- Conceptually there are two types here: text string, and HTML string. Sadly we haven't taught the type-checker the difference -- they're both
- One thing that helps: For each variable that represents a fragment that'll get used in templates, give it a name that's explicit about whether it's a text string or an HTML string. Especially if it's an HTML string, make that explicit. That way when there's an interpolation like
$!${someVar}
, and it's not a name like$!${someHtml}
, it jumps out as suspicious.- This is why
messageHeaderAsHtml.js
has variablestopicHtml
, for example. - So here,
tagHtml
would be a good name fortag
. SimilarlydisplayedContentHtml
.
- This is why
- Another thing that helps: make it so that every last backtick-interpolated string in the HTML-rendering code uses
template
. That way any that don't are suspicious.- This commit adds one non-
template
backtick-literal:editedTime
. The behavior is fine, because it truly is a text string, and it gets duly escaped when it's used. But it'd be good to maintain that invariant, to help make any future oversights stand out. One simple way would be to just inline that expression in the place whereeditedTime
is used.
- This commit adds one non-
For history see 9941453, where I fixed the HTML injection (in 2018-05.) Some discussion of these hygiene practices in af5a56f, a few commits later; template
itself was introduced in the latter's parent.
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 for the detailed explaination!
.edit-history-block{ | ||
padding:8px; | ||
border-bottom:1px solid hsla(0, 0%, 50%, 0.25); | ||
} | ||
.edit-history-block .static-timestamp{ | ||
margin-top: 8px; | ||
text-align: right; | ||
} | ||
.edit-history-content{ | ||
-webkit-user-select: text; /* Safari 3.1+ */ | ||
-moz-user-select: text; /* Firefox 2+ */ | ||
-ms-user-select: text; /* IE 10+ */ | ||
user-select: text; /* Standard syntax */ | ||
-khtml-user-select: text; | ||
-webkit-touch-callout: text; | ||
word-wrap: break-word; | ||
} | ||
.highlight_text_inserted { | ||
color: #9effa1; | ||
background-color: hsla(120,64%,95%,.3); | ||
} | ||
.highlight_text_deleted { | ||
color: red; | ||
background-color: #fcdcd8; | ||
text-decoration: line-through; | ||
word-break: break-all; | ||
} |
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.
Are these based on something in the webapp, or did you develop them afresh?
Either is fine 🙂 -- I'd just like to know (and have the commit message say so for future readers), and if it's from the webapp have a quick comment in the file saying where exactly it's from. (Including if it's based loosely or in part on something in the webapp.) That will save us some archaeological work in any future effort to unify the message CSS on web and mobile.
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 .highlight_text_*
CSS is borrowed form the web-app, the others are new.
src/message/EditHistory.js
Outdated
<WebView | ||
source={{ baseUrl, html }} | ||
originWhitelist={['file://']} | ||
style={{ backgroundColor: 'transparent' }} | ||
onShouldStartLoadWithRequest={getOnShouldStartLoadWithRequest(baseUrl)} | ||
onError={(msg: mixed) => { | ||
// eslint-disable-next-line no-console | ||
console.error(msg); | ||
}} | ||
/> |
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 is another example of a subtle set of details I'm keen on not duplicating 🙂 .
Most of this should get factored out to go along with getOnShouldStartLoadWithRequest
-- the originWhitelist
, the transparent-background style
, the onShouldStartLoadWithRequest
. The bits that the factored-out code (perhaps a new React component?) should take as parameters (or React props) are something like: html
, the onMessage
and onError
callbacks, and maybe baseUrl
.
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 for the detailed review @gnprice! 😄
I've made the changes you suggested.
src/webview/html/editHistoryHtml.js
Outdated
return template` | ||
<div class="edit-history-block"> | ||
<div class="static-timestamp">${editedTime}</div> | ||
<span class="edit-history-content">$!${displayedContent}</span> | ||
<div class="message-tags">$!${tag}</div> |
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 for the detailed explaination!
.edit-history-block{ | ||
padding:8px; | ||
border-bottom:1px solid hsla(0, 0%, 50%, 0.25); | ||
} | ||
.edit-history-block .static-timestamp{ | ||
margin-top: 8px; | ||
text-align: right; | ||
} | ||
.edit-history-content{ | ||
-webkit-user-select: text; /* Safari 3.1+ */ | ||
-moz-user-select: text; /* Firefox 2+ */ | ||
-ms-user-select: text; /* IE 10+ */ | ||
user-select: text; /* Standard syntax */ | ||
-khtml-user-select: text; | ||
-webkit-touch-callout: text; | ||
word-wrap: break-word; | ||
} | ||
.highlight_text_inserted { | ||
color: #9effa1; | ||
background-color: hsla(120,64%,95%,.3); | ||
} | ||
.highlight_text_deleted { | ||
color: red; | ||
background-color: #fcdcd8; | ||
text-decoration: line-through; | ||
word-break: break-all; | ||
} |
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 .highlight_text_*
CSS is borrowed form the web-app, the others are new.
src/webview/MessageList.js
Outdated
class MessageList extends Component<Props> { | ||
static contextType = ThemeContext; | ||
context: ThemeColors; | ||
|
||
webview: ?WebView; | ||
webview = null; |
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.
Hmm, what was the reason to lose the type annotation here? Is it possible to bring it back? If it can't be brought back simply as-is, maybe we could try with the newer React.createRef
API? (My example at #4101 (comment) might be helpful.)
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 innerRef is actually a ref to react-native-webview
, so to annotate the type, I need to import both ZulipWebView
and WebView
in MessageList.js
. Done this now.
src/common/WebView.js
Outdated
@@ -0,0 +1,165 @@ | |||
/* @flow strict-local */ |
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.
f1a7aca webview: Refactor core logic into a common component.
Cool! It's great to see this core logic centralized so we don't have to duplicate it.
With the choice to do it in a new React component, one thing to be aware of (even if nothing actually changes, and it turns out to be fine) is that it adds a layer of complexity if we want to change the behavior (or debug something) around React components' lifecycles, particularly updates, a.k.a. re-render
s. I mentioned this briefly here but didn't go into very much detail.
The WebView
component from react-native-webview
has its own ideas about when it should update; it can choose to be a stateless "function component", a PureComponent
, or a regular Component
, and, if the latter, it can give hints about its desired behavior in shouldComponentUpdate
. We should assume its updates may be triggered by updates in its parent component.
There's now another layer between that WebView
and our MessageList
(and between that WebView
and the new EditHistory
, but I think it's much more important to think about MessageList
).
Our MessageList
component implements shouldComponentUpdate
to say that we just never want the MessageList
component to update. (React would probably say this is controversial; they recommend, "Do not rely on it to “prevent” a rendering, as this can lead to bugs", in this doc. But so far we haven't run into problems with this.)
I haven't tested, but I think everything should actually turn out fine. I would be concerned if our new WebView
component did some additional updates of its own, not prompted by an update in its parent. The only case I can think of where this would likely happen is if our new WebView
component had some state
that it decided, on its own, to change. (From the shouldComponentUpdate
doc: "Note that returning false
does not prevent child components from re-rendering when their state changes.")
One (optional) simple thing that could be done about this case, for a bit of peace of mind, I think, is to make our new WebView
a stateless "function component" (doc) and add a note that we shouldn't introduce state
without considering the extra complexity it would add around re-render
ing. I very rarely use "function component"s but I think it would be appropriate in this case. If we're going this route, maybe let's not use an arrow function, because, for the sake of logging output, we don't want it to be anonymous. A regular function declaration would be okay, as in the example in the doc (function Welcome(props) {
). But maybe better (so we don't have to think about hoisting) is to use const
with a named function expression, like const ZulipWebView = function ZulipWebView(props) {
(here's a quick 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.
I also think, just in case we do eventually have to do some hairy debugging around components' lifecycles, or just to be clear about what's what, in general, it might be nice to name our new WebView
something like ZulipWebView
. I think of these as reasons for the naming of ZulipButton
(but actually, looking closer, it doesn't actually wrap a component called Button
, hmm).
Thanks, @agrawal-d! I think #4183 may be about to land (I'll go resolve some conflicts), which will change the logic to get the |
Thanks for the review @chrisbobbe, I've made the changes you suggested. Now I guess I'll wait for #4183 to land. |
@chrisbobbe I've rebased this PR. It was a bit convoluted, so if I missed something from your commits, please let me know. CC @gnprice |
f6a3e5b
to
b922e90
Compare
While the docs for the API response of get-message-history[1] are still unclear, by experimenting it's possible to determine which fields are always present. So, improve the type definition based on these findings. [1] https://zulipchat.com/api/get-message-history
Our usage of 'react-native-webview' is configured intricately to migigate bugs and provide basic scaffolding. So, to make it reusable, refactor it into a common component -ZulipWebView, so that it can be reused later. Sets up Ref Forwarding [1] to ensure that web-view events still work. [1]: https://reactjs.org/docs/forwarding-refs.html
Refactors the default export so that it can be used in different contexts, and now also accepts an `htmlBody` parameter, which should contain the content to be rendered. This file will later be used to render edit message history.
Creates `editHistoryHtml.js` and adds styles to `base.css` to generate markup that can be used to show message edit history, in a webview. The CSS for the diff highlight was borrowed from the webapp, but the CSS for rendering the edit history blocks is new.
Creates a component 'EditHistory' that renders a webview to show the edit history of a message. This webview is non-interactive - the user can select/copy message contents, but can not interact with them - like open links/navigate to narrows etc. Fixes: zulip#4134
b922e90
to
2e45013
Compare
I just rebased and resolved some conflicts. I also made a small change (getting the theme name for |
Fixes: #4134