diff --git a/README.md b/README.md
index 571e1d6..ef3687f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
-# oozik 2
+# voozik
Webapp edition
socket messages types: serverInfos, login, leave, userList, createRoom, connectRoom, offer, answer, candidate
+data types: status, vote
diff --git a/client/public/index.html b/client/public/index.html
index 4123528..e9bb139 100644
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -4,6 +4,7 @@
+
<%= htmlWebpackPlugin.options.title %>
diff --git a/client/src/store/appModule.js b/client/src/store/appModule.js
index 7478e05..09fff12 100644
--- a/client/src/store/appModule.js
+++ b/client/src/store/appModule.js
@@ -32,6 +32,11 @@ const actions = {
createRoom ({ commit, dispatch }, code) {
commit('CREATE_ROOM')
dispatch('room/setRoomCode', code, { root: true })
+ dispatch('room/setAdmin', null, { root: true })
+ },
+ connectRoom ({ commit, dispatch }, name) {
+ commit('CONNECT_ROOM')
+ dispatch('rtc/makeOffer', name, { root: true })
},
error ({ commit }, error) {
commit('ERROR', error)
@@ -56,8 +61,9 @@ const mutations = {
state.serverStatus = serverStatus
},
CREATE_ROOM (state) {
- state.room = true
- state.admin = true
+ router.push({ name: 'Room' })
+ },
+ CONNECT_ROOM (state) {
router.push({ name: 'Room' })
},
ERROR (state, error) {
diff --git a/client/src/store/roomModule.js b/client/src/store/roomModule.js
index 83c9709..7a0f27c 100644
--- a/client/src/store/roomModule.js
+++ b/client/src/store/roomModule.js
@@ -9,8 +9,6 @@ const state = {
}
const getters = {
- displayRoomCode: state => state.roomCode,
- displayRoomName: state => state.roomName
}
const actions = {
@@ -22,6 +20,9 @@ const actions = {
},
setRoomStatus ({ commit }, roomStatus) {
commit('SET_ROOMSTATUS', roomStatus)
+ },
+ setAdmin ({ commit }) {
+ commit('SET_ADMIN')
}
}
@@ -34,6 +35,12 @@ const mutations = {
},
SET_ROOMSTATUS (state, roomStatus) {
state.roomStatus = roomStatus
+ },
+ SET_ADMIN (state) {
+ state.admin = true
+ },
+ BROADCAST_ROOMSTATUS (state) {
+
}
}
diff --git a/client/src/store/rtcModule.js b/client/src/store/rtcModule.js
index 4a522a4..d08dadf 100644
--- a/client/src/store/rtcModule.js
+++ b/client/src/store/rtcModule.js
@@ -1,4 +1,7 @@
import { send } from './signalPlugin'
+import store from './index'
+
+var lastPeer // horrible
const configuration = {
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
@@ -6,7 +9,7 @@ const configuration = {
const state = {
name: null,
- connections: new Map()
+ peers: []
}
const getters = {
@@ -17,15 +20,21 @@ const actions = {
setName ({ commit }, name) {
commit('SET_NAME', name)
},
- async offer ({ commit }, offer, name) {
- commit('CREATE_PEER_CONNECTION', name)
- commit('ANSWER', name, offer)
+ async makeOffer ({ commit }, targetName) {
+ commit('CREATE_PEER_CONNECTION', targetName)
+ commit('OFFER', targetName)
},
- answer ({ commit }, answer, name) {
- commit('OFFER', name, answer)
+ async offer ({ commit }, { offer, senderName }) {
+ console.log('offer from ' + senderName)
+ commit('CREATE_PEER_CONNECTION', senderName)
+ commit('ANSWER', { target: senderName, offer: offer })
},
- candidate ({ commit }, candidate, name) {
- commit('ANSWER', name, candidate)
+ answer ({ commit }, { answer, senderName }) {
+ console.log('answer from ' + senderName)
+ commit('FINALIZE', { target: senderName, answer: answer })
+ },
+ candidate ({ commit }, { candidate, senderName }) {
+ commit('CANDIDATE', { target: senderName, candidate: candidate })
},
leave ({ commit }) {
commit('LEAVE')
@@ -40,78 +49,105 @@ const mutations = {
state.name = name
},
CREATE_PEER_CONNECTION (state, target) {
- console.log('CREATED PEER CONNECTION')
- var connection = new RTCPeerConnection(configuration)
- state.connections[target] = connection
+ console.log('[RTC] create peer connection with ' + target)
+ var peer = {
+ name: target,
+ connection: new RTCPeerConnection(configuration),
+ dataChannel: null
+ }
- connection.onicecandidate = function (event) {
+ state.peers.push(peer)
+
+ peer.connection.onicecandidate = function (event) {
if (event.candidate) {
send({
type: 'candidate',
- name: name,
+ name: state.name,
target: target,
- candidate: event.candidate
+ data: event.candidate
})
}
}
- connection.onnegotiationneeded = function () { handleNegotiationNeededEvent(target) }
- connection.onsignalingstatechange = function () { handleSignalingStateChangeEvent(connection) }
- connection.oniceconnectionstatechange = function () { handleICEConnectionStateChangeEvent(connection) }
- connection.onicegatheringstatechange = function () { handleICEGatheringStateChangeEvent(connection) }
+ peer.connection.onnegotiationneeded = function () { handleNegotiationNeededEvent(target) }
+ peer.connection.onsignalingstatechange = function () { handleSignalingStateChangeEvent(peer.connection) }
+ peer.connection.oniceconnectionstatechange = function () { handleICEConnectionStateChangeEvent(peer.connection) }
+ peer.connection.onicegatheringstatechange = function () { handleICEGatheringStateChangeEvent(peer.connection) }
},
- CREATE_DATA_CHANNEL (state) {
+ async OFFER (state, target) {
+ console.log('[RTC] make offer to ' + target)
+ var peer = state.peers.find(peer => peer.name === target)
+ peer.dataChannel = peer.connection.createDataChannel('dataChannel')
+ peer.dataChannel.onopen = handleDataChannelStateChangeEvent
+ peer.dataChannel.onclose = handleDataChannelStateChangeEvent
+
+ var offer = await peer.connection.createOffer()
+ send({
+ type: 'offer',
+ name: state.name,
+ target: target,
+ data: offer
+ })
+ await peer.connection.setLocalDescription(offer)
},
- ANSWER (state, target, offer) {
- var connection = state.connections[target]
+ async ANSWER (state, { target, offer }) {
+ console.log('[RTC] answer to ' + target)
+ var peer = state.peers.find(peer => peer.name === target)
- connection.setRemoteDescription(new RTCSessionDescription(offer))
+ // Permet d'associer le datachannel de la callback au bon peer
+ lastPeer = peer
- var answer = connection.createAnswer()
+ peer.connection.ondatachannel = handleDataChannelCallback
- connection.setLocalDescription(answer)
+ await peer.connection.setRemoteDescription(new RTCSessionDescription(offer))
+
+ var answer = await peer.connection.createAnswer()
+
+ await peer.connection.setLocalDescription(answer)
send({
type: 'answer',
- name: name,
+ name: state.name,
target: target,
- answer: answer
+ data: answer
})
},
- OFFER (state, target, answer) {
- var connection = state.connections[target]
- connection.setRemoteDescription(new RTCSessionDescription(answer))
+ FINALIZE (state, { target, answer }) {
+ var peer = state.peers.find(peer => peer.name === target)
+ peer.connection.setRemoteDescription(new RTCSessionDescription(answer))
},
- CANDIDATE (state, target, candidate) {
- var connection = state.connections[target]
- connection.addIceCandidate(new RTCIceCandidate(candidate))
+ CANDIDATE (state, { target, candidate }) {
+ var peer = state.peers.find(peer => peer.name === target)
+ peer.connection.addIceCandidate(new RTCIceCandidate(candidate))
},
LEAVE (state) {
- state.connections.forEach((connection) => {
- connection.close()
- connection = null
+ state.peers.forEach((peer) => {
+ peer.connection.close()
})
- state.connections = new Map()
+ state.peers = {}
},
BROADCAST (state, message) {
+ state.peers.forEach(peer => {
+ peer.dataChannel.send(message)
+ })
}
}
function handleICEConnectionStateChangeEvent (connection) {
- console.log('ICE CONNECTION CHANGE ' + connection.iceConnectionState)
+ console.log('[RTC] ice connection change to ' + connection.iceConnectionState)
}
function handleICEGatheringStateChangeEvent (connection) {
- console.log('ICE GATHERING CHANGE ' + connection.iceGatheringState)
+ console.log('[RTC] ice gathering change to ' + connection.iceGatheringState)
}
async function handleNegotiationNeededEvent (target) {
- console.log('NEGOTIATION NEEDED FROM ' + target)
+ console.log('[RTC] negotiation needed from ' + target)
}
async function handleSignalingStateChangeEvent (connection) {
- console.log('STATE CHANGED TO : ' + connection.signalingState)
+ console.log('[RTC] state changed to ' + connection.signalingState)
switch (connection.signalingState) {
case 'closed':
await connection.close()
@@ -119,6 +155,33 @@ async function handleSignalingStateChangeEvent (connection) {
}
}
+function handleDataChannelStateChangeEvent () {
+ console.log('[RTC] data channel state change')
+}
+
+function handleDataChannelCallback (event) {
+ console.log('[RTC] data channel callback ' + event)
+ var peer = lastPeer
+ peer.dataChannel = event.channel
+ peer.dataChannel.onmessage = handleDataChannelMessage
+ peer.dataChannel.onopen = handleDataChannelStateChangeEvent
+ peer.dataChannel.onclose = handleDataChannelStateChangeEvent
+
+ store.dispatch('rtc/broadcast', store.state.room.roomStatus)
+}
+
+function handleDataChannelMessage (event) {
+ console.log('[RTC] data channel message ' + event.data)
+ var data = event.data
+ switch (data.type) {
+ case 'status':
+ store.dispatch('room/setRoomStatus', data.message)
+ break
+ case 'vote':
+ break
+ }
+}
+
export default {
namespaced: true,
state,
diff --git a/client/src/store/signalPlugin.js b/client/src/store/signalPlugin.js
index f827bb6..7b1a51b 100644
--- a/client/src/store/signalPlugin.js
+++ b/client/src/store/signalPlugin.js
@@ -4,31 +4,33 @@ const connection = new WebSocket('ws://localhost:8181/socket')
export default function createSignalPlugin () {
return store => {
connection.onopen = function () {
- console.log('WS connected')
+ console.log('[WS] connected')
store.dispatch('app/signalConnected')
}
connection.onerror = function (error) {
- console.log('WS error ' + error)
+ console.log('[WS] error ' + error)
store.dispatch('app/signalError', error)
}
connection.onmessage = function (message) {
- console.log('WS message', message.data)
+ console.log('[WS] message', message.data)
var data = JSON.parse(message.data)
switch (data.type) {
case 'offer':
- store.dispatch('rtc/offer', data.offer, data.name)
+ console.log('offer from ' + data.name)
+ store.dispatch('rtc/offer', { offer: data.data, senderName: data.name })
break
case 'answer':
- store.dispatch('rtc/answer', data.answer, data.name)
+ console.log('answer from ' + data.name)
+ store.dispatch('rtc/answer', { answer: data.data, senderName: data.name })
break
case 'candidate':
- store.dispatch('rtc/candidate', data.candidate, data.name)
+ store.dispatch('rtc/candidate', { candidate: data.data, senderName: data.name })
break
case 'leave':
@@ -47,6 +49,10 @@ export default function createSignalPlugin () {
store.dispatch('app/createRoom', data.message)
break
+ case 'connectRoom':
+ store.dispatch('app/connectRoom', data.message)
+ break
+
case 'error':
store.dispatch('app/error', data.message)
break
@@ -59,6 +65,6 @@ export default function createSignalPlugin () {
}
export function send (message) {
- console.log('WS send', message)
+ console.log('[WS] send', message)
connection.send(JSON.stringify(message))
}
diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue
index de5471b..b5cd71b 100644
--- a/client/src/views/Home.vue
+++ b/client/src/views/Home.vue
@@ -33,6 +33,7 @@ export default {
this.$buefy.dialog.prompt({
message: 'Choose a name',
trapFocus: true,
+ canCancel: false,
inputAttrs: {
placeholder: 'pedro',
minlength: 3,
diff --git a/client/src/views/Room.vue b/client/src/views/Room.vue
index 242cac7..7b5a712 100644
--- a/client/src/views/Room.vue
+++ b/client/src/views/Room.vue
@@ -1,29 +1,51 @@
{{roomStatus.roomName}}
- {{roomStatus.roomCode}}
{{roomStatus.current.title}}
-
-
-
- {{props.row.title}}
-
+
+
+
+
+
+ {{props.row.title}}
+
-
- {{props.row.link}}
-
+
+ {{props.row.link}}
+
-
- {{props.row.vote}}
-
+
+ {{props.row.vote}}
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{props.row.name}}
+
+
+
+
+
+
+
+
+
+
+ {{roomStatus.roomCode}}
+
+
+
@@ -33,7 +55,20 @@ export default {
computed: {
roomStatus () {
return this.$store.state.room.roomStatus
+ },
+ showAdmin () {
+ return this.$store.state.room.admin
+ },
+ usersList () {
+ console.log('USER LIST ' + this.$store.state.rtc.peers)
+ return this.$store.state.rtc.peers
+ },
+ isLoggedIn () {
+ return this.$store.state.app.loginSuccess
}
+ },
+ mounted () {
+ if (!this.isLoggedIn) this.$router.push({ name: 'Home' })
}
}
diff --git a/server/src/main/java/gltronic/oozik/business/IRoomManager.java b/server/src/main/java/gltronic/oozik/business/IRoomManager.java
index c0a1027..9555b75 100644
--- a/server/src/main/java/gltronic/oozik/business/IRoomManager.java
+++ b/server/src/main/java/gltronic/oozik/business/IRoomManager.java
@@ -2,7 +2,7 @@ package gltronic.oozik.business;
import java.io.IOException;
-import org.json.JSONObject;
+import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
public interface IRoomManager {
@@ -10,7 +10,7 @@ public interface IRoomManager {
public void leave(WebSocketSession session);
public void createRoom(WebSocketSession session) throws InterruptedException, IOException;
public void connectRoom(WebSocketSession session, String roomName) throws InterruptedException, IOException;
- public void followRTC(WebSocketSession session, JSONObject jsonObject) throws InterruptedException, IOException;
+ public void followRTC(WebSocketSession session, TextMessage message) throws InterruptedException, IOException;
public void sendMessage(WebSocketSession session, String type, String message) throws InterruptedException, IOException;
public void sendServerInfos(WebSocketSession session) throws InterruptedException, IOException;
}
\ No newline at end of file
diff --git a/server/src/main/java/gltronic/oozik/business/RoomManager.java b/server/src/main/java/gltronic/oozik/business/RoomManager.java
index d6000c3..4ff6bce 100644
--- a/server/src/main/java/gltronic/oozik/business/RoomManager.java
+++ b/server/src/main/java/gltronic/oozik/business/RoomManager.java
@@ -74,14 +74,17 @@ public class RoomManager implements IRoomManager{
return;
}
- String roomAdmin = rooms.getKey(roomName);
- sendMessage(session, "coonectRoom", roomAdmin);
+ String roomAdmin = rooms.get(roomName);
+ sendMessage(session, "connectRoom", roomAdmin);
+
+ String userName = users.getKey(session);
+ System.err.println("[ROOM] Connection to room "+roomName+" ("+roomAdmin+") by "+userName);
}
- public void followRTC(WebSocketSession session, JSONObject jsonObject) throws InterruptedException, IOException {
+ public void followRTC(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
+ String payload = message.getPayload();
+ JSONObject jsonObject = new JSONObject(payload);
String target = (String) jsonObject.get("target");
- String type = (String) jsonObject.get("type");
- String data = (String) jsonObject.get("data");
if (target == null) {
sendMessage(session, "error", "no target");
@@ -96,7 +99,11 @@ public class RoomManager implements IRoomManager{
}
System.err.println("[ROOM] Foward RTC");
- sendMessage(targetSession, type, data);
+ followMessage(targetSession, message);
+ }
+
+ public void followMessage (WebSocketSession session, TextMessage message) throws IOException {
+ session.sendMessage(message);
}
public void sendMessage(WebSocketSession session, String type, String message) throws InterruptedException, IOException {
diff --git a/server/src/main/java/gltronic/oozik/web/SocketHandler.java b/server/src/main/java/gltronic/oozik/web/SocketHandler.java
index a481581..306f95f 100644
--- a/server/src/main/java/gltronic/oozik/web/SocketHandler.java
+++ b/server/src/main/java/gltronic/oozik/web/SocketHandler.java
@@ -43,10 +43,12 @@ public class SocketHandler extends TextWebSocketHandler {
break;
case "leave":
roomManager.leave(session);
+ break;
case "offer":
case "answer":
case "candidate":
- roomManager.followRTC(session, jsonObject);
+ roomManager.followRTC(session, message);
+ break;
default:
roomManager.sendMessage(session, "error", "unknow command");
}