First version

This commit is contained in:
Vladislav Khorev 2018-07-10 16:24:50 +05:00
commit cc019b423f
30 changed files with 22262 additions and 0 deletions

96
.gitignore vendored Executable file
View File

@ -0,0 +1,96 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless

2444
README.md Executable file

File diff suppressed because it is too large Load Diff

11585
package-lock.json generated Executable file

File diff suppressed because it is too large Load Diff

19
package.json Executable file
View File

@ -0,0 +1,19 @@
{
"name": "rnn-chat",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"chat-template": "0.0.26",
"react": "^16.4.1",
"react-chat-bubble": "^0.8.7",
"react-dom": "^16.4.1",
"react-scripts": "1.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}

BIN
public/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

40
public/index.html Executable file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

15
public/manifest.json Executable file
View File

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

38
src/App.css Executable file
View File

@ -0,0 +1,38 @@
.App {
text-align: center;
height: 100%;
}
html {
height: 100%;
}
#root {
height: 100%;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-title {
font-size: 1.5em;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

99
src/App.js Executable file
View File

@ -0,0 +1,99 @@
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import ChatBubble from './react-chat-bubble/src/components/ChatBubble';
import axios from 'axios';
class App extends Component {
state = {
messages: [
{
type: 1,
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Default_profile_picture_%28male%29_on_Facebook.jpg/240px-Default_profile_picture_%28male%29_on_Facebook.jpg",
text: "Hello! Type something!"
}
],
disabled : false
};
handleNewMessage = async (text) =>
{
console.log("!!! handleNewMessage");
await this.setState({
messages: this.state.messages.concat([{
text: text,
type: 0,
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Default_profile_picture_%28male%29_on_Facebook.jpg/240px-Default_profile_picture_%28male%29_on_Facebook.jpg"
}])
});
try {
await this.setState({
messages: this.state.messages.concat([{
text : "Wait, I am processing...",
type: 1,
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Default_profile_picture_%28male%29_on_Facebook.jpg/240px-Default_profile_picture_%28male%29_on_Facebook.jpg"
}]),
disabled: true
});
let response = await axios.get('http://localhost:3001', {
params: {
phrase: text
},
responseType: 'text'
});
console.log("!!! RESPONSE");
console.log(response);
console.log(response.data);
await this.setState({
messages: this.state.messages.concat([{
text : response.data,
type: 1,
image: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Default_profile_picture_%28male%29_on_Facebook.jpg/240px-Default_profile_picture_%28male%29_on_Facebook.jpg"
}]),
disabled: false
});
} catch (error) {
console.error(error)
}
}
componentWillMount(){
document.body.style.height = "100%";
}
componentWillUnmount(){
//document.body.style.backgroundColor = null;
}
render() {
return <ChatBubble messages={this.state.messages}
onNewMessage={this.handleNewMessage} disabled={this.state.disabled}/>
}
}
export default App;

9
src/App.test.js Executable file
View File

@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

5
src/index.css Executable file
View File

@ -0,0 +1,5 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}

8
src/index.js Executable file
View File

@ -0,0 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();

7
src/logo.svg Executable file
View File

@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

17
src/react-chat-bubble/.babelrc Executable file
View File

@ -0,0 +1,17 @@
{
"presets": [
[
"env",
{
"targets": {
"browsers": [
"last 2 versions",
"safari >= 7"
]
}
}
],
"react"
],
"plugins": ["transform-class-properties"]
}

View File

@ -0,0 +1 @@
/node_modules

67
src/react-chat-bubble/README.md Executable file
View File

@ -0,0 +1,67 @@
A Chat bubble component for building Chat App in ReactJS.
## Demo
![Alt Chatbubble](https://raw.githubusercontent.com/sabinbajracharya/react-chat-bubble/master/screenshots/chatbubble.png?raw=true "Chatbubble")
## Installing the react-chat-bubble
```
npm install --save react-chat-bubble
```
## Using the Component
If you are using ES6 modules, if not, you are encouraged to use [`import` and `export`](http://exploringjs.com/es6/ch_modules.html) instead.
Example:
```js
import ChatBubble from 'react-chat-bubble';
class App extends Component {
render() {
<ChatBubble messages = {this.state.messages} />
}
}
export default App;
```
The message object passed as prop should be of following format:
```json
this.state.messages =
[{
"type" : 0,
"image": "cat.jpg",
"text": "Hello! Good Morning!"
}, {
"type": 1,
"image": "dog.jpg",
"text": "Hello! Good Afternoon!"
}];
```
```
type = 0 for sender AND 1 for receiver
image = url of contact image
text = message from the user
```
## To-do
```
> More style customization
> Delivered and seen status
```
##License
The MIT License (MIT)
Copyright (c) 2017 Sabin Bir Bajracharya.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

25
src/react-chat-bubble/build/ChatBubble.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"ChatBubble.js","sources":["webpack:///ChatBubble.js"],"mappings":"AAAA;;;;;AA0SA;AAm6IA;;;;;;;;;;;;;;AAkhBA;AAipIA;AAgjJA;AAy/HA","sourceRoot":""}

View File

@ -0,0 +1,20 @@
<html>
<head>
<meta charset="utf-8">
<style>
html, body, #root {
height: 100%;
}
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="ChatBubble.js" charset="utf-8"></script>
</body>
</html>

View File

@ -0,0 +1,100 @@
.bubble-container{
margin-top: 8px;
margin-bottom: 8px;
display:flex;
font-family: sans-serif;
font-size: 14px;
align-items: center;
}
.bubble-direction-reverse {
flex-direction: row-reverse;
}
.bubble{
background-color: #F2F2F2;
border-radius: 5px;
box-shadow: 0 0 6px #B2B2B2;
display: block;
padding: 10px 18px;
position: relative;
vertical-align: top;
color: white;
word-wrap: break-word;
}
.bubble::before {
background-color: #F2F2F2;
content: "\00a0";
display: block;
height: 16px;
position: absolute;
top: 11px;
transform: rotate( 29deg ) skew( -35deg );
-moz-transform: rotate( 29deg ) skew( -35deg );
-ms-transform: rotate( 29deg ) skew( -35deg );
-o-transform: rotate( 29deg ) skew( -35deg );
-webkit-transform: rotate( 29deg ) skew( -35deg );
width: 20px;
}
.me {
background-color: #8f5db7;
margin-left: 18px;
margin-right:60px;
}
.me::before {
box-shadow: -2px 2px 2px 0 rgba( 178, 178, 178, .4 );
left: -9px;
background-color: #8f5db7;
}
.you {
background-color: #087FFE;
margin-left: 60px;
margin-right:18px;
}
.you::before {
box-shadow: 2px -2px 2px 0 rgba( 178, 178, 178, .4 );
right: -9px;
background-color: #087FFE;
}
.img-circle {
border-radius: 42%;
height:42px;
width:42px;
}
.chats {
position: relative;
height: 100%;
}
.chat-list {
height: calc(100% - 3rem);
overflow-y: scroll;
padding: 1rem;
padding-bottom: 0.5rem;
}
.new-message {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #087FFE;
height: 3rem;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
.new-message-input {
width: 99%;
height: 80%;
font-size: 1.5rem;
}

134
src/react-chat-bubble/lib/ChatBubble.js vendored Executable file
View File

@ -0,0 +1,134 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
require('./ChatBubble.css');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var ChatBubble = function (_Component) {
_inherits(ChatBubble, _Component);
function ChatBubble() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, ChatBubble);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ChatBubble.__proto__ || Object.getPrototypeOf(ChatBubble)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
newMessage: ''
}, _this.handleSubmit = function (e) {
e.preventDefault();
var _this2 = _this,
onNewMessage = _this2.props.onNewMessage,
newMessage = _this2.state.newMessage;
if (onNewMessage && newMessage) {
onNewMessage(newMessage);
}
_this.setState({
newMessage: ''
});
}, _this.handleInputChange = function (e) {
return _this.setState({
newMessage: e.target.value
});
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(ChatBubble, [{
key: 'getConversations',
value: function getConversations(messages) {
if (messages == undefined) {
return;
}
var listItems = messages.map(function (message, index) {
var bubbleClass = 'me';
var bubbleDirection = '';
if (message.type === 0) {
bubbleClass = 'you';
bubbleDirection = "bubble-direction-reverse";
}
return _react2.default.createElement(
'div',
{ className: 'bubble-container ' + bubbleDirection, key: index },
_react2.default.createElement('img', { className: 'img-circle', src: message.image }),
_react2.default.createElement(
'div',
{ className: 'bubble ' + bubbleClass },
message.text
)
);
});
return listItems;
}
}, {
key: 'render',
value: function render() {
var messages = this.props.messages,
newMessage = this.state.newMessage;
var chatList = this.getConversations(messages);
return _react2.default.createElement(
'div',
{ className: 'chats' },
_react2.default.createElement(
'div',
{ className: 'chat-list' },
chatList
),
_react2.default.createElement(
'form',
{
className: 'new-message',
onSubmit: this.handleSubmit
},
_react2.default.createElement('input', {
value: newMessage,
placeholder: 'Write a new message',
onChange: this.handleInputChange,
className: 'new-message-input'
})
)
);
}
}]);
return ChatBubble;
}(_react.Component);
ChatBubble.propTypes = {
messages: _propTypes2.default.array.isRequired,
onNewMessage: _propTypes2.default.func.isRequired
};
exports.default = ChatBubble;

4187
src/react-chat-bubble/package-lock.json generated Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
{
"_from": "react-chat-bubble",
"_id": "react-chat-bubble@0.8.7",
"_inBundle": false,
"_integrity": "sha1-CbXbVNHJi0Zdl4fJQPDDwVp5IxE=",
"_location": "/react-chat-bubble",
"_phantomChildren": {
"create-react-class": "15.6.3",
"fbjs": "0.8.17",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
"prop-types": "15.6.2"
},
"_requested": {
"type": "tag",
"registry": true,
"raw": "react-chat-bubble",
"name": "react-chat-bubble",
"escapedName": "react-chat-bubble",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/react-chat-bubble/-/react-chat-bubble-0.8.7.tgz",
"_shasum": "09b5db54d1c98b465d9787c940f0c3c15a792311",
"_spec": "react-chat-bubble",
"_where": "D:\\Work\\nodejsprojects\\rnn-chat",
"author": {
"name": "Sabin Bajracharya"
},
"bugs": {
"url": "https://github.com/sabinbajracharya/react-chat-bubble/issues"
},
"bundleDependencies": false,
"dependencies": {
"prop-types": "^15.5.10",
"react": "^15.4.2",
"react-dom": "^15.4.2"
},
"deprecated": false,
"description": "A chat bubble component for ReactJS",
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-env": "^1.6.0",
"babel-preset-react": "^6.22.0",
"css-loader": "^0.26.1",
"style-loader": "^0.13.1",
"webpack": "^1.14.0"
},
"homepage": "https://github.com/sabinbajracharya/react-chat-bubble#readme",
"keywords": [
"chat",
"bubble",
"reactjs",
"component",
"conversation",
"ui"
],
"license": "ISC",
"main": "lib/ChatBubble.js",
"name": "react-chat-bubble",
"repository": {
"type": "git",
"url": "git+https://github.com/sabinbajracharya/react-chat-bubble.git"
},
"scripts": {
"build": "webpack --progress --colors --p",
"start": "webpack --progress --colors --watch -d",
"test": "echo \"Error: no test specified\" && exit 1",
"transpile": "babel ./src/components/ChatBubble.js --out-file lib/ChatBubble.js"
},
"version": "0.8.7"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1,100 @@
.bubble-container{
margin-top: 8px;
margin-bottom: 8px;
display:flex;
font-family: sans-serif;
font-size: 14px;
align-items: center;
}
.bubble-direction-reverse {
flex-direction: row-reverse;
}
.bubble{
background-color: #F2F2F2;
border-radius: 5px;
box-shadow: 0 0 6px #B2B2B2;
display: block;
padding: 10px 18px;
position: relative;
vertical-align: top;
color: white;
word-wrap: break-word;
}
.bubble::before {
background-color: #F2F2F2;
content: "\00a0";
display: block;
height: 16px;
position: absolute;
top: 11px;
transform: rotate( 29deg ) skew( -35deg );
-moz-transform: rotate( 29deg ) skew( -35deg );
-ms-transform: rotate( 29deg ) skew( -35deg );
-o-transform: rotate( 29deg ) skew( -35deg );
-webkit-transform: rotate( 29deg ) skew( -35deg );
width: 20px;
}
.me {
background-color: #8f5db7;
margin-left: 18px;
margin-right:60px;
}
.me::before {
box-shadow: -2px 2px 2px 0 rgba( 178, 178, 178, .4 );
left: -9px;
background-color: #8f5db7;
}
.you {
background-color: #087FFE;
margin-left: 60px;
margin-right:18px;
}
.you::before {
box-shadow: 2px -2px 2px 0 rgba( 178, 178, 178, .4 );
right: -9px;
background-color: #087FFE;
}
.img-circle {
border-radius: 42%;
height:42px;
width:42px;
}
.chats {
position: relative;
height: 100%;
}
.chat-list {
height: calc(100% - 3rem);
overflow-y: scroll;
padding: 1rem;
padding-bottom: 0.5rem;
}
.new-message {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: #087FFE;
height: 3rem;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
.new-message-input {
width: 99%;
height: 80%;
font-size: 1.5rem;
}

View File

@ -0,0 +1,83 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types'
import './ChatBubble.css';
class ChatBubble extends Component {
state = {
newMessage: ''
}
getConversations(messages){
if(messages == undefined){
return;
}
const listItems = messages.map((message, index) => {
let bubbleClass = 'me';
let bubbleDirection = '';
if(message.type === 0){
bubbleClass = 'you';
bubbleDirection = "bubble-direction-reverse";
}
return (
<div className={`bubble-container ${bubbleDirection}`} key={index}>
<img className={`img-circle`} src={message.image} />
<div className={`bubble ${bubbleClass}`}>{message.text}</div>
</div>
);
});
return listItems;
}
handleSubmit = e => {
e.preventDefault()
const {props: {onNewMessage}, state: {newMessage}} = this
if(onNewMessage && newMessage) {
onNewMessage(newMessage)
}
this.setState({
newMessage: '',
})
}
handleInputChange = e => this.setState({
newMessage: e.target.value,
})
render() {
const {props: {messages, disabled}, state: {newMessage}} = this;
const chatList = this.getConversations(messages);
return (
<div className="chats">
<div className="chat-list">
{chatList}
</div>
<form
className="new-message"
onSubmit={this.handleSubmit}
>
<input
value={newMessage}
placeholder="Write a new message"
onChange={this.handleInputChange}
className="new-message-input"
disabled={disabled}
/>
</form>
</div>
);
}
}
ChatBubble.propTypes = {
messages: PropTypes.array.isRequired,
onNewMessage: PropTypes.func.isRequired,
};
export default ChatBubble;

46
src/react-chat-bubble/src/index.js vendored Executable file
View File

@ -0,0 +1,46 @@
import React, { Component } from "react";
import ReactDOM from "react-dom";
import ChatBubble from "./components/ChatBubble";
const image = 'http://www.bradfordwhite.com/sites/default/files/images/corporate_imgs/iStock_000012107870XSmall.jpg';
class App extends Component {
state = {
messages: [
{
type: 0,
image,
text: "Hello! Good Morning!"
},
{
type: 1,
image,
text: "Hello! Good Afternoon!"
}
],
};
handleNewMessage = text =>
this.setState({
messages: this.state.messages.concat([{
text,
type: 0,
image,
}])
});
render() {
return (
<ChatBubble
messages={this.state.messages}
onNewMessage={this.handleNewMessage}
/>
);
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
);

View File

@ -0,0 +1,30 @@
const webpack = require('webpack');
module.exports = {
//devtool:'source-map',
devtool: 'cheap-module-source-map',
entry: './src/index.js',
output: {
path: __dirname + "/build",
filename: "ChatBubble.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.css$/,
loaders: [ 'style-loader', 'css-loader' ]
}
]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
})
]
};

2889
src/react-chat-bubble/yarn.lock Executable file

File diff suppressed because it is too large Load Diff

117
src/registerServiceWorker.js Executable file
View File

@ -0,0 +1,117 @@
// In production, we register a service worker to serve assets from local cache.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://goo.gl/SC7cgQ'
);
});
} else {
// Is not local host. Just register service worker
registerValidSW(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}