This repository was archived by the owner on Oct 11, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Render mobile message replies #3088
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
d0011b2
Start implementing mobile message replies
mxstbr 5b7a119
Style the chat input
mxstbr 919027a
Fix quoted message width
mxstbr 60cc64e
Create reusable isShort util for messages
mxstbr 755bf24
Add gradient below long quoted messages
mxstbr eca9377
Be able to write quoted message replies
mxstbr 2ece6c5
Add action sheet with quote action to Messages
mxstbr e5516dc
console.log -> console.error
mxstbr d0bc19b
Merge branch 'alpha' into mobile-message-replies
mxstbr 087b74c
Merge branch 'alpha' into mobile-message-replies
mxstbr 68ecb79
Finish implementing sending quoted messages
mxstbr 9b73b5c
Merge branch 'alpha' into mobile-message-replies
mxstbr d035e3a
Use DraftJS-specific check for only containing emoji
mxstbr 5ce4f86
Move FlowIssue to right place
mxstbr 1bf97ad
Merge branch 'alpha' into mobile-message-replies
mxstbr cb07bf0
Remove duplicate dep
mxstbr 62b69b1
Render emoji-only messages
mxstbr f0f3b87
Merge branch 'alpha' into mobile-message-replies
mxstbr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // flow-typed signature: e49a967b35c065ea303b9bb16e18af7b | ||
| // flow-typed version: <<STUB>>/@expo/react-native-action-sheet_v1.0.2/flow_v0.66.0 | ||
|
|
||
| /** | ||
| * This is an autogenerated libdef stub for: | ||
| * | ||
| * '@expo/react-native-action-sheet' | ||
| * | ||
| * Fill this stub out by replacing all the `any` types. | ||
| * | ||
| * Once filled out, we encourage you to share your work with the | ||
| * community by sending a pull request to: | ||
| * https://github.com/flowtype/flow-typed | ||
| */ | ||
|
|
||
| declare module '@expo/react-native-action-sheet' { | ||
| declare module.exports: any; | ||
| } | ||
|
|
||
| /** | ||
| * We include stubs for each file inside this npm package in case you need to | ||
| * require those files directly. Feel free to delete any files that aren't | ||
| * needed. | ||
| */ | ||
| declare module '@expo/react-native-action-sheet/ActionSheet.android' { | ||
| declare module.exports: any; | ||
| } | ||
|
|
||
| declare module '@expo/react-native-action-sheet/ActionSheet.ios' { | ||
| declare module.exports: any; | ||
| } | ||
|
|
||
| declare module '@expo/react-native-action-sheet/ActionSheetProvider' { | ||
| declare module.exports: any; | ||
| } | ||
|
|
||
| declare module '@expo/react-native-action-sheet/connectActionSheet' { | ||
| declare module.exports: any; | ||
| } | ||
|
|
||
| // Filename aliases | ||
| declare module '@expo/react-native-action-sheet/ActionSheet.android.js' { | ||
| declare module.exports: $Exports<'@expo/react-native-action-sheet/ActionSheet.android'>; | ||
| } | ||
| declare module '@expo/react-native-action-sheet/ActionSheet.ios.js' { | ||
| declare module.exports: $Exports<'@expo/react-native-action-sheet/ActionSheet.ios'>; | ||
| } | ||
| declare module '@expo/react-native-action-sheet/ActionSheetProvider.js' { | ||
| declare module.exports: $Exports<'@expo/react-native-action-sheet/ActionSheetProvider'>; | ||
| } | ||
| declare module '@expo/react-native-action-sheet/connectActionSheet.js' { | ||
| declare module.exports: $Exports<'@expo/react-native-action-sheet/connectActionSheet'>; | ||
| } | ||
| declare module '@expo/react-native-action-sheet/index' { | ||
| declare module.exports: $Exports<'@expo/react-native-action-sheet'>; | ||
| } | ||
| declare module '@expo/react-native-action-sheet/index.js' { | ||
| declare module.exports: $Exports<'@expo/react-native-action-sheet'>; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,5 +3,5 @@ | |
| "lanType": "ip", | ||
| "dev": true, | ||
| "minify": false, | ||
| "urlRandomness": "66-qcw" | ||
| "urlRandomness": "2s-bjc" | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,48 +1,47 @@ | ||
| // @flow | ||
| import React from 'react'; | ||
| import { TextInput, View, Button } from 'react-native'; | ||
| import KeyboardSpacer from 'react-native-keyboard-spacer'; | ||
| import compose from 'recompose/compose'; | ||
| import { connect } from 'react-redux'; | ||
| import { withNavigation } from 'react-navigation'; | ||
| import { replyToMessage } from '../../../src/actions/message'; | ||
| import Input, { type InputProps } from './input'; | ||
| import { QuotedMessage } from '../Message/QuotedMessage'; | ||
| import { getMessageById } from '../../../shared/graphql/queries/message/getMessage'; | ||
| import type { Node } from 'react'; | ||
|
|
||
| type Props = { | ||
| onSubmit: (text: string) => void, | ||
| // Don't let people pass in their own children, this container takes care of that | ||
| ...$Diff<InputProps, { children: Node }>, | ||
| dispatch: Function, | ||
| quotedMessage?: string, | ||
| }; | ||
|
|
||
| type State = { | ||
| value: string, | ||
| }; | ||
|
|
||
| class ChatInput extends React.Component<Props, State> { | ||
| state = { | ||
| value: '', | ||
| }; | ||
| const QuotedMessageById = getMessageById(props => { | ||
| if (props.data.loading || !props.data.message) return null; | ||
| return <QuotedMessage noPadding message={props.data.message} />; | ||
| }); | ||
|
|
||
| onChangeText = (value: string) => { | ||
| this.setState({ value }); | ||
| // Get the quoted message ID for the current thread | ||
| // by combining the navigation state + the redux state | ||
| const mapStateToProps = (state, ownProps): * => { | ||
| const { state: navigationState } = ownProps.navigation; | ||
| const threadId = | ||
| navigationState.routeName === 'Thread' ? navigationState.params.id : null; | ||
| return { | ||
| quotedMessage: threadId ? state.message.quotedMessage[threadId] : null, | ||
| }; | ||
| }; | ||
|
|
||
| submit = () => { | ||
| this.props.onSubmit(this.state.value); | ||
| this.onChangeText(''); | ||
| }; | ||
| const ChatInputContainer = (props: Props) => { | ||
| const { quotedMessage, dispatch, ...inputProps } = props; | ||
|
|
||
| render() { | ||
| return ( | ||
| <View> | ||
| <TextInput | ||
| value={this.state.value} | ||
| onChangeText={this.onChangeText} | ||
| placeholder="Your message here..." | ||
| multiline | ||
| autoFocus={false} | ||
| disableFullscreenUI | ||
| onSubmitEditing={this.submit} | ||
| /> | ||
| <Button onPress={this.submit} title="Send" /> | ||
| {/* NOTE(@mxstbr): Magic number, otherwise the chatinput is way above the keyboard */} | ||
| <KeyboardSpacer topSpacing={-75} /> | ||
| </View> | ||
| ); | ||
| } | ||
| } | ||
| return ( | ||
| <Input {...inputProps}> | ||
| {quotedMessage ? <QuotedMessageById id={quotedMessage} /> : null} | ||
| </Input> | ||
| ); | ||
| }; | ||
|
|
||
| export default ChatInput; | ||
| export default compose(withNavigation, connect(mapStateToProps))( | ||
| ChatInputContainer | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // @flow | ||
| import React from 'react'; | ||
| import { TextInput, View, Button } from 'react-native'; | ||
| import KeyboardSpacer from 'react-native-keyboard-spacer'; | ||
| import { | ||
| ChatInputWrapper, | ||
| ChatInputTextInputWrapper, | ||
| SendButton, | ||
| } from './style'; | ||
|
|
||
| import type { Node } from 'react'; | ||
|
|
||
| export type InputProps = { | ||
| onSubmit: (text: string) => void, | ||
| children?: Node, | ||
| }; | ||
|
|
||
| type State = { | ||
| value: string, | ||
| }; | ||
|
|
||
| class ChatInput extends React.Component<InputProps, State> { | ||
| state = { | ||
| value: '', | ||
| }; | ||
|
|
||
| onChangeText = (value: string) => { | ||
| this.setState({ value }); | ||
| }; | ||
|
|
||
| submit = () => { | ||
| this.props.onSubmit(this.state.value); | ||
| this.onChangeText(''); | ||
| }; | ||
|
|
||
| render() { | ||
| return ( | ||
| <View style={{ width: '100%' }}> | ||
| <ChatInputWrapper> | ||
| <ChatInputTextInputWrapper> | ||
| {this.props.children} | ||
| <TextInput | ||
| value={this.state.value} | ||
| onChangeText={this.onChangeText} | ||
| placeholder="Your message here..." | ||
| multiline | ||
| autoFocus={false} | ||
| disableFullscreenUI | ||
| onSubmitEditing={this.submit} | ||
| /> | ||
| </ChatInputTextInputWrapper> | ||
| <SendButton onPress={this.submit} size={32} /> | ||
| </ChatInputWrapper> | ||
| {/* NOTE(@mxstbr): Magic number, otherwise the chatinput is way above the keyboard */} | ||
| <KeyboardSpacer topSpacing={-75} /> | ||
| </View> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| export default ChatInput; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| // @flow | ||
| import { TouchableOpacity } from 'react-native'; | ||
| import { Svg } from 'expo'; | ||
| import React from 'react'; | ||
| import styled, { withTheme } from 'styled-components/native'; | ||
|
|
||
| const { Path, G } = Svg; | ||
|
|
||
| export const ChatInputWrapper = styled.View` | ||
| flex-direction: row; | ||
| align-items: center; | ||
| width: 100%; | ||
| margin: 0px; | ||
| padding: 8px 4px; | ||
| background-color: ${props => props.theme.bg.default}; | ||
| border-top-width: 1px; | ||
| border-top-color: ${({ theme }) => theme.bg.border}; | ||
| `; | ||
|
|
||
| export const ChatInputTextInputWrapper = styled.View` | ||
| flex: 1; | ||
| flex-direction: column; | ||
| min-height: 40px; | ||
| padding: ${props => (props.hasAttachment ? '16px' : '8px 16px')}; | ||
| border-radius: 24px; | ||
| border-width: 1px; | ||
| border-color: ${props => props.theme.bg.border}; | ||
| background: ${props => props.theme.bg.default}; | ||
| `; | ||
|
|
||
| type SendButtonProps = { | ||
| onPress: Function, | ||
| size?: number, | ||
| theme: Object, | ||
| }; | ||
|
|
||
| export const SendButton = withTheme( | ||
| ({ onPress, size, theme }: SendButtonProps) => { | ||
| const actualSize = size ? size : 64; | ||
| return ( | ||
| <TouchableOpacity onPress={onPress}> | ||
| <Svg width={actualSize} height={actualSize} viewBox={`0 0 32 32`}> | ||
| <G> | ||
| <Path | ||
| fill={theme.text.alt} | ||
| d="M9,8l0,5.287l7.054,1.495c0.628,0.133 0.966,0.665 0.989,1.164c0,0.009 0.001,0.022 0.001,0.034c0,0.004 0,0.008 0,0.012c0,0.005 0,0.009 0,0.013c0,0.012 -0.001,0.025 -0.001,0.034c-0.023,0.498 -0.361,1.031 -0.989,1.164l-7.054,1.495l0,5.627c0.02,0.001 0.049,-0.002 0.09,-0.017c4.386,-1.524 15.41,-7.808 15.41,-8.308c0,-0.5 -11.075,-6.473 -15.41,-7.984c-0.041,-0.014 -0.07,-0.017 -0.09,-0.016Zm17.555,7.992l0,-0.011l0,-0.003c-0.011,-0.698 -0.39,-1.289 -0.925,-1.685c-3.631,-2.688 -11.512,-6.642 -15.882,-8.165c-0.624,-0.218 -1.3,-0.158 -1.843,0.185c-0.554,0.349 -0.905,0.958 -0.905,1.667l0,5.712c0,0.708 0.496,1.32 1.189,1.467l3.931,0.833l-3.931,0.834c-0.693,0.146 -1.189,0.758 -1.189,1.467l0,6.052c0,0.709 0.351,1.317 0.905,1.667c0.543,0.343 1.219,0.403 1.843,0.185c4.371,-1.527 12.29,-5.85 15.881,-8.505c0.536,-0.397 0.915,-0.987 0.925,-1.685l0,-0.003l0.001,-0.012Z" | ||
| /> | ||
| </G> | ||
| </Svg> | ||
| </TouchableOpacity> | ||
| ); | ||
| } | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // @flow | ||
| import React from 'react'; | ||
| import { TouchableOpacity } from 'react-native'; | ||
| import Message from './'; | ||
| import Author from '../Messages/Author'; | ||
| import { QuoteWrapper, QuotedParagraph, QuoteGradient } from './style'; | ||
|
|
||
| import { isShort } from '../../../shared/clients/draft-js/utils/isShort'; | ||
| import type { MessageInfoType } from '../../../shared/graphql/fragments/message/messageInfo'; | ||
|
|
||
| type QuotedMessageProps = { | ||
| message: MessageInfoType, | ||
| noPadding?: boolean, | ||
| }; | ||
|
|
||
| type QuotedMessageState = { | ||
| isShort: boolean, | ||
| isExpanded: boolean, | ||
| }; | ||
|
|
||
| export class QuotedMessage extends React.Component< | ||
| QuotedMessageProps, | ||
| QuotedMessageState | ||
| > { | ||
| constructor(props: QuotedMessageProps) { | ||
| super(props); | ||
|
|
||
| const short = isShort(props.message); | ||
| this.state = { | ||
| isShort: short, | ||
| isExpanded: short, | ||
| }; | ||
| } | ||
|
|
||
| shouldComponentUpdate( | ||
| nextProps: QuotedMessageProps, | ||
| nextState: QuotedMessageState | ||
| ) { | ||
| return nextState.isExpanded !== this.state.isExpanded; | ||
| } | ||
|
|
||
| toggle = () => { | ||
| if (this.state.isShort) return; | ||
| this.setState(prev => ({ isExpanded: !prev.isExpanded })); | ||
| }; | ||
|
|
||
| render() { | ||
| const { message, noPadding = false } = this.props; | ||
| const { isExpanded, isShort } = this.state; | ||
| // TODO(@mxstbr): Use <ConditionalWrap> to only add TouchableOpacity to long messages | ||
| return ( | ||
| <TouchableOpacity onPress={this.toggle}> | ||
| <QuoteWrapper noPadding={noPadding} expanded={isExpanded}> | ||
| <Author me={false} avatar={false} author={message.author} /> | ||
| <QuotedParagraph> | ||
| <Message bubble={false} message={message} me={false} /> | ||
| </QuotedParagraph> | ||
| {!isExpanded && <QuoteGradient />} | ||
| </QuoteWrapper> | ||
| </TouchableOpacity> | ||
| ); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Should this have notes around why this needs to be this specific thing?
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 automatically generated by Expo