Skip to content
This repository has been archived by the owner on Aug 9, 2022. It is now read-only.

Commit

Permalink
Add text-chat sample
Browse files Browse the repository at this point in the history
  • Loading branch information
aikw committed Jul 5, 2018
1 parent e30f41a commit aeef8b6
Show file tree
Hide file tree
Showing 8 changed files with 735 additions and 0 deletions.
5 changes: 5 additions & 0 deletions samples/text-chat/LICENSE
@@ -0,0 +1,5 @@
The MIT License (MIT)
Copyright (c) 2018 Ricoh Company, Ltd.
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.
65 changes: 65 additions & 0 deletions samples/text-chat/README.md
@@ -0,0 +1,65 @@

# Text Chat Sample

Study sample code to learn how to use Messaging API via RICOH Cloud SDK for JavaScript.

This code sample offers a back-end function and a front-end function. The sample uses Koa.

## Back-End

A back-end Web API server implementation which hides Client Credentials from front-end service users. The source code is located in `server.js`.

Note that this code sample aims to show the basic usage of the API and the SDK. If you want to publish this sample to offer your service, you should consider adding more user limitation to this implementation.

## Front-End

A front-end application for XMPP(BOSH). The source code is located in `public` directory.

## Requirements

- yarn

## How to Use

Download this code sample using `git clone`.

```sh
$ git clone https://github.com/ricohapi/ricoh-cloud-sdk-js.git
$ cd ricoh-cloud-sdk-js/samples/text-chat
```

Set your Client Credentials in `server.js`.

```javascript
L17: const CLIENT_ID = "<Set your Client ID here>";
L18: const CLIENT_SECRET = "<Set your Client Secret here>";
```

Run the following commands to start a web server on port 3000.


```sh
$ yarn
$ yarn start
```

## Step by Step guide
1. Open ``http://localhost:3000/signin`` in a web browser(support only Chrome or Firefox).

2. Login with *user_1*'s ricoh account.

3. Click ``CONNECT`` button.

4. Open ``http://localhost:3000/signin`` on another tab in the web browser.

5. Login with *user_2*'s ricoh account.

6. Click ``CONNECT`` button.

7. On ``Contacts`` panel, enter *user_1*'s sub and click ``SUBSCRIBE`` button.

8. Click ``CHAT`` on ``Contacts`` panel.

9. On ``Chat`` panel, enter the message to send to *user_2* and click ``SEND`` button. The message will be sent.

10. Click ``DISCONNECT`` button for disconnect.
25 changes: 25 additions & 0 deletions samples/text-chat/package.json
@@ -0,0 +1,25 @@
{
"name": "tchat",
"version": "0.1.0",
"description": "text chat sample app",
"main": "server.js",
"author": "RICOH",
"license": "MIT",
"private": true,
"scripts": {
"start": "node server.js"
},
"engines": {
"node": ">= 7.6.0"
},
"dependencies": {
"koa": "2.5.0",
"koa-router": "7.4.0",
"koa-better-serve": "2.0.7",
"kcors": "2.2.1",
"koa-convert": "1.2.0",
"jsonwebtoken": "8.1.1",
"axios": "0.18.0",
"query-string": "5.0.1"
}
}
129 changes: 129 additions & 0 deletions samples/text-chat/public/app.js
@@ -0,0 +1,129 @@
/* global Vue, BOSHClient */
/**
* Copyright (c) 2018 Ricoh Company, Ltd. All Rights Reserved.
* See LICENSE for more information
*/

let bosh = null;

const DOMAIN = 'xmpp.api.ricoh';

/* eslint-disable no-unused-vars */
const app = new Vue({
el: '#app',
data: {
state: 'ready',
user: '',
token: '',
sndmsg: '',
newpeer: '',
rcvmsg: '',
peers: [],
target: '',
chat: '',
errmsg: '',
},
mounted: function() {
this.onload();
},
methods: {
getParams: function() {
const params = {};
const query = window.location.search.substr(1);
query.split('&').forEach((item) => {
const tmp = item.split('=');
params[tmp[0]] = decodeURIComponent(tmp[1]);
});
return params;
},
onload: function() {
const params = this.getParams();
this.user = params['user'];
this.token = params['token'];
},
connect: function() {
const self = this;
this.peers = [];

bosh = new BOSHClient();
bosh.onMessage = (from, message) => {
self.target = from;
self.state = 'opened';
self.chat += `${this.nowstr()}: (${from.substr(0,10)}...) ${message}\n`;
};
bosh.onError = (status) => {
self.state = 'error';
self.errmsg = status;
};
bosh.onConnect = () => {
self.state = 'connected';
};
bosh.onPresence = (from, state) => {
if (state === 'subscribe') {
bosh.subscribed(from);
bosh.subscribe(from);
} else if (state === 'unsubscribe') {
bosh.unsubscribe(from);
} else if (state === 'unsubscribed') {
bosh.unsubscribed(from);
bosh.unsubscribe(from);
}
};
bosh.onRoster = (peers) => {
self.peers = peers;
};
bosh.connect(`${this.user}@${DOMAIN}`, this.token);
this.state = 'connecting';
},
open: function(id) {
this.target = id;
this.state = 'opened';
this.chat = 'start chat with ' + id + '...\n';
},
rename: function(jid, name) {
if (!bosh) return;
bosh.rename(jid, name);
},
subscribe: function() {
if (!bosh) return;
bosh.subscribe(`${this.newpeer}@${DOMAIN}`);
},
unsubscribe: function(jid) {
if (!bosh) return;
bosh.unsubscribe(jid);
},
away: function() {
if (!bosh) return;
bosh.presence('away');
},
onChat: function() {
if (!bosh) return;
bosh.presence('chat');
},
send: function() {
if (!bosh) return;
if (this.sndmsg.length === 0) return;
bosh.send(`${this.target}`, this.sndmsg);
this.chat += `${this.nowstr()}: (${this.user.substr(0,10)}...) ${this.sndmsg}\n`;
this.sndmsg = '';
},
nowstr: function() {
const now = new Date();
return now.toLocaleString('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
});
},
disconnect: function() {
bosh.disconnect();
bosh = null;
this.chat = '';
this.state = 'ready';
},
},
});
150 changes: 150 additions & 0 deletions samples/text-chat/public/boshsdk.js
@@ -0,0 +1,150 @@
/* global Strophe, $pres, $iq, $msg */
/**
* Copyright (c) 2018 Ricoh Company, Ltd. All Rights Reserved.
* See LICENSE for more information
*/

const ENDPOINT = 'https://xmpp.api.ricoh/v1/http-bind/';

const STATUS = {};
STATUS[Strophe.Status.CONNECTING] = 'connecting';
STATUS[Strophe.Status.CONNFAIL] = 'connfail';
STATUS[Strophe.Status.AUTHENTICATING] = 'authenticating';
STATUS[Strophe.Status.AUTHFAIL] = 'authfail';
STATUS[Strophe.Status.CONNECTED] = 'connected';
STATUS[Strophe.Status.DISCONNECTED] = 'disconnected';
STATUS[Strophe.Status.DISCONNECTING] = 'disconnecting';
STATUS[Strophe.Status.ATTACHED] = 'attached';
STATUS[Strophe.Status.ERROR] = 'error';
STATUS[Strophe.Status.REDIRECT] = 'redirect';
STATUS[Strophe.Status.CONNTIMEOUT] = 'timeout';

/* eslint-disable no-unused-vars */
class BOSHClient {
constructor() {
this.onMessage = null;
this.onPresence = null;
this.onRoster = null;
this.onError = null;
this.onConnect = null;
this._conn = null;
this._user = '';
this._roster = [];
}

_onConnect(status) {
/* eslint-disable no-console */
console.log(`connect handler: ${STATUS[status]}`);
if (status === Strophe.Status.ERROR
|| status === Strophe.Status.CONNTIMEOUT
|| status === Strophe.Status.AUTHFAIL) {
if (this.onError) this.onError(STATUS[status]);
}
if (status !== Strophe.Status.CONNECTED) return true;
this._conn.addHandler(this._onMessage.bind(this), null, 'message', 'chat');
this._conn.addHandler(this._onPresence.bind(this), null, 'presence');
this._requestRoster();
this._conn.send($pres());
if (this.onConnect) this.onConnect();
return true;
}

_onMessage(stanza) {
const from = Strophe.getBareJidFromJid(stanza.attributes.from.textContent);
if (from === this._user) return true;
if (this.onMessage) this.onMessage(from, stanza.firstChild.textContent);
return true;
}

_getState(jid) {
let state = null;
this._roster.forEach((r) => {
if (r.id === jid) state = r.state;
});
return state;
}

_onRoster(stanza) {
this._roster = (stanza.firstChild.hasChildNodes()) ?
Array.from(stanza.firstChild.childNodes).map((n) => {
const jid = n.attributes.jid.textContent;
const state = this._getState(jid) || 'pending';
const name = (n.attributes.name) ? n.attributes.name.textContent : '';
return { id: jid, state: state, name: name };
}) : [];
if (this.onRoster) this.onRoster(this._roster);
return true;
}

_onPresence(stanza) {
const from = Strophe.getBareJidFromJid(stanza.attributes.from.textContent);
if (from === this._user) return true;

const typeattr = stanza.attributes.type;
let state = (typeattr) ? typeattr.textContent : 'available';

stanza.childNodes.forEach((c) => {
if (c.nodeName === 'show') state = c.textContent;
});
if (this.onPresence) this.onPresence(from, state);
/* eslint-disable no-param-reassign */
this._roster.forEach((r) => {
if (r.id === from) r.state = state;
});
if (this.onRoster) this.onRoster(this._roster);
return true;
}

_setRoster(jid, type, item) {
this._conn.sendIQ($iq({ type: 'set' }).c('query', { xmlns: 'jabber:iq:roster' }).c('item', item));
this._conn.send($pres({ to: jid, type: type }));
}

_requestRoster() {
this._conn.sendIQ($iq({ type: 'get' }).c('query', { xmlns: 'jabber:iq:roster' }), this._onRoster.bind(this));
}

subscribed(jid) {
this._setRoster(jid, 'subscribed', { jid: jid, name: jid.substr(0,10) });
this._requestRoster();
}

unsubscribed(jid) {
this._setRoster(jid, 'unsubscribed', { jid: jid, name: jid.substr(0,10), subscription: 'remove' });
this._requestRoster();
}
subscribe(jid) {
this._setRoster(jid, 'subscribe', { jid: jid, name: jid.substr(0,10) });
this._requestRoster();
}

unsubscribe(jid) {
this._setRoster(jid, 'unsubscribe', { jid: jid, subscription: 'remove' });
this._requestRoster();
}

rename(jid, name) {
this._conn.sendIQ($iq({ type: 'set' }).c('query', { xmlns: 'jabber:iq:roster' }).c('item', {jid: jid, name: name}));
this._requestRoster();
}

presence(type) {
this._conn.send($pres().c('show').t(type));
}

connect(user, pass) {
this._user = user;
this._conn = new Strophe.Connection(ENDPOINT, { mechanisms: [Strophe.SASLPlain] });
this._conn.connect(user, pass, this._onConnect.bind(this),3);
}

disconnect() {
if (!this._conn) return;
this._conn.disconnect('normal');
this._conn = null;
}

send(jid, msg) {
this._conn.send($msg({ to: jid, type: 'chat' }).c('body').t(msg).up());
}
}

0 comments on commit aeef8b6

Please sign in to comment.