-
Notifications
You must be signed in to change notification settings - Fork 56
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
Add chat drafts to conversation state. #111
base: master
Are you sure you want to change the base?
Conversation
This allows drafts to be stored per conversation instead of keeping the same draft for all conversations. However, it doesn't handle drafts in game chats the same way, because those conversations are not stored in the same way.
Hi Logan. Thanks for the PR! This is a good start. There are a few things to mention. First and biggest: the approach you took is to save the draft text into a conversation record after every change to the text input. While this is the simplest, most straight-forward approach, in practice doing it this way can bog down performance. Currently Shin uses a single store for the entire app, and when its state changes, the entire app re-renders from top to bottom. So after each keypress, the entire screen re-renders, not just the message bar. To fix this, you can use component-local state to store the latest value of the input, and do a rate-limited update to the global conversation state (maybe 'SAVE_CHAT_DRAFT' as a message type). If you wanted to get fancy, you could even create a reusable text input with that feature built-in (value changes as you type, and then runs a debounced callback every X milliseconds). Sidebar: in the future, we may have components subscribe to only the subset of state they need from the store, so app state changes don't trigger unnecessary render checks everywhere (we do use PureComponent which does not re-render unless props actually change). For most cases (other than inputs), a single top-down data flow is simpler. Other issues:
|
src/ui/chat/ChatMessageBar.js
Outdated
@@ -6,6 +6,7 @@ import type {Conversation} from '../../model'; | |||
type Props = { | |||
conversation: ?Conversation, | |||
onSubmit: string => any; | |||
onDraft: string => any; |
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.
Given that this is not used for games, I'd make it optional: onDraft?: string => any
src/ui/chat/ChatMessageBar.js
Outdated
@@ -22,6 +23,13 @@ export default class ChatMessageBar extends Component { | |||
} | |||
} | |||
|
|||
_onChange(event: SyntheticEvent) { |
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 convention we're using in Shin is to use class property arrow functions for event handlers:
_onChange = (event: SyntheticEvent) => {
This convention sets this
appropriately, removing the need for bind
later on.
src/ui/chat/ChatMessageBar.js
Outdated
@@ -39,6 +52,8 @@ export default class ChatMessageBar extends Component { | |||
className='ChatMessageBar-input' | |||
type='text' | |||
placeholder={placeholder} | |||
value={draft === '' ? null : draft} | |||
onChange={this._onChange.bind(this)} |
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.
see prior comment about arrow functions for event handlers
src/ui/game/GameScreen.js
Outdated
chatsDisabled: !game.tree | ||
}} | ||
onSubmit={this._onChat} /> | ||
onSubmit={this._onChat} | ||
onDraft={()=>{}} |
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.
see previous comment about making onDraft
optional, in which case this can be left out
Thanks for the thorough feedback! I'll start taking care of those things. At a first glance, it looks like storing the input text value in component local state and making |
Regarding debouncing -- do you have a preference for adding a dependency (e.g. lodash.debounce) vs sticking a debounce implementation in the utils? |
@loganmhb A debounce util function is preferred |
I think the latest commit finishes implementing your suggestions. Let me know if there's anything else you notice or would like changed. |
thanks, will review when I get a chance |
Almost there! A couple issues:
Code wise it's looking nice. 👍 |
@loganmhb Could you rebase this PR? |
First of all, thanks for a great-looking app! I'm really looking forward to seeing this grow. I did some work on #92 because it seemed like a nice-sized task to start learning my way around the codebase.
This pull request allows drafts to be stored per conversation instead of keeping the same draft for all conversations, resolving #92. However, it doesn't (yet?) handle drafts in game chats (it ignores them), because those conversations are not stored in the same way as far as I can tell. I'd be happy to work on improving that as well, but I figured I'd go ahead and put this up for feedback first.
Disclaimer: I'm a backend developer by day and I don't have much experience with React/Flow, so if there are rookie mistakes, obvious problems, or you want to take a different approach that feedback is more than welcome. I also haven't figured out how to test against an API server that's not KGS itself, so I haven't tested submission of chat messages.