Signaling server, WS, login, room creation
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div id="nav">
|
||||
<router-link to="/">Home</router-link> |
|
||||
<router-link to="/about">About</router-link>
|
||||
</div>
|
||||
<router-view/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,14 +15,5 @@
|
||||
|
||||
#nav {
|
||||
padding: 30px;
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
|
||||
&.router-link-exact-active {
|
||||
color: #42b983;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa" target="_blank" rel="noopener">pwa</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped lang="scss">
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,12 @@ import App from './App.vue'
|
||||
import './registerServiceWorker'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import Buefy from 'buefy'
|
||||
|
||||
import 'buefy/dist/buefy.css'
|
||||
|
||||
// import './assets/style.scss'
|
||||
Vue.use(Buefy)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
|
||||
@@ -11,12 +11,9 @@ const routes = [
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'About',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
||||
path: '/room',
|
||||
name: 'Room',
|
||||
component: () => import('../views/Room.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
205
client/src/rtc.js
Normal file
205
client/src/rtc.js
Normal file
@@ -0,0 +1,205 @@
|
||||
var name
|
||||
var connections = new Map()
|
||||
|
||||
const configuration = {
|
||||
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
||||
}
|
||||
|
||||
const offerOptions = {
|
||||
offerToReceiveAudio: 1,
|
||||
offerToReceiveVideo: 1
|
||||
}
|
||||
|
||||
function handleLogin (success) {
|
||||
if (success === false) {
|
||||
alert('try a different username')
|
||||
} else {
|
||||
loginDiv.style.display = 'none'
|
||||
connectDiv.style.display = 'block'
|
||||
}
|
||||
}
|
||||
|
||||
async function createPeerConnection (target) {
|
||||
console.log('CREATED PEER CONNECTION')
|
||||
var connection = new RTCPeerConnection(configuration)
|
||||
connections[target] = connection
|
||||
|
||||
connection.onicecandidate = function (event) {
|
||||
if (event.candidate) {
|
||||
send({
|
||||
type: 'candidate',
|
||||
name: name,
|
||||
target: target,
|
||||
candidate: event.candidate
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
connection.onnegotiationneeded = function () { handleNegotiationNeededEvent(target) }
|
||||
connection.ontrack = function (event) { handleTrackEvent(event) }
|
||||
connection.onsignalingstatechange = function () { handleSignalingStateChangeEvent(connection) }
|
||||
connection.oniceconnectionstatechange = function () { handleICEConnectionStateChangeEvent(connection) }
|
||||
connection.onicegatheringstatechange = function () { handleICEGatheringStateChangeEvent(connection) }
|
||||
|
||||
// window.setInterval(getConnectionStats, 1000)
|
||||
}
|
||||
|
||||
function handleICEConnectionStateChangeEvent (connection) {
|
||||
console.log('ICE CONNECTION CHANGE ' + connection.iceConnectionState)
|
||||
}
|
||||
|
||||
function handleICEGatheringStateChangeEvent (connection) {
|
||||
console.log('ICE GATHERING CHANGE ' + connection.iceGatheringState)
|
||||
}
|
||||
|
||||
async function makeOffer (target) {
|
||||
createPeerConnection(target)
|
||||
|
||||
var connection = connections[target]
|
||||
|
||||
var offer = await connection.createOffer()
|
||||
send({
|
||||
type: 'offer',
|
||||
name: name,
|
||||
target: target,
|
||||
offer: offer
|
||||
})
|
||||
await connection.setLocalDescription(offer)
|
||||
|
||||
}
|
||||
|
||||
async function handleOffer (offer, target) {
|
||||
console.log('GOT OFFER FROM ' + target)
|
||||
await createPeerConnection(target)
|
||||
|
||||
var connection = connections[target]
|
||||
|
||||
await connection.setRemoteDescription(new RTCSessionDescription(offer))
|
||||
|
||||
if (stream) {
|
||||
console.log('STREAM DETECTED')
|
||||
stream.getTracks().forEach((track) => {
|
||||
console.log('ADDED TRACK')
|
||||
connection.addTrack(track, stream)
|
||||
})
|
||||
}
|
||||
|
||||
var answer = await connection.createAnswer()
|
||||
|
||||
await connection.setLocalDescription(answer)
|
||||
|
||||
send({
|
||||
type: 'answer',
|
||||
name: name,
|
||||
target: target,
|
||||
answer: answer
|
||||
})
|
||||
|
||||
for (let user of connections.keys()) {
|
||||
console.log(user)
|
||||
}
|
||||
|
||||
console.log('CONNECTION SIZE ' + connections.size)
|
||||
|
||||
videoTitle.innerHTML = name + ' | ' + connections.size + ' users connected'
|
||||
}
|
||||
|
||||
async function handleAnswer (answer, target) {
|
||||
console.log('GOT ANSWER FROM ' + target)
|
||||
var connection = connections[target]
|
||||
await connection.setRemoteDescription(new RTCSessionDescription(answer))
|
||||
}
|
||||
|
||||
async function handleCandidate (candidate, target) {
|
||||
console.log('GOT CANDIDATE FROM ' + target)
|
||||
var connection = connections[target]
|
||||
await connection.addIceCandidate(new RTCIceCandidate(candidate))
|
||||
}
|
||||
|
||||
async function handleNegotiationNeededEvent (target) {
|
||||
console.log('NEGOTIATION NEEDED FROM ' + target)
|
||||
|
||||
var connection = connections[target]
|
||||
var offer = await connection.createOffer(offerOptions)
|
||||
|
||||
await connection.setLocalDescription(offer)
|
||||
|
||||
send({
|
||||
type: 'video-offer',
|
||||
name: name,
|
||||
target: target,
|
||||
sdp: connection.localDescription
|
||||
})
|
||||
}
|
||||
|
||||
function handleLeave () {
|
||||
connections.forEach((connection) => {
|
||||
connection.close()
|
||||
connection = null
|
||||
})
|
||||
connections = new Map()
|
||||
|
||||
remoteVideo.pause()
|
||||
remoteVideo.src = null
|
||||
}
|
||||
|
||||
async function handleVideoOffer (sdp, target) {
|
||||
console.log('GOT VIDEO OFFER FROM ' + target)
|
||||
await createPeerConnection(target)
|
||||
var connection = connections[target]
|
||||
await connection.setRemoteDescription(new RTCSessionDescription(sdp))
|
||||
|
||||
if (stream) {
|
||||
console.log('STREAM DETECTED')
|
||||
stream.getTracks().forEach((track) => {
|
||||
console.log('ADDED TRACK')
|
||||
connection.addTrack(track, stream)
|
||||
})
|
||||
}
|
||||
|
||||
var answer = await connection.createAnswer()
|
||||
await connection.setLocalDescription(answer)
|
||||
|
||||
send({
|
||||
type: 'video-answer',
|
||||
name: name,
|
||||
target: target,
|
||||
sdp: answer
|
||||
})
|
||||
|
||||
// var keys = connections.keys().next().value
|
||||
videoTitle.innerHTML = name + ' | connected to ' + target
|
||||
}
|
||||
|
||||
async function handleVideoAnswer(sdp, target) {
|
||||
console.log('GOT VIDEO ANSWER FROM ' + target)
|
||||
|
||||
var connection = connections[target]
|
||||
await connection.setRemoteDescription(new RTCSessionDescription(sdp))
|
||||
}
|
||||
|
||||
async function handleSignalingStateChangeEvent (connection) {
|
||||
console.log('STATE CHANGED TO : ' + connection.signalingState)
|
||||
switch(connection.signalingState) {
|
||||
case 'closed':
|
||||
await connection.close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
function handleTrackEvent (event) {
|
||||
console.log('GOT TRACK')
|
||||
remoteVideo.srcObject = event.streams[0]
|
||||
videoDiv.style.display = 'block'
|
||||
loadDiv.style.display = 'none'
|
||||
}
|
||||
|
||||
function getConnectionStats () {
|
||||
connections.forEach((connection, target) => {
|
||||
console.log('[' + target + '] ' + connection.connectionState)
|
||||
})
|
||||
}
|
||||
|
||||
export const rtc = {
|
||||
send
|
||||
}
|
||||
92
client/src/script.js
Normal file
92
client/src/script.js
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
const loginDiv = document.querySelector('#loginDiv')
|
||||
const loginInput = document.querySelector('#loginInput')
|
||||
const loginBt = document.querySelector('#loginBt')
|
||||
|
||||
const connectDiv = document.querySelector('#connectDiv')
|
||||
const callInput = document.querySelector('#callInput')
|
||||
const callDatalist = document.querySelector('#callDatalist')
|
||||
const callBt = document.querySelector('#callBt')
|
||||
const videoInput = document.querySelector('#videoInput')
|
||||
|
||||
const videoDiv = document.querySelector('#videoDiv')
|
||||
const videoTitle = document.querySelector('#videoTitle')
|
||||
const remoteVideo = document.querySelector('#video')
|
||||
const disconnectBt = document.querySelector('#disconnectBt')
|
||||
|
||||
const loadDiv = document.querySelector('#loadDiv')
|
||||
const titleDiv = document.querySelector('#titleDiv')
|
||||
|
||||
var stream
|
||||
var users
|
||||
|
||||
loginDiv.style.display = 'block'
|
||||
|
||||
loginBt.addEventListener('click', function (event) {
|
||||
name = loginInput.value
|
||||
|
||||
if (name.length > 0) {
|
||||
send({
|
||||
type: 'login',
|
||||
name: name
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
callBt.addEventListener('click', function () {
|
||||
var callToUsername = callInput.value;
|
||||
|
||||
if (callToUsername.length > 0) {
|
||||
makeOffer(callToUsername)
|
||||
loadDiv.style.display = 'block'
|
||||
titleDiv.style.display = 'none'
|
||||
connectDiv.style.display = 'none'
|
||||
}
|
||||
})
|
||||
|
||||
disconnectBt.addEventListener('click', function () {
|
||||
send({
|
||||
type: 'leave',
|
||||
name: name
|
||||
})
|
||||
handleLeave()
|
||||
videoDiv.style.display = 'none'
|
||||
loginDiv.style.display = 'block'
|
||||
titleDiv.style.display = 'block'
|
||||
})
|
||||
|
||||
videoInput.addEventListener('change', function (event) {
|
||||
remoteVideo.src = URL.createObjectURL(this.files[0])
|
||||
|
||||
videoDiv.style.display = 'block'
|
||||
connectDiv.style.display = 'none'
|
||||
titleDiv.style.display = 'none'
|
||||
videoTitle.innerHTML = name + ' | ' + connections.size + ' users connected'
|
||||
})
|
||||
|
||||
remoteVideo.onplay = function () {
|
||||
console.log('ADD STREAM')
|
||||
if (remoteVideo.mozCaptureStream()) stream = remoteVideo.mozCaptureStream()
|
||||
else stream = remoteVideo.captureStream()
|
||||
}
|
||||
|
||||
function handleUserlist(list) {
|
||||
console.log('GOT USER LIST '+list)
|
||||
users = Array.from(list)
|
||||
console.log(' users '+users)
|
||||
console.log(typeof users)
|
||||
console.log(' users '+users)
|
||||
callDatalist.innerHTML = ''
|
||||
users.forEach(user => {
|
||||
if(user != name) callDatalist.innerHTML += '<option value="'+user+'"/>'
|
||||
})
|
||||
}
|
||||
|
||||
function handleError(message) {
|
||||
console.log('Error '+message)
|
||||
alert(
|
||||
'AWWW FUCK \n\n'
|
||||
+message
|
||||
+'\n\nYou should probably reload the page')
|
||||
}
|
||||
*/
|
||||
66
client/src/signal.js
Normal file
66
client/src/signal.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// const conn = new WebSocket('wss://oozik.gltronic.ovh/')
|
||||
const conn = new WebSocket('wss://localhost:8080')
|
||||
|
||||
conn.onopen = function () {
|
||||
console.log('Connected to the signaling server')
|
||||
}
|
||||
|
||||
conn.onmessage = function (msg) {
|
||||
console.log('Got message', msg.data)
|
||||
|
||||
var data = JSON.parse(msg.data)
|
||||
|
||||
switch (data.type) {
|
||||
case 'login':
|
||||
handleLogin(data.success)
|
||||
break
|
||||
|
||||
case 'offer':
|
||||
handleOffer(data.offer, data.name)
|
||||
break
|
||||
|
||||
case 'answer':
|
||||
handleAnswer(data.answer, data.name)
|
||||
break
|
||||
|
||||
case 'candidate':
|
||||
handleCandidate(data.candidate, data.name)
|
||||
break
|
||||
|
||||
case 'userlist':
|
||||
handleUserlist(data.users)
|
||||
break
|
||||
|
||||
case 'leave':
|
||||
handleLeave()
|
||||
break
|
||||
|
||||
case 'video-offer':
|
||||
handleVideoOffer(data.sdp, data.name)
|
||||
break
|
||||
|
||||
case 'video-answer':
|
||||
handleVideoAnswer(data.sdp, data.name)
|
||||
break
|
||||
|
||||
case 'error':
|
||||
handleError(data.message)
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
conn.onerror = function (err) {
|
||||
console.log('Got error', err)
|
||||
}
|
||||
|
||||
function send (message) {
|
||||
console.log('Sended message', message)
|
||||
conn.send(JSON.stringify(message))
|
||||
}
|
||||
|
||||
export const signal = {
|
||||
send
|
||||
}
|
||||
75
client/src/store/appModule.js
Normal file
75
client/src/store/appModule.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import { DialogProgrammatic as Dialog } from 'buefy'
|
||||
import router from '@/router/index'
|
||||
|
||||
const state = {
|
||||
signalServerConnected: false,
|
||||
loginSuccess: false,
|
||||
error: null,
|
||||
serverStatus: {}
|
||||
}
|
||||
|
||||
const getters = {
|
||||
displayError: state => state.error,
|
||||
displayUserList: state => state.userList,
|
||||
displayLoginStatus: state => state.loginSuccess,
|
||||
displayServerStatus: state => state.signalServerConnected
|
||||
}
|
||||
|
||||
const actions = {
|
||||
signalConnected ({ commit }) {
|
||||
commit('SIGNAL_SUCCESS')
|
||||
},
|
||||
signalError ({ commit }, error) {
|
||||
commit('SIGNAL_ERROR', error)
|
||||
},
|
||||
login ({ commit }, success) {
|
||||
if (success) commit('LOGIN_SUCCESS')
|
||||
else commit('LOGIN_ERROR')
|
||||
},
|
||||
serverStatus ({ commit }, serverStatus) {
|
||||
commit('SET_SERVERSTATUS', serverStatus)
|
||||
},
|
||||
createRoom ({ commit, dispatch }, code) {
|
||||
commit('CREATE_ROOM')
|
||||
dispatch('room/setRoomCode', code, { root: true })
|
||||
},
|
||||
error ({ commit }, error) {
|
||||
commit('ERROR', error)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SIGNAL_SUCCESS (state) {
|
||||
state.signalServerConnected = true
|
||||
},
|
||||
SIGNAL_ERROR (state, error) {
|
||||
state.signalServerConnected = false
|
||||
state.error = error
|
||||
},
|
||||
LOGIN_SUCCESS (state) {
|
||||
state.loginSuccess = true
|
||||
},
|
||||
LOGIN_ERROR (state) {
|
||||
state.loginSuccess = false
|
||||
},
|
||||
SET_SERVERSTATUS (state, serverStatus) {
|
||||
state.serverStatus = serverStatus
|
||||
},
|
||||
CREATE_ROOM (state) {
|
||||
state.room = true
|
||||
state.admin = true
|
||||
router.push({ name: 'Room' })
|
||||
},
|
||||
ERROR (state, error) {
|
||||
state.error = error
|
||||
Dialog.alert(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
@@ -1,15 +1,19 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import signal from './signalPlugin'
|
||||
import rtc from './rtcModule'
|
||||
import room from './roomModule'
|
||||
import app from './appModule'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
},
|
||||
mutations: {
|
||||
},
|
||||
actions: {
|
||||
},
|
||||
modules: {
|
||||
}
|
||||
rtc,
|
||||
app,
|
||||
room
|
||||
},
|
||||
plugins: [
|
||||
signal()
|
||||
]
|
||||
})
|
||||
|
||||
46
client/src/store/roomModule.js
Normal file
46
client/src/store/roomModule.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const state = {
|
||||
admin: false,
|
||||
roomStatus: {
|
||||
roomName: '',
|
||||
roomCode: '',
|
||||
current: '',
|
||||
playlist: []
|
||||
}
|
||||
}
|
||||
|
||||
const getters = {
|
||||
displayRoomCode: state => state.roomCode,
|
||||
displayRoomName: state => state.roomName
|
||||
}
|
||||
|
||||
const actions = {
|
||||
setRoomCode ({ commit }, roomCode) {
|
||||
commit('SET_ROOMCODE', roomCode)
|
||||
},
|
||||
setRoomName ({ commit }, roomName) {
|
||||
commit('SET_ROOMNAME', roomName)
|
||||
},
|
||||
setRoomStatus ({ commit }, roomStatus) {
|
||||
commit('SET_ROOMSTATUS', roomStatus)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_ROOMCODE (state, code) {
|
||||
state.roomStatus.roomCode = code
|
||||
},
|
||||
SET_ROOMNAME (state, name) {
|
||||
state.roomStatus.roomName = name
|
||||
},
|
||||
SET_ROOMSTATUS (state, roomStatus) {
|
||||
state.roomStatus = roomStatus
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
128
client/src/store/rtcModule.js
Normal file
128
client/src/store/rtcModule.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import { send } from './signalPlugin'
|
||||
|
||||
const configuration = {
|
||||
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
|
||||
}
|
||||
|
||||
const state = {
|
||||
name: null,
|
||||
connections: new Map()
|
||||
}
|
||||
|
||||
const getters = {
|
||||
displayName: state => state.name
|
||||
}
|
||||
|
||||
const actions = {
|
||||
setName ({ commit }, name) {
|
||||
commit('SET_NAME', name)
|
||||
},
|
||||
async offer ({ commit }, offer, name) {
|
||||
commit('CREATE_PEER_CONNECTION', name)
|
||||
commit('ANSWER', name, offer)
|
||||
},
|
||||
answer ({ commit }, answer, name) {
|
||||
commit('OFFER', name, answer)
|
||||
},
|
||||
candidate ({ commit }, candidate, name) {
|
||||
commit('ANSWER', name, candidate)
|
||||
},
|
||||
leave ({ commit }) {
|
||||
commit('LEAVE')
|
||||
},
|
||||
broadcast ({ commit }, message) {
|
||||
commit('BROADCAST', message)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_NAME (state, name) {
|
||||
state.name = name
|
||||
},
|
||||
CREATE_PEER_CONNECTION (state, target) {
|
||||
console.log('CREATED PEER CONNECTION')
|
||||
var connection = new RTCPeerConnection(configuration)
|
||||
state.connections[target] = connection
|
||||
|
||||
connection.onicecandidate = function (event) {
|
||||
if (event.candidate) {
|
||||
send({
|
||||
type: 'candidate',
|
||||
name: name,
|
||||
target: target,
|
||||
candidate: event.candidate
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
connection.onnegotiationneeded = function () { handleNegotiationNeededEvent(target) }
|
||||
connection.onsignalingstatechange = function () { handleSignalingStateChangeEvent(connection) }
|
||||
connection.oniceconnectionstatechange = function () { handleICEConnectionStateChangeEvent(connection) }
|
||||
connection.onicegatheringstatechange = function () { handleICEGatheringStateChangeEvent(connection) }
|
||||
},
|
||||
CREATE_DATA_CHANNEL (state) {
|
||||
|
||||
},
|
||||
ANSWER (state, target, offer) {
|
||||
var connection = state.connections[target]
|
||||
|
||||
connection.setRemoteDescription(new RTCSessionDescription(offer))
|
||||
|
||||
var answer = connection.createAnswer()
|
||||
|
||||
connection.setLocalDescription(answer)
|
||||
|
||||
send({
|
||||
type: 'answer',
|
||||
name: name,
|
||||
target: target,
|
||||
answer: answer
|
||||
})
|
||||
},
|
||||
OFFER (state, target, answer) {
|
||||
var connection = state.connections[target]
|
||||
connection.setRemoteDescription(new RTCSessionDescription(answer))
|
||||
},
|
||||
CANDIDATE (state, target, candidate) {
|
||||
var connection = state.connections[target]
|
||||
connection.addIceCandidate(new RTCIceCandidate(candidate))
|
||||
},
|
||||
LEAVE (state) {
|
||||
state.connections.forEach((connection) => {
|
||||
connection.close()
|
||||
connection = null
|
||||
})
|
||||
state.connections = new Map()
|
||||
},
|
||||
BROADCAST (state, message) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleICEConnectionStateChangeEvent (connection) {
|
||||
console.log('ICE CONNECTION CHANGE ' + connection.iceConnectionState)
|
||||
}
|
||||
|
||||
function handleICEGatheringStateChangeEvent (connection) {
|
||||
console.log('ICE GATHERING CHANGE ' + connection.iceGatheringState)
|
||||
}
|
||||
|
||||
async function handleNegotiationNeededEvent (target) {
|
||||
console.log('NEGOTIATION NEEDED FROM ' + target)
|
||||
}
|
||||
|
||||
async function handleSignalingStateChangeEvent (connection) {
|
||||
console.log('STATE CHANGED TO : ' + connection.signalingState)
|
||||
switch (connection.signalingState) {
|
||||
case 'closed':
|
||||
await connection.close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
64
client/src/store/signalPlugin.js
Normal file
64
client/src/store/signalPlugin.js
Normal file
@@ -0,0 +1,64 @@
|
||||
const connection = new WebSocket('ws://localhost:8181/socket')
|
||||
// const connection = new WebSocket('wss://echo.websocket.org')
|
||||
|
||||
export default function createSignalPlugin () {
|
||||
return store => {
|
||||
connection.onopen = function () {
|
||||
console.log('WS connected')
|
||||
store.dispatch('app/signalConnected')
|
||||
}
|
||||
|
||||
connection.onerror = function (error) {
|
||||
console.log('WS error ' + error)
|
||||
store.dispatch('app/signalError', error)
|
||||
}
|
||||
|
||||
connection.onmessage = function (message) {
|
||||
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)
|
||||
break
|
||||
|
||||
case 'answer':
|
||||
store.dispatch('rtc/answer', data.answer, data.name)
|
||||
break
|
||||
|
||||
case 'candidate':
|
||||
store.dispatch('rtc/candidate', data.candidate, data.name)
|
||||
break
|
||||
|
||||
case 'leave':
|
||||
store.dispatch('rtc/leave')
|
||||
break
|
||||
|
||||
case 'login':
|
||||
store.dispatch('app/login', data.message)
|
||||
break
|
||||
|
||||
case 'serverInfos':
|
||||
store.dispatch('app/serverStatus', data)
|
||||
break
|
||||
|
||||
case 'createRoom':
|
||||
store.dispatch('app/createRoom', data.message)
|
||||
break
|
||||
|
||||
case 'error':
|
||||
store.dispatch('app/error', data.message)
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function send (message) {
|
||||
console.log('WS send', message)
|
||||
connection.send(JSON.stringify(message))
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,18 +1,103 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img alt="Vue logo" src="../assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
||||
<div v-if="serverConnected">
|
||||
<h1 class="title has-text-success">Server online</h1>
|
||||
<h1 class="title">{{serverStatus.userCount}} users | {{serverStatus.roomCount}} rooms</h1>
|
||||
</div>
|
||||
<h1 v-else class="title has-text-danger">Server offline</h1>
|
||||
<hr>
|
||||
<b-button type="is-primary" @click="connectToRoomPrompt">Join a room</b-button>
|
||||
<hr>
|
||||
<b-button type="is-primary" @click="makeRoomPrompt">Make a room</b-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import HelloWorld from '@/components/HelloWorld.vue'
|
||||
import { send } from '../store/signalPlugin'
|
||||
|
||||
export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
HelloWorld
|
||||
computed: {
|
||||
serverStatus () {
|
||||
return this.$store.state.app.serverStatus
|
||||
},
|
||||
serverConnected () {
|
||||
return this.$store.state.app.signalServerConnected
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.loginPrompt()
|
||||
},
|
||||
methods: {
|
||||
loginPrompt () {
|
||||
this.$buefy.dialog.prompt({
|
||||
message: 'Choose a name',
|
||||
trapFocus: true,
|
||||
inputAttrs: {
|
||||
placeholder: 'pedro',
|
||||
minlength: 3,
|
||||
maxlength: 30
|
||||
},
|
||||
confirmText: 'KK',
|
||||
onConfirm: (name) => this.login(name)
|
||||
})
|
||||
},
|
||||
login (name) {
|
||||
send({
|
||||
type: 'login',
|
||||
name: name
|
||||
})
|
||||
this.$store.dispatch('rtc/setName', name)
|
||||
},
|
||||
makeRoomPrompt () {
|
||||
this.$buefy.dialog.prompt({
|
||||
message: 'Choose a room name',
|
||||
trapFocus: true,
|
||||
inputAttrs: {
|
||||
placeholder: 'mah roomy',
|
||||
minlength: 3,
|
||||
maxlength: 30
|
||||
},
|
||||
cancelText: 'Nah',
|
||||
confirmText: 'Go',
|
||||
onConfirm: (name) => this.makeRoom(name)
|
||||
})
|
||||
},
|
||||
makeRoom (name) {
|
||||
send({
|
||||
type: 'createRoom',
|
||||
name: name
|
||||
})
|
||||
this.$store.dispatch('room/setRoomName', name)
|
||||
},
|
||||
connectToRoomPrompt () {
|
||||
this.$buefy.dialog.prompt({
|
||||
message: 'Enter room code',
|
||||
trapFocus: true,
|
||||
inputAttrs: {
|
||||
placeholder: 'mah roomy',
|
||||
minlength: 3,
|
||||
maxlength: 30
|
||||
},
|
||||
cancelText: 'Nah',
|
||||
confirmText: 'Connect',
|
||||
onConfirm: (code) => this.connectToRoom(code)
|
||||
})
|
||||
},
|
||||
connectToRoom (code) {
|
||||
send({
|
||||
type: 'connectRoom',
|
||||
name: code
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.home {
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin: 20px 15px 0px 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
43
client/src/views/Room.vue
Normal file
43
client/src/views/Room.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="room container">
|
||||
<h1 class="title is-1">{{roomStatus.roomName}}</h1>
|
||||
<h1 class="subtitle is-4">{{roomStatus.roomCode}}</h1>
|
||||
<h2 class="subtitle">{{roomStatus.current.title}}</h2>
|
||||
|
||||
<b-table :data="roomStatus.playlist" striped hoverable default-sort="vote">
|
||||
<template slot-scope="props">
|
||||
<b-table-column field="title">
|
||||
{{props.row.title}}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="link">
|
||||
{{props.row.link}}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column field="vote">
|
||||
{{props.row.vote}}
|
||||
</b-table-column>
|
||||
|
||||
<b-table-column>
|
||||
<b-button icon-left="arrow-up-bold-outline" type="is-dark"/>
|
||||
<b-button icon-left="arrow-down-bold-outline" type="is-dark"/>
|
||||
</b-table-column>
|
||||
</template>
|
||||
</b-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Room',
|
||||
computed: {
|
||||
roomStatus () {
|
||||
return this.$store.state.room.roomStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user