Skip to content

Commit

Permalink
Merge pull request #1356 from maapteh/chore/chat-example-update
Browse files Browse the repository at this point in the history
Chore: update Chat example
  • Loading branch information
vektah authored Nov 8, 2020
2 parents de8af66 + 1e8c34e commit 88cffee
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 69 deletions.
7 changes: 3 additions & 4 deletions example/chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@apollo/client": "^3.2.3",
"@apollo/react-hooks": "^4.0.0",
"apollo-cache-inmemory": "^1.3.11",
"apollo-client": "^2.4.7",
"apollo-link": "^1.2.4",
"apollo-link-http": "^1.5.7",
"apollo-link-ws": "^1.0.10",
"apollo-utilities": "^1.0.26",
"graphql": "^14.0.2",
"graphql-tag": "^2.10.0",
"react": "^16.6.3",
"react-apollo": "^2.3.1",
"react-dom": "^16.6.3",
"react-scripts": "^2.1.1",
"styled-components": "^5.2.0",
"subscriptions-transport-ws": "^0.9.5"
},
"scripts": {
Expand Down
47 changes: 26 additions & 21 deletions example/chat/src/App.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import React, { Component } from 'react';
import Room from './Room';
import React, { useState } from 'react';
import styled from 'styled-components';
import { Room } from './Room';

class App extends Component {
constructor(props) {
super(props);
const Input = styled.div`
padding: 4px;
margin: 0 0 4px;
this.state = {
name: 'tester',
channel: '#gophers',
}
input {
border: 1px solid #ccc;
padding: 2px;
font-size: 14px;
}
render() {
return (<div>
name: <br/>
<input value={this.state.name} onChange={(e) => this.setState({name: e.target.value })} /> <br />
`;

channel: <br />
<input value={this.state.channel} onChange={(e) => this.setState({channel: e.target.value })}/> <br/>
export const App = () => {
const [name, setName] = useState('tester');
const [channel, setChannel] = useState('#gophers');

<Room channel={this.state.channel} name={this.state.name} />
</div>
);
}
}
return (
<>
<Input>
name: <input value={name} onChange={(e) => setName(e.target.value)} />
</Input>
<Input>
channel: <input value={channel} onChange={(e) => setChannel(e.target.value)} />
</Input>

<Room channel={channel} name={name} />
</>
);

export default App;
};
115 changes: 76 additions & 39 deletions example/chat/src/Room.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,103 @@
import React , {Component} from 'react';
import { graphql, compose } from 'react-apollo';
import React, { useState, useEffect, useRef } from 'react';
import gql from 'graphql-tag';
import { useQuery, useMutation } from '@apollo/react-hooks';
import { Chat, ChatContainer, Message, MessageReceived } from './components/room';

class Room extends Component {
constructor(props) {
super(props)
export const Room = ({ channel, name }) => {
const messagesEndRef = useRef(null)
const [ text, setText ] = useState('');

this.state = {text: ''}
}
const [ addMessage ] = useMutation(MUTATION, {
onCompleted: () => {
setText('');
}
});

const { loading, error, data, subscribeToMore } = useQuery(QUERY, {
variables: {
channel
},
});

componentWillMount() {
this.props.data.subscribeToMore({
document: Subscription,
// subscribe to more messages
useEffect(() => {
const subscription = subscribeToMore({
document: SUBSCRIPTION,
variables: {
channel: this.props.channel,
channel,
},
updateQuery: (prev, {subscriptionData}) => {
updateQuery: (prev, { subscriptionData }) => {

if (!subscriptionData.data) {
return prev;
}
const newMessage = subscriptionData.data.messageAdded;

if (prev.room.messages.find((msg) => msg.id === newMessage.id)) {
return prev
}

return Object.assign({}, prev, {
room: Object.assign({}, prev.room, {
messages: [...prev.room.messages, newMessage],
})
});
}
},
});
}

render() {
const data = this.props.data;

if (data.loading) {
return <div>loading</div>
}
return () => subscription();

return <div>
<div>
}, [subscribeToMore, channel]);

// auto scroll down
useEffect(() => {
messagesEndRef && messagesEndRef.current && messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
}, [messagesEndRef, data]);

if (loading) {
return <div>loading</div>
}

if (error) {
return <div>error</div>
}

return (<>
<Chat>
<ChatContainer>
{data.room.messages.map((msg) =>
<div key={msg.id}>{msg.createdBy}: {msg.text}</div>
msg.createdBy === name ? <Message key={msg.id}>
{msg.text}
</Message> : <MessageReceived key={msg.id}>
<span>{msg.createdBy}</span>
{msg.text}
</MessageReceived>
)}
</div>
<input value={this.state.text} onChange={(e) => this.setState({text: e.target.value})}/>
<button onClick={() => this.props.mutate({
variables: {
text: this.state.text,
channel: this.props.channel,
name: this.props.name,
}
})} >send</button>
</div>;
}
</ChatContainer>
<div ref={messagesEndRef} />
</Chat>

<input value={text} onChange={(e) => setText(e.target.value)} />

<p>
<button
onClick={() => addMessage({
variables: {
text: text,
channel: channel,
name: name,
}
})
} >
send
</button>
</p>
</>);

}

const Subscription = gql`
const SUBSCRIPTION = gql`
subscription MoreMessages($channel: String!) {
messageAdded(roomName:$channel) {
id
Expand All @@ -67,19 +107,16 @@ const Subscription = gql`
}
`;

const Query = gql`
const QUERY = gql`
query Room($channel: String!) {
room(name: $channel) {
messages { id text createdBy }
}
}
`;

const Mutation = gql`
const MUTATION = gql`
mutation sendMessage($text: String!, $channel: String!, $name: String!) {
post(text:$text, roomName:$channel, username:$name) { id }
}
`;


export default compose(graphql(Mutation), graphql(Query))(Room);
83 changes: 83 additions & 0 deletions example/chat/src/components/room.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import styled from 'styled-components';

export const Chat = styled.div`
padding: 4px;
margin: 0 0 12px;
max-width: 400px;
max-height: 400px;
border: 1px solid #ccc;
background-color: #babdc6;
overflow-x: hidden;
overflow-y: scroll;
position: relative;
font: 14px verdana;
`;

export const ChatContainer = styled.div`
&:after {
content: "";
display: table;
clear: both;
}
`;

const ChatLine = styled.div`
color: #000;
clear: both;
line-height: 120%;
padding: 8px;
position: relative;
margin: 8px 0;
max-width: 85%;
word-wrap: break-word;
z-index: 1;
&:after {
position: absolute;
content: "";
width: 0;
height: 0;
border-style: solid;
}
span {
display: inline-block;
float: right;
padding: 0 0 0 7px;
position: relative;
bottom: -4px;
}
}`;

export const MessageReceived = styled(ChatLine)`
background: #fff;
border-radius: 0px 5px 5px 5px;
float: left;
&:after {
border-width: 0px 10px 10px 0;
border-color: transparent #fff transparent transparent;
top: 0;
left: -4px;
}
span {
display: block;
color: #bbb;
font-size: 10px;
}
`;

export const Message = styled(ChatLine)`
background: #e1ffc7;
border-radius: 5px 0px 5px 5px;
float: right;
&:after {
border-width: 0px 0 10px 10px;
border-color: transparent transparent transparent #e1ffc7;
top: 0;
right: -4px;
}
`;
12 changes: 7 additions & 5 deletions example/chat/src/index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import ApolloClient from 'apollo-client';
import App from './App';
import {
ApolloClient,
ApolloProvider,
HttpLink,
split,
} from '@apollo/client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { split } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { App } from './App';

const wsLink = new WebSocketLink({
uri: `ws://localhost:8085/query`,
Expand Down

0 comments on commit 88cffee

Please sign in to comment.