add account selection, settings, login modal
Change-Id: Ica6d38270c783de070bf1d5bb30603173dbeb0df
diff --git a/JamiDaemon.js b/JamiDaemon.js
new file mode 100755
index 0000000..e85ea9b
--- /dev/null
+++ b/JamiDaemon.js
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2017-2021 Savoir-faire Linux Inc.
+ *
+ * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ * Author: Asad Salman <me@asad.co>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+const Account = require('./model/Account')
+
+"use strict";
+class JamiDaemon {
+ constructor() {
+ this.accounts = []
+ this.dring = require("./dring.node")
+ this.dring.init({
+ "AccountsChanged": () => {
+ console.log("AccountsChanged")
+ const newAccounts = []
+ this.stringVectToArr(this.dring.getAccountList()).forEach(accountId => {
+ for (const account in this.accounts) {
+ if (account.id === accountId) {
+ newAccounts.push(account)
+ return
+ }
+ }
+ newAccounts.push(new Account(accountId,
+ this.mapToJs(this.dring.getAccountDetails(accountId))
+ //this.mapToJs(this.dring.getVolatileDetails(accountId)),
+ ))
+ })
+ this.accounts = newAccounts
+ },
+ "AccountDetailsChanged": (accountId, details) => {
+ console.log(`AccountDetailsChanged ${accountId}`)
+ const account = this.getAccount(accountId)
+ if (!account) {
+ console.log(`Unknown account ${accountId}`)
+ return
+ }
+ account.details = details
+ },
+ "VolatileDetailsChanged": (accountId, details) => {
+ console.log(`VolatileDetailsChanged ${accountId}`)
+ const account = this.getAccount(accountId)
+ if (!account) {
+ console.log(`Unknown account ${accountId}`)
+ return
+ }
+ account.volatileDetails = details
+ },
+ "IncomingAccountMessage": (accountId, from, message) => {
+ console.log(`Received message: ${accountId} ${from} ${message["text/plain"]}`)
+/*
+ if (parser.validate(message["text/plain"]) === true) {
+ console.log(message["text/plain"]);
+ } else {
+
+ user = connectedUsers[accountId];
+ console.log(user.socketId)
+ io.to(user.socketId).emit('receivedMessage', message["text/plain"]);
+ //io.emit('receivedMessage', message["text/plain"]);
+ }*/
+ },
+ "RegistrationStateChanged": (accountId, state, /*int*/ code, detail) => {
+ const account = this.getAccount(accountId)
+ if (!account) {
+ console.log(`Unknown account ${accountId}`)
+ return
+ }
+ account.registrationState = state
+ console.log("RegistrationStateChanged: " + accountId + " " + state + " " + code + " " + detail)
+ if (state === "REGISTERED") {
+ /*if (tempAccounts[accountId]) {
+
+ const ctx = tempAccounts[accountId]
+ ctx.newUser.accountId = accountId
+ ctx.newUser.jamiId = jami.dring.getAccountDetails(accountId).get("Account.username")
+ //connectedUsers[accountId] = ctx.newUser
+ ctx.done(null, ctx.newUser)
+ delete tempAccounts[accountId]
+ }*/
+ } else if (state === "ERROR_AUTH") {
+ //done(null, false)
+ //remove account
+ }
+ },
+ "RegisteredNameFound": (accountId, state, address, name) => {
+ console.log("RegistrationStateChanged: " + accountId + " " + state + " " + address + " " + name)
+ }
+ })
+ this.stringVectToArr(this.dring.getAccountList()).forEach(accountId => {
+ this.accounts.push(new Account(accountId,
+ this.mapToJs(this.dring.getAccountDetails(accountId)),
+ this.mapToJs(this.dring.getVolatileAccountDetails(accountId))
+ ))
+ })
+ }
+
+ addAccount(account) {
+ const params = accountDetailsToNative(account)
+ params.set("Account.type", "RING")
+ return this.dring.addAccount(params)
+ }
+ getAccount(accountId) {
+ for (let i = 0; i < this.accounts.length; i++) {
+ const account = this.accounts[i]
+ if (account.getId() === accountId)
+ return account
+ }
+ return undefined
+ }
+ getAccountList() {
+ return this.accounts
+ }
+ /*getAccountDetails(accountId) {
+ return this.mapToJs(this.dring.getAccountDetails(accountId));
+ }*/
+ setAccountDetails(accountId, details) {
+ this.dring.setAccountDetails(accountId, mapToNative(details));
+ }
+ getAudioOutputDeviceList() {
+ return this.stringVectToArr(this.dring.getAudioOutputDeviceList());
+ }
+ getVolume(deviceName) {
+ return this.dring.getVolume(deviceName);
+ }
+ setVolume(deviceName, volume) {
+ return this.dring.setVolume(deviceName, volume);
+ }
+
+ stop() {
+ this.dring.fini();
+ }
+
+// private
+
+ boolToStr(bool) {
+ return bool ? "TRUE" : "FALSE";
+ }
+
+ accountDetailsToNative(account) {
+ const params = new this.dring.StringMap();
+ if (account.managerUri)
+ params.set("Account.managerUri", account.managerUri);
+ if (account.managerUsername)
+ params.set("Account.managerUsername", account.managerUsername);
+ if (account.archivePassword) {
+ params.set("Account.archivePassword", account.archivePassword);
+ } else {
+ console.log("archivePassword required");
+ return;
+ }
+ if (account.alias)
+ params.set("Account.alias", account.alias);
+ if (account.displayName)
+ params.set("Account.displayName", account.displayName);
+ if (account.enable)
+ params.set("Account.enable", this.boolToStr(account.enable));
+ if (account.autoAnswer)
+ params.set("Account.autoAnswer", this.boolToStr(account.autoAnswer));
+ if (account.ringtonePath)
+ params.set("Account.ringtonePath", account.ringtonePath);
+ if (account.ringtoneEnabled)
+ params.set("Account.ringtoneEnabled", this.boolToStr(account.ringtoneEnabled));
+ if (account.videoEnabled)
+ params.set("Account.videoEnabled", this.boolToStr(account.videoEnabled));
+ if (account.useragent) {
+ params.set("Account.useragent", account.useragent);
+ params.set("Account.hasCustomUserAgent", "TRUE");
+ } else {
+ params.set("Account.hasCustomUserAgent", "FALSE");
+ }
+ if (account.audioPortMin)
+ params.set("Account.audioPortMin", account.audioPortMin);
+ if (account.audioPortMax)
+ params.set("Account.audioPortMax", account.audioPortMax);
+ if (account.videoPortMin)
+ params.set("Account.videoPortMin", account.videoPortMin);
+ if (account.videoPortMax)
+ params.set("Account.videoPortMax", account.videoPortMax);
+ if (account.localInterface)
+ params.set("Account.localInterface", account.localInterface);
+ if (account.publishedSameAsLocal)
+ params.set("Account.publishedSameAsLocal", this.boolToStr(account.publishedSameAsLocal));
+ if (account.localPort)
+ params.set("Account.localPort", account.localPort);
+ if (account.publishedPort)
+ params.set("Account.publishedPort", account.publishedPort);
+ if (account.publishedAddress)
+ params.set("Account.publishedAddress", account.publishedAddress);
+ if (account.upnpEnabled)
+ params.set("Account.upnpEnabled", this.boolToStr(account.upnpEnabled));
+ return params;
+ }
+ stringVectToArr(stringvect) {
+ const outputArr = [];
+ for (let i = 0; i < stringvect.size(); i++)
+ outputArr.push(stringvect.get(i));
+ return outputArr;
+ }
+ mapToJs(m) {
+ const outputObj = {};
+ this.stringVectToArr(m.keys())
+ .forEach(k => outputObj[k] = m.get(k));
+ return outputObj;
+ }
+ mapToNative(map){
+ const ret = new this.dring.StringMap();
+ map.forEach((value, key) => ret.set(key, value));
+ return ret;
+ }
+
+}
+
+module.exports = JamiDaemon;
diff --git a/RingDaemon.js b/RingDaemon.js
deleted file mode 100755
index c194321..0000000
--- a/RingDaemon.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2017 Savoir-faire Linux Inc.
- *
- * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
- * Author: Asad Salman <me@asad.co>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-
-"use strict";
-class RingDaemon {
- constructor(callbackMap) {
- if (callbackMap) {
- this.dring = require("./dring.node");
- this.dring.init(callbackMap);
- }
- }
-
- boolToStr(bool) {
- if (bool)
- return "TRUE";
- else
- return "FALSE";
- }
-
- addAccount(account) {
- var params = new this.dring.StringMap();
- params.set("Account.type", "RING");
- if (account.archivePassword) {
- params.set("Account.archivePassword", account.archivePassword);
- } else {
- console.log("archivePassword required");
- return;
- }
- if (account.alias)
- params.set("Account.alias", account.alias);
- if (account.displayName)
- params.set("Account.displayName", account.displayName);
- if (account.enable)
- params.set("Account.enable", this.boolToStr(account.enable));
- if (account.autoAnswer)
- params.set("Account.autoAnswer", this.boolToStr(account.autoAnswer));
- if (account.ringtonePath)
- params.set("Account.ringtonePath", account.ringtonePath);
- if (account.ringtoneEnabled)
- params.set("Account.ringtoneEnabled", this.boolToStr(account.ringtoneEnabled));
- if (account.videoEnabled)
- params.set("Account.videoEnabled", this.boolToStr(account.videoEnabled));
- if (account.useragent) {
- params.set("Account.useragent", account.useragent);
- params.set("Account.hasCustomUserAgent", "TRUE");
- } else {
- params.set("Account.hasCustomUserAgent", "FALSE");
- }
- if (account.audioPortMin)
- params.set("Account.audioPortMin", account.audioPortMin);
- if (account.audioPortMax)
- params.set("Account.audioPortMax", account.audioPortMax);
- if (account.videoPortMin)
- params.set("Account.videoPortMin", account.videoPortMin);
- if (account.videoPortMax)
- params.set("Account.videoPortMax", account.videoPortMax);
- if (account.localInterface)
- params.set("Account.localInterface", account.localInterface);
- if (account.publishedSameAsLocal)
- params.set("Account.publishedSameAsLocal", this.boolToStr(account.publishedSameAsLocal));
- if (account.localPort)
- params.set("Account.localPort", account.localPort);
- if (account.publishedPort)
- params.set("Account.publishedPort", account.publishedPort);
- if (account.publishedAddress)
- params.set("Account.publishedAddress", account.publishedAddress);
- if (account.upnpEnabled)
- params.set("Account.upnpEnabled", this.boolToStr(account.upnpEnabled));
-
- this.dring.addAccount(params);
- }
- stringVectToArr(stringvect) {
- const outputArr = [];
- for (let i = 0; i < stringvect.size(); i++)
- outputArr.push(stringvect.get(i));
- return outputArr;
- }
- mapToJs(m) {
- const outputObj = {};
- this.stringVectToArr(m.keys())
- .forEach(k => outputObj[k] = m.get(k));
- return outputObj;
- }
- getAccountList() {
- return this.stringVectToArr(this.dring.getAccountList());
- }
- getAccountDetails(accountId) {
- return this.mapToJs(this.dring.getAccountDetails(accountId));
- }
- getAudioOutputDeviceList() {
- return this.stringVectToArr(this.dring.getAudioOutputDeviceList());
- }
- getVolume(deviceName) {
- return this.dring.getVolume(deviceName);
- }
-
- setVolume(deviceName, volume) {
- return this.dring.setVolume(deviceName, volume);
- }
-
- stop() {
- clearInterval(this.pollIntervalId);
- this.dring.fini();
- }
-}
-
-module.exports = RingDaemon;
\ No newline at end of file
diff --git a/app.js b/app.js
index 5f616cd..861409e 100644
--- a/app.js
+++ b/app.js
@@ -1,49 +1,48 @@
-require('dotenv/config');
+const env = require('dotenv/config')
const express = require('express')
-const app = express();
-const server = require('http').Server(app);
+const http = require('http')
const session = require('express-session')
-const io = require('socket.io')(server);
-const path = require('path');
+//const cookieParser = require('cookie-parser')
+//const io = require('socket.io')(server)
+const path = require('path')
+const passport = require('passport')
+ , LocalStrategy = require('passport-local').Strategy
-//const redis = require('redis')
-const redis = require('redis-url').connect();
+const redis = require('redis-url').connect()
const RedisStore = require('connect-redis')(session)
-/*var passportSocketIo = require('passport.socketio');*/
-//const cookieParser = require('cookie-parser');
-const cookieParser = require('cookie-parser')(process.env.SECRET_KEY_BASE); // <- your secret here
+/*const passportSocketIo = require('passport.socketio')*/
-var indexRouter = require('./routes/index');
+const indexRouter = require('./routes/index')
+
//const cors = require('cors')
-var parser = require('fast-xml-parser');
+const parser = require('fast-xml-parser')
-const RingDaemon = require('./RingDaemon.js');
-const passport = require('passport')
- , LocalStrategy = require('passport-local').Strategy;
+const JamiRestApi = require('./routes/jami')
+const JamiDaemon = require('./JamiDaemon')
-const sessionStore = new RedisStore({ client: redis });
+//const sessionStore = new RedisStore({ client: redis })
+const sessionStore = new session.MemoryStore()
+const app = express()
/*
Configuation for Passeport Js
*/
+//app.use(cookieParser(process.env.SECRET_KEY_BASE));
+app.disable('x-powered-by');
-// app.use(express.static('public'));
-// app.use(cookieParser());
-// app.use(bodyParser());
app.use(session({
- store: sessionStore,
+ //store: sessionStore,
resave: false,
- saveUninitialized: false,
+ saveUninitialized: true,
cookie: {
- secure: process.env.ENVIRONMENT !== 'development' && process.env.ENVIRONMENT !== 'test',
+ secure: false,//process.env.ENVIRONMENT !== 'development' && process.env.ENVIRONMENT !== 'test',
maxAge: 2419200000
},
secret: process.env.SECRET_KEY_BASE
}));
-
app.use(passport.initialize());
app.use(passport.session());
// app.use(app.router);
@@ -78,55 +77,23 @@
tempAccounts holds users accounts while tempting to authenticate them on Jams.
connectedUsers holds users accounts after they got authenticated by Jams.
- Users should be removed from connectedUsers when receiving a disconnect
+ Users should be removed from connectedUsers when receiving a disconnect
web socket call
*/
const tempAccounts = {};
const connectedUsers = {};
-const callbackMap = {
- "IncomingAccountMessage": (accountId, from, message) => {
- console.log("Received message: " + accountId + " " + from + " " + message["text/plain"]);
+const jami = new JamiDaemon();
+const apiRouter = new JamiRestApi(jami).getRouter()
- if (parser.validate(message["text/plain"]) === true) {
- console.log(message["text/plain"]);
- } else {
-
- user = connectedUsers[accountId];
- console.log(user.socketId)
- io.to(user.socketId).emit('receivedMessage', message["text/plain"]);
- //io.emit('receivedMessage', message["text/plain"]);
- }
- },
- "RegistrationStateChanged": (accountId, state, /*int*/ code, detail) => {
- console.log("RegistrationStateChanged: " + accountId + " " + state + " " + code + " " + detail);
- if (state === "REGISTERED") {
- if (tempAccounts[accountId]) {
-
- const ctx = tempAccounts[accountId];
- ctx.newUser.accountId = accountId;
- ctx.newUser.jamiId = jami.dring.getAccountDetails(accountId).get("Account.username");
- //connectedUsers[accountId] = ctx.newUser;
- ctx.done(null, ctx.newUser);
- delete tempAccounts[accountId];
- }
- } else if (state === "ERROR_AUTH") {
- done(null, false);
- //remove account
- }
- }
-}
-const jami = new RingDaemon(callbackMap);
-
-passport.serializeUser(function (user, done) {
+passport.serializeUser((user, done) => {
console.log(user)
connectedUsers[user.accountId] = user;
console.log("=============================SerializeUser called " + user.accountId)
done(null, user.accountId);
});
-
const deserializeUser = (id, done) => {
console.log("=============================DeserializeUser called on: " + id + " " + connectedUsers[id])
done(null, connectedUsers[id]);
@@ -135,25 +102,20 @@
//var tempAccountId = '';
-passport.use(new LocalStrategy(
+const jamsStrategy = new LocalStrategy(
(username, password, done) => {
+
const newUser = {};
newUser.username = username;
- //newUser.socketid =
+ //newUser.socketid =
- const template = jami.dring.getAccountTemplate("RING");
+ const accountId = jami.addAccount({
+ 'managerUri': 'https://jams.savoirfairelinux.com',
+ 'managerUsername': username,
+ 'archivePassword': password
+ });
- /*
- For test purpose we are not checking if a user can SSO using Jams,
- instead we juste create a new user an get he's or her's newly created accountId
- */
- template.set("Account.managerUri", "https://jams.savoirfairelinux.com");
- template.set("Account.managerUsername", username);
- template.set("Account.archivePassword", password);
-
- const accountId = jami.dring.addAccount(template);
-
- const newProps = jami.getAccountDetails(accountId);
+ const newProps = jami.getAccount(accountId).details;
console.log(newProps);
//Object.entries(newProps).forEach(v => console.log(v[0], v[1]))
//tempAccountId = accountId;
@@ -175,23 +137,62 @@
return done(null, user);
});*/
}
-));
+);
+jamsStrategy.name = "jams";
-app.post('/api/login', passport.authenticate('local'), function (req, res) {
- res.json({ loggedin: true });
+const localStrategy = new LocalStrategy(
+ (username, password, done) => {
+ console.log("localStrategy: " + username + " " + password);
+
+ const newUser = {};
+ newUser.accountId = jami.getAccountList()[0].getId();
+ console.log("Local AccountId: " + newUser.accountId);
+ connectedUsers[newUser.accountId] = newUser;
+ done(null, newUser);
+ }
+);
+
+passport.use(jamsStrategy);
+passport.use(localStrategy);
+
+const secured = (req, res, next) => {
+ console.log(`isSecured ${req.user}`);
+ if (req.user && req.user.accountId) {
+ return next();
+ }
+ res.status(401).end()
+};
+const securedRedirect = (req, res, next) => {
+ if (req.user && req.user.accountId) {
+ return next();
+ }
+ req.session.returnTo = req.originalUrl;
+ res.redirect('/login');
+};
+
+app.post('/auth', passport.authenticate('jams'), (req, res) => {
+ res.json({ loggedin: true })
});
+app.post('/api/localLogin', passport.authenticate('local'), (req, res) => {
+ res.json({ loggedin: true })
+});
+
+app.use('/api', secured, apiRouter);
+
app.use('/', indexRouter);
/* GET React App */
app.use(express.static(path.join(__dirname, 'public')))
-app.use(function (req, res, next) {
+app.use((req, res, next) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
+const server = http.Server(app);
server.listen(3000);
+/*
io.on('connection', (socket) => {
console.log("Client just connected !")
socket.on('SendMessage', (data) => {
@@ -202,7 +203,6 @@
});
});
-
io.use((socket, next) => {
cookieParser(socket.handshake, {}, (err) => {
if (err) {
@@ -233,8 +233,6 @@
user.socketId = socket.id;
socket.session.user = user;
console.log("User added to session --------> " + user.accountId);
- /*data[auth.userProperty] = user;
- data[auth.userProperty].logged_in = true;*/
//auth.success(data, accept);
next(err, true);
});
@@ -242,3 +240,4 @@
});
});
});
+*/
diff --git a/jaas-client/dist/index.html b/jaas-client/dist/index.html
index eaccd26..3353d9d 100644
--- a/jaas-client/dist/index.html
+++ b/jaas-client/dist/index.html
@@ -9,7 +9,7 @@
<body>
<div id="root"></div>
- <script src="bundle.js"></script>
+ <script src="/bundle.js"></script>
</body>
</html>
\ No newline at end of file
diff --git a/jaas-client/src/App.css b/jaas-client/src/App.css
index 74b5e05..915ab44 100644
--- a/jaas-client/src/App.css
+++ b/jaas-client/src/App.css
@@ -28,6 +28,10 @@
color: #61dafb;
}
+.loginCard {
+ margin-top: 2em;
+}
+
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
diff --git a/jaas-client/src/App.js b/jaas-client/src/App.js
index d1c880b..c943b71 100644
--- a/jaas-client/src/App.js
+++ b/jaas-client/src/App.js
@@ -1,35 +1,47 @@
/*
Company: Savoir-faire Linux
Author: Larbi Gharib <larbi.gharib@savoirfairelinux.com>
- UI Project inspired from https://scrimba.com/p/pbNpTv/cbZBmfV
License: AGPL-3
*/
import React from 'react';
+import CssBaseline from '@material-ui/core/CssBaseline';
+import authManager from './AuthManager'
//import logo from './logo.svg';
import './App.css';
-
import { BrowserRouter as Router, Route, Switch, Link, Redirect } from 'react-router-dom';
-import SignInPage from "./pages/index.jsx";
-import Jaas from "./pages/jaas.jsx"
+import SignInPage from "./pages/loginDialog.jsx";
+import JamiMessenger from "./pages/messenger.jsx"
+import AccountSettings from "./pages/accountSettings.jsx"
+import AccountSelection from "./pages/accountSelection.jsx"
import NotFoundPage from "./pages/404.jsx"
-
class App extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ authenticated: authManager.isAuthenticated(),
+ };
+ authManager.setOnAuthChanged(authenticated => this.setState({authenticated}))
+ }
render() {
- return <Router>
- <Switch>
- <Route exact path="/" component={SignInPage} />
- <Route to="/Jaas" component={Jaas} />
- <Route exact path="/404" component={NotFoundPage} />
- <Redirect to="/404" />
- </Switch>
-
- </Router>
+ return <React.Fragment>
+ <CssBaseline />
+ <Router>
+ <Switch>
+ <Route exact path="/"><Redirect to="/account" /></Route>
+ <Route path="/account/:accountId" component={AccountSettings} />
+ <Route path="/account" component={AccountSelection} />
+ <Route path="/messaging" component={JamiMessenger} />
+ <Route component={NotFoundPage} />
+ </Switch>
+ </Router>
+ {!this.state.authenticated && <SignInPage open={!this.state.authenticated}/>}
+ </React.Fragment>
}
}
-export default App;
\ No newline at end of file
+export default App
\ No newline at end of file
diff --git a/jaas-client/src/AuthManager.js b/jaas-client/src/AuthManager.js
new file mode 100644
index 0000000..09aef64
--- /dev/null
+++ b/jaas-client/src/AuthManager.js
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017-2021 Savoir-faire Linux Inc.
+ *
+ * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//import cookie from 'cookie';
+
+class AuthManager {
+ constructor() {
+ console.log("AuthManager()")
+ this.authenticated = true//'connect.sid' in cookie.parse(document.cookie)
+ this.authenticating = false
+ this.tasks = []
+ this.onAuthChanged = undefined
+ }
+ setOnAuthChanged(onAuthChanged) {
+ this.onAuthChanged = onAuthChanged
+ }
+
+ isAuthenticated() {
+ return this.authenticated
+ }
+
+ authenticate() {
+ if (this.authenticating)
+ return
+ console.log("Starting authentication")
+ this.authenticating = true
+ fetch('/api/localLogin?username=local&password=local', { method:"POST" })
+ .then(response => {
+ console.log(response)
+ this.authenticating = false
+ this.authenticated = response.ok && response.status === 200
+ if (this.onAuthChanged)
+ this.onAuthChanged(this.authenticated)
+ while (this.tasks.length !== 0) {
+ const task = this.tasks.shift()
+ if (this.authenticated)
+ fetch(task.url, task.init).then(res => task.resolve(res))
+ else
+ task.reject(new Error("Authentication failed"))
+ }
+ })
+ }
+
+ disconnect() {
+ console.log("Disconnect")
+ this.authenticated = false
+ if (this.onAuthChanged)
+ this.onAuthChanged(this.authenticated)
+ }
+
+ fetch(url, init) {
+ console.log(`get ${url}`)
+ if (!this.authenticated) {
+ return new Promise((resolve, reject) => this.tasks.push({url, init, resolve, reject}))
+ }
+ return fetch(url, init)
+ .then(response => {
+ console.log(`Got status ${response.status}`)
+ if (response.status === 401) {
+ this.disconnect()
+ return this.fetch(url, init)
+ }
+ return response
+ })
+ }
+}
+
+export default new AuthManager()
\ No newline at end of file
diff --git a/jaas-client/src/components/AccountList.js b/jaas-client/src/components/AccountList.js
new file mode 100644
index 0000000..6f38c3c
--- /dev/null
+++ b/jaas-client/src/components/AccountList.js
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+import ListItemText from '@material-ui/core/ListItemText';
+import ListItemAvatar from '@material-ui/core/ListItemAvatar';
+import Avatar from '@material-ui/core/Avatar';
+import PersonRoundedIcon from '@material-ui/icons/PersonRounded';
+
+class AccountList extends React.Component {
+ render() {
+ return (
+ <List>
+ {
+ this.props.accounts.map(account => <ListItem button key={account.getId()} onClick={() => this.props.onClick(account)}>
+ <ListItemAvatar>
+ <Avatar>
+ <PersonRoundedIcon />
+ </Avatar>
+ </ListItemAvatar>
+ <ListItemText primary={account.getDisplayName()} secondary={account.getDisplayUri()} />
+ </ListItem>
+ )
+ }
+ </List>)
+ }
+}
+
+export default AccountList;
\ No newline at end of file
diff --git a/jaas-client/src/components/AccountPreferences.js b/jaas-client/src/components/AccountPreferences.js
new file mode 100644
index 0000000..f486dd9
--- /dev/null
+++ b/jaas-client/src/components/AccountPreferences.js
@@ -0,0 +1,63 @@
+import React from 'react';
+
+import Typography from '@material-ui/core/Typography';
+
+import JamiIdCard from './JamiIdCard';
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+import ListItemIcon from '@material-ui/core/ListItemIcon';
+import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
+import ListItemText from '@material-ui/core/ListItemText';
+import ListSubheader from '@material-ui/core/ListSubheader';
+import Switch from '@material-ui/core/Switch';
+import PhoneCallbackIcon from '@material-ui/icons/PhoneCallback';
+import GroupRoundedIcon from '@material-ui/icons/GroupRounded';
+import Account from '../../../model/Account';
+
+class AccountPreferences extends React.Component {
+
+ render() {
+ const account = this.props.account
+ const isJamiAccount = account.getType() === Account.TYPE_JAMI
+ return (
+ <React.Fragment>
+ <Typography variant="h2" component="h2">Jami account</Typography>
+
+ {isJamiAccount &&
+ <JamiIdCard account={account} />}
+
+ <List subheader={<ListSubheader>Settings</ListSubheader>}>
+ <ListItem>
+ <ListItemIcon>
+ <GroupRoundedIcon />
+ </ListItemIcon>
+ <ListItemText id="switch-list-label-rendezvous" primary="Rendez-Vous point" />
+ <ListItemSecondaryAction>
+ <Switch
+ edge="end"
+ /*onChange={handleToggle('wifi')}*/
+ checked={account.isRendezVous()}
+ inputProps={{ 'aria-labelledby': 'switch-list-label-wifi' }}
+ />
+ </ListItemSecondaryAction>
+ </ListItem>
+ <ListItem>
+ <ListItemIcon>
+ <PhoneCallbackIcon />
+ </ListItemIcon>
+ <ListItemText id="switch-list-label-publicin" primary="Allow connection from unkown peers" />
+ <ListItemSecondaryAction>
+ <Switch
+ edge="end"
+ /*onChange={handleToggle('bluetooth')}*/
+ checked={account.isPublicIn()}
+ inputProps={{ 'aria-labelledby': 'switch-list-label-bluetooth' }}
+ />
+ </ListItemSecondaryAction>
+ </ListItem>
+ </List>
+ </React.Fragment>)
+ }
+}
+
+export default AccountPreferences;
\ No newline at end of file
diff --git a/jaas-client/src/components/Header.js b/jaas-client/src/components/Header.js
index 22a6c74..d245976 100644
--- a/jaas-client/src/components/Header.js
+++ b/jaas-client/src/components/Header.js
@@ -2,10 +2,11 @@
import Button from '@material-ui/core/Button';
import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem';
-import { useHistory } from "react-router-dom";
+//import { useHistory } from "react-router-dom";
+import authManager from '../AuthManager'
export default function Header() {
- const history = useHistory();
+ //const history = useHistory();
const [anchorEl, setAnchorEl] = React.useState(null);
@@ -18,15 +19,16 @@
};
const disconnect = () => {
- let path = `/`;
- history.push(path);
+ authManager.disconnect()
+ //let path = `/`;
+ //history.push(path);
}
return (
<div>
<Button aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick}>
Menu
- </Button>
+ </Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
diff --git a/jaas-client/src/components/JamiIdCard.js b/jaas-client/src/components/JamiIdCard.js
new file mode 100644
index 0000000..eb31a26
--- /dev/null
+++ b/jaas-client/src/components/JamiIdCard.js
@@ -0,0 +1,25 @@
+import React from 'react';
+
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import Typography from '@material-ui/core/Typography';
+
+class JamiIdCard extends React.Component {
+ render() {
+ const account = this.props.account
+ const registeredName = account.getRegisteredName()
+ return (
+ <Card style={{marginBottom:16}}>
+ <CardContent>
+ <Typography variant="h6">Jami key ID</Typography>
+ <Typography variant="body1">{account.getUri()}</Typography>
+ {registeredName && <div>
+ <Typography variant="h6">Jami username</Typography>
+ <Typography variant="body1">{registeredName}</Typography></div>
+ }
+ </CardContent>
+ </Card>)
+ }
+}
+
+export default JamiIdCard;
\ No newline at end of file
diff --git a/jaas-client/src/components/SendMessageForm.js b/jaas-client/src/components/SendMessageForm.js
index e52635d..43a4998 100644
--- a/jaas-client/src/components/SendMessageForm.js
+++ b/jaas-client/src/components/SendMessageForm.js
@@ -1,5 +1,6 @@
import React from 'react'
-import InputEmoji from "react-input-emoji";
+import TextField from '@material-ui/core/TextField'
+//import InputEmoji from "react-input-emoji";
class SendMessageForm extends React.Component {
@@ -25,7 +26,6 @@
this.setState({
message: ''
})
-
}
render() {
@@ -33,7 +33,7 @@
<div
//onSubmit={this.handleSubmit}
className="send-message-form">
- <InputEmoji
+ <TextField
disabled={this.props.disabled}
onChange={this.handleChange}
value={this.state.message}
diff --git a/jaas-client/src/pages/accountSelection.jsx b/jaas-client/src/pages/accountSelection.jsx
new file mode 100644
index 0000000..2a4c99b
--- /dev/null
+++ b/jaas-client/src/pages/accountSelection.jsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import Header from '../components/Header'
+import authManager from '../AuthManager'
+import AccountList from '../components/AccountList';
+import CircularProgress from '@material-ui/core/CircularProgress';
+import { withRouter } from 'react-router-dom';
+import Card from '@material-ui/core/Card';
+import CardHeader from '@material-ui/core/CardHeader';
+import Container from '@material-ui/core/Container';
+import Account from '../../../model/Account';
+
+class AccountSelection extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.controller = new AbortController()
+ this.state = {
+ loaded: false,
+ error: false,
+ accounts: []
+ }
+ }
+
+ componentDidMount() {
+ if (!this.state.loaded) {
+ authManager.fetch('/api/accounts', {signal: this.controller.signal})
+ .then(res => res.json())
+ .then(result => {
+ console.log(result)
+ this.setState({
+ loaded: true,
+ accounts: result.map(account => Account.from(account)),
+ })
+ }, error => {
+ console.log(`get error ${error}`)
+ this.setState({
+ loaded: true,
+ error: true
+ })
+ })
+ }
+ }
+ componentWillUnmount() {
+ this.controller.abort()
+ }
+
+ render() {
+ if (!this.state.loaded)
+ return <Container><CircularProgress /></Container>
+ return (
+ <div className="app" style={{marginBottom:32}} >
+ <Header />
+ <Container>
+ <Card style={{marginTop:32, marginBottom:32}}>
+ <CardHeader title="Choose an account" />
+ <AccountList accounts={this.state.accounts} onClick={account => this.props.history.push(`/account/${account.getId()}`)} />
+ </Card>
+ </Container>
+ </div>
+ )
+ }
+}
+
+export default withRouter(AccountSelection);
\ No newline at end of file
diff --git a/jaas-client/src/pages/accountSettings.jsx b/jaas-client/src/pages/accountSettings.jsx
new file mode 100644
index 0000000..d7f71cf
--- /dev/null
+++ b/jaas-client/src/pages/accountSettings.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import Header from '../components/Header'
+import AccountPreferences from '../components/AccountPreferences'
+import Container from '@material-ui/core/Container';
+import CircularProgress from '@material-ui/core/CircularProgress';
+import authManager from '../AuthManager'
+import Account from '../../../model/Account'
+
+class AccountSettings extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.accountId = props.accountId || props.match.params.accountId
+ this.state = { loaded: false, account: props.account }
+ this.req = undefined
+ this.controller = new AbortController()
+ }
+
+ componentDidMount() {
+ if (this.req === undefined) {
+ this.req = authManager.fetch(`/api/accounts/${this.accountId}`, {signal: this.controller.signal})
+ .then(res => res.json())
+ .then(result => {
+ console.log(result)
+ this.setState({loaded: true, account: Account.from(result)})
+ })
+ }
+ }
+
+ componentWillUnmount() {
+ this.controller.abort()
+ this.req = undefined
+ }
+
+ render() {
+ return (
+ <Container maxWidth="sm" className="app" >
+ <Header />
+ {this.state.loaded ? <AccountPreferences account={this.state.account} /> : <CircularProgress />}
+ </Container>
+ )
+ }
+}
+
+export default AccountSettings;
\ No newline at end of file
diff --git a/jaas-client/src/pages/index.jsx b/jaas-client/src/pages/index.jsx
deleted file mode 100644
index ad831b1..0000000
--- a/jaas-client/src/pages/index.jsx
+++ /dev/null
@@ -1,223 +0,0 @@
-import React from 'react';
-import Avatar from '@material-ui/core/Avatar';
-import Button from '@material-ui/core/Button';
-import CssBaseline from '@material-ui/core/CssBaseline';
-import TextField from '@material-ui/core/TextField';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
-import Checkbox from '@material-ui/core/Checkbox';
-import Link from '@material-ui/core/Link';
-import Grid from '@material-ui/core/Grid';
-import Box from '@material-ui/core/Box';
-import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
-import Typography from '@material-ui/core/Typography';
-//import { makeStyles } from '@material-ui/core/styles';
-import Container from '@material-ui/core/Container';
-import { Redirect } from 'react-router-dom';
-import CircularProgress from '@material-ui/core/CircularProgress';
-import DialogTitle from '@material-ui/core/DialogTitle';
-import Dialog from '@material-ui/core/Dialog';
-
-
-function Copyright() {
- return (
- <Typography variant="body2" color="textSecondary" align="center">
- {'Copyright © 2016-'}{new Date().getFullYear()}{' Savoir-faire Linux Inc. GNU '}
- <Link color="inherit" href="https://jami.net/">
- Jami.net
- </Link>{' '}
-
- {'.'}
- </Typography>
- );
-}
-
-/*const useStyles = makeStyles((theme) => ({
- paper: {
- marginTop: theme.spacing(8),
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- },
- avatar: {
- margin: theme.spacing(1),
- backgroundColor: theme.palette.secondary.main,
- },
- form: {
- width: '100%', // Fix IE 11 issue.
- marginTop: theme.spacing(1),
- },
- submit: {
- margin: theme.spacing(3, 0, 2),
- },
-}));*/
-
-/*function SignIn() {
- const classes = useStyles();
-
-
-}*/
-
-/*
- TODO:
- Use useState to handle username password and redirect states to render this page to
- comply with material-ui usage of useStyles
- Src: https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/
-*/
-
-class SignInPage extends React.Component {
-
- constructor() {
- super()
- this.state = {
- username: '',
- password: '',
- redirect: false,
- session: null,
- submitted: false,
- loading: false,
- error: false,
- open: false,
- errorMessage: ''
- }
- this.handleSubmit = this.handleSubmit.bind(this);
- }
-
- handleusername(text) {
- this.setState({ username: text.target.value })
- }
-
- handlePassword(text) {
- this.setState({ password: text.target.value })
- }
-
- handleSubmit(event) {
- event.preventDefault();
- let obj = {}
- obj.username = this.state.username;
- obj.password = this.state.password;
-
- this.setState({
- submitted: true,
- loading: true
- })
-
- fetch('/api/login?username=' + obj.username + '&password=' + obj.password,
- {
- header: {
- "Content-Type": "application/json"
- },
- method: "POST",
- credentials: 'same-origin'
- //body: JSON.stringify({ obj })
- }
- ).then((res) => {
- if (res.status == '200') {
- this.setState({
- redirect: true
- });
- } else if (res.status == '401') {
- this.setState({
- loading: false,
- error: true,
- open: true,
- errorMessage: "Wrong credentials! Your are not allowed to connect"
- })
- }
- //this.setState({ session: res });
- }).catch((e) => {
- this.setState({
- loading: false,
- error: true,
- open: true,
- errorMessage: e.toString()
- })
- })
- }
-
-
- render() {
- if (this.state.redirect) {
- return <Redirect to="/jaas" />
- }
-
- return (
- <Container component="main" maxWidth="xs" >
- <CssBaseline />
- <div className=""/*{classes.paper}*/>
- <Typography component="h1" variant="h5">
- Se connecter
- </Typography>
- <form className=""/*{classes.form}*/ onSubmit={this.handleSubmit} >
- <TextField
- variant="outlined"
- margin="normal"
- required
- fullWidth
- id="username"
- label="LDAP Savoir-faire Linux"
- name="username"
- autoComplete="email"
- autoFocus
- onChange={(text) => { this.handleusername(text) }}
- />
- <TextField
- variant="outlined"
- margin="normal"
- required
- fullWidth
- name="password"
- label="Mot de passe"
- type="password"
- id="password"
- autoComplete="current-password"
- onChange={(text) => { this.handlePassword(text) }}
- />
- <FormControlLabel
- control={<Checkbox value="remember" color="primary" />}
- label="Se rapeller de moi"
- />
- <Button
- type="submit"
- fullWidth
- variant="contained"
- color="primary"
- className=""/*{classes.submit}*/
- // onClick={() => { this.login() }}
- >
- Se connecter
- </Button>
- <Grid container>
- <Grid item xs>
- <Link href="#" variant="body2">
- Mot de passe oublié ?
- </Link>
- </Grid>
- <Grid item>
- <Link href="#" variant="body2">
- {"Tu n'as pas de compte? Inscris-toi"}
- </Link>
- </Grid>
- </Grid>
- </form>
- </div>
- <Box mt={8}>
- <Copyright />
- </Box>
- {this.state.submitted && this.state.loading && <CircularProgress
- style={{ position: 'relative' }}
- size={40}
- top={10}
- style={{ marginLeft: '50%' }}
- />}
- {
- this.state.error && <Dialog aria-labelledby="simple-dialog-title" open={this.state.open} >
- <DialogTitle id="simple-dialog-title">{this.state.errorMessage}</DialogTitle>
- </Dialog>
- }
- </Container>
- );
- }
-}
-
-
-export default SignInPage;
\ No newline at end of file
diff --git a/jaas-client/src/pages/loginDialog.jsx b/jaas-client/src/pages/loginDialog.jsx
new file mode 100644
index 0000000..93f9f59
--- /dev/null
+++ b/jaas-client/src/pages/loginDialog.jsx
@@ -0,0 +1,245 @@
+import React from 'react';
+
+import Avatar from '@material-ui/core/Avatar';
+import Button from '@material-ui/core/Button';
+import TextField from '@material-ui/core/TextField';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import Checkbox from '@material-ui/core/Checkbox';
+import Link from '@material-ui/core/Link';
+import Grid from '@material-ui/core/Grid';
+import Box from '@material-ui/core/Box';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import CardActionArea from '@material-ui/core/CardActionArea';
+import CardActions from '@material-ui/core/CardActions';
+
+import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
+import Typography from '@material-ui/core/Typography';
+//import { makeStyles } from '@material-ui/core/styles';
+import Container from '@material-ui/core/Container';
+import { Redirect } from 'react-router-dom';
+import CircularProgress from '@material-ui/core/CircularProgress';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import Dialog from '@material-ui/core/Dialog';
+import DialogActions from '@material-ui/core/DialogActions';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogContentText from '@material-ui/core/DialogContentText';
+
+import authManager from '../AuthManager'
+
+function Copyright() {
+ return (
+ <Typography variant="body2" color="textSecondary" align="center">
+ {'Copyright © 2016-'}{new Date().getFullYear()}{' Savoir-faire Linux Inc.'}
+ <Link color="inherit" href="https://jami.net/">
+ Jami.net
+ </Link>{' '}
+ {'.'}
+ </Typography>
+ );
+}
+
+/*const useStyles = makeStyles((theme) => ({
+ paper: {
+ marginTop: theme.spacing(8),
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ },
+ avatar: {
+ margin: theme.spacing(1),
+ backgroundColor: theme.palette.secondary.main,
+ },
+ form: {
+ width: '100%', // Fix IE 11 issue.
+ marginTop: theme.spacing(1),
+ },
+ submit: {
+ margin: theme.spacing(3, 0, 2),
+ },
+}));*/
+
+/*function SignIn() {
+ const classes = useStyles();
+
+
+}*/
+
+/*
+ TODO:
+ Use useState to handle username password and redirect states to render this page to
+ comply with material-ui usage of useStyles
+ Src: https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/
+*/
+
+class SignInPage extends React.Component {
+
+ constructor(props) {
+ console.log("SignInPage " + props.open)
+ super(props)
+ this.state = {
+ /*username: '',
+ password: '',
+ redirect: false,
+ session: null,*/
+ submitted: false,
+ loading: false/*,
+ error: false,
+ open: false,
+ errorMessage: ''*/
+ }
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.localLogin = this.localLogin.bind(this);
+ }
+
+ handleusername(text) {
+ this.setState({ username: text.target.value })
+ }
+
+ handlePassword(text) {
+ this.setState({ password: text.target.value })
+ }
+
+ localLogin() {
+ this.setState({
+ submitted: true,
+ loading: true
+ })
+ authManager.authenticate()
+ /*fetch('/api/localLogin?username=none&password=none', {
+ header: { "Content-Type": "application/json" },
+ method: "POST",
+ credentials: 'same-origin'
+ //body: JSON.stringify({ obj })
+ })
+ .then((res) => {
+ if (res.status === '200') {
+ this.setState({
+ redirect: true
+ });
+ } else if (res.status === '401') {
+ this.setState({
+ loading: false,
+ error: true,
+ open: true,
+ errorMessage: "Wrong credentials! Your are not allowed to connect"
+ })
+ }
+ //this.setState({ session: res });
+ }).catch((e) => {
+ this.setState({
+ loading: false,
+ error: true,
+ open: true,
+ errorMessage: e.toString()
+ })
+ })*/
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+ let obj = {}
+ obj.username = this.state.username;
+ obj.password = this.state.password;
+
+ this.setState({
+ submitted: true,
+ loading: true
+ })
+
+ fetch('/api/login?username=' + obj.username + '&password=' + obj.password,
+ {
+ header: {
+ "Content-Type": "application/json"
+ },
+ method: "POST",
+ credentials: 'same-origin'
+ //body: JSON.stringify({ obj })
+ }
+ ).then((res) => {
+ if (res.status === '200') {
+ this.setState({
+ redirect: true
+ });
+ } else if (res.status === '401') {
+ this.setState({
+ loading: false,
+ error: true,
+ open: true,
+ errorMessage: "Wrong credentials! Your are not allowed to connect"
+ })
+ }
+ //this.setState({ session: res });
+ }).catch((e) => {
+ this.setState({
+ loading: false,
+ error: true,
+ open: true,
+ errorMessage: e.toString()
+ })
+ })
+ }
+
+ render() {
+ console.log("SignInPage render " + this.props.open)
+ return (
+ <Dialog open={this.props.open}>
+ <DialogTitle>Se connecter</DialogTitle>
+ <DialogContent>
+ <Button
+ type="submit"
+ fullWidth
+ variant="contained"
+ color="primary"
+ className=""/*{classes.submit}*/
+ onClick={() => { this.localLogin() }}
+ >
+ Compte local
+ </Button>
+
+ <TextField
+ variant="outlined"
+ margin="normal"
+ required
+ fullWidth
+ id="username"
+ label="LDAP Savoir-faire Linux"
+ name="username"
+ autoComplete="email"
+ autoFocus
+ onChange={(text) => { this.handleusername(text) }}
+ />
+ <TextField
+ variant="outlined"
+ margin="normal"
+ required
+ fullWidth
+ name="password"
+ label="Mot de passe"
+ type="password"
+ id="password"
+ autoComplete="current-password"
+ onChange={(text) => { this.handlePassword(text) }}
+ />
+ <FormControlLabel
+ control={<Checkbox value="remember" color="primary" />}
+ label="Se rapeller de moi"
+ />
+ </DialogContent>
+
+ <DialogActions>
+ <Button
+ type="submit"
+ size="medium"
+ onClick={this.handleSubmit}
+ >
+ Se connecter
+ </Button>
+ </DialogActions>
+ </Dialog>
+ );
+ }
+}
+
+
+export default SignInPage;
\ No newline at end of file
diff --git a/jaas-client/src/pages/jaas.jsx b/jaas-client/src/pages/messenger.jsx
similarity index 84%
rename from jaas-client/src/pages/jaas.jsx
rename to jaas-client/src/pages/messenger.jsx
index 5c8a4aa..23f3935 100644
--- a/jaas-client/src/pages/jaas.jsx
+++ b/jaas-client/src/pages/messenger.jsx
@@ -4,11 +4,11 @@
import MessageList from '../components/MessageList'
import SendMessageForm from '../components/SendMessageForm'
import NewContactForm from '../components/NewContactForm'
-import Sound from 'react-sound';
+//import Sound from 'react-sound';
import io from "socket.io-client";
-var socket = io.connect('http://localhost:3000');
+//const socket = io.connect('http://localhost:3000');
-class Jaas extends React.Component {
+class JamiMessenger extends React.Component {
constructor() {
super()
@@ -17,11 +17,10 @@
sound: false
}
- socket.on('connect', () => {
+ /*socket.on('connect', () => {
console.log("Success !")
- })
+ })*/
- //import io from 'socket.io-client';
//this.socket = socketIOClient(ENDPOINT);
//this.socket.on("FromAPI", data => {
@@ -33,8 +32,8 @@
}
componentDidMount() {
- socket.on('receivedMessage', (data) => {
- var message = {
+ /*socket.on('receivedMessage', (data) => {
+ const message = {
senderId: '65f6674b26e5af6ed0b4e92a13b80ff4bbfdf1e8',
text: data
}
@@ -42,7 +41,7 @@
messages: [...this.state.messages, message],
sound: true
})
- });
+ });*/
}
sendMessage(text) {
@@ -51,7 +50,7 @@
destinationId: '65f6674b26e5af6ed0b4e92a13b80ff4bbfdf1e8',
text: text
}
- socket.emit("SendMessage", data);
+ //socket.emit("SendMessage", data);
console.log(data.text);
this.setState({
messages: [...this.state.messages, data],
@@ -79,4 +78,4 @@
}
}
-export default Jaas;
\ No newline at end of file
+export default JamiMessenger;
\ No newline at end of file
diff --git a/model/Account.js b/model/Account.js
new file mode 100644
index 0000000..3141176
--- /dev/null
+++ b/model/Account.js
@@ -0,0 +1,55 @@
+class Account {
+ constructor(id, details, volatileDetails) {
+ this.id = id
+ this.details = details
+ this.volatileDetails = volatileDetails
+ }
+
+ static from(object) {
+ return new Account(object.id, object.details, object.volatileDetails)
+ }
+
+ update(data) {
+ this.details = data.details
+ this.volatileDetails = data.volatileDetails
+ }
+
+ getId() { return this.id }
+
+ getType() { return this.details["Account.type"] }
+
+ getUri() { return this.details["Account.username"] }
+
+ getRegisteredName() { return this.volatileDetails["Account.registeredName"] }
+
+ isRendezVous() { return this.details["Account.rendezVous"] === Account.BOOL_TRUE }
+ isPublicIn() { return this.details["DHT.PublicInCalls"] === Account.BOOL_TRUE }
+
+ getObject() {
+ return {
+ id: this.id,
+ details: this.details,
+ volatileDetails: this.volatileDetails
+ }
+ }
+
+ getSummary() {
+ return this.getObject()
+ }
+
+ getDisplayName() {
+ return this.details["Account.displayName"] || this.getDisplayUri()
+ }
+
+ getDisplayUri() {
+ return this.getRegisteredName() || this.getUri()
+ }
+}
+
+Account.TYPE_JAMI = "RING"
+Account.TYPE_SIP = "SIP"
+
+Account.BOOL_TRUE = "true"
+Account.BOOL_FALSE = "false"
+
+module.exports = Account;
diff --git a/public/index.html b/public/index.html
index eaccd26..3353d9d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -9,7 +9,7 @@
<body>
<div id="root"></div>
- <script src="bundle.js"></script>
+ <script src="/bundle.js"></script>
</body>
</html>
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
index 7ddb40e..01e6c07 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,15 +1,9 @@
-var express = require('express');
-var router = express.Router();
-var path = require('path');
-
-// /* GET home page. */
-// router.get('/', function (req, res, next) {
-// console.log("Request get index");
-// res.render('index', { title: 'Express' });
-// });
+const express = require('express');
+const router = express.Router();
+const path = require('path');
/* GET React App */
-router.get(['/app', '/app/*'], function (req, res, next) {
+router.get(['/app', '/app/*'], (req, res, next) => {
res.sendFile(path.join(__dirname, '../public', 'index.html'));
});
diff --git a/routes/jami.js b/routes/jami.js
new file mode 100644
index 0000000..90b9422
--- /dev/null
+++ b/routes/jami.js
@@ -0,0 +1,43 @@
+const express = require('express');
+
+class JamiRestApi {
+ constructor(jami) {
+ this.jami = jami
+ }
+
+ getRouter() {
+ const router = express.Router({mergeParams: true});
+
+ // Accounts
+ router.get(['/accounts'], (req, res, next) => {
+ console.log("Request account list")
+ res.json(this.jami.getAccountList().map(account => account.getSummary()))
+ });
+
+ router.get(['/accounts/:accountId'], (req, res, next) => {
+ console.log(`Request account ${req.params.accountId}`)
+ const account = this.jami.getAccount(req.params.accountId);
+ if (account)
+ res.json(account.getObject())
+ else
+ res.sendStatus(404)
+ });
+
+ // Conversations
+ const conversationRouter = express.Router({mergeParams: true});
+ conversationRouter.get('/', (req, res, next) => {
+ console.log(`Request conversations for account ${req.params.accountId}`);
+ res.json([]);
+ })
+
+ conversationRouter.get('/:conversationId', (req, res, next) => {
+ console.log(`Request conversation ${req.params.conversationId} for account ${req.params.accountId}`);
+ res.json({});
+ })
+
+ router.use('/accounts/:accountId/conversations', conversationRouter);
+ return router;
+ }
+}
+
+module.exports = JamiRestApi;