messages class as event emitter.

This commit is contained in:
Miroslav Šedivý 2020-11-07 18:47:02 +01:00
parent f90c506928
commit 70025fd2d7
4 changed files with 122 additions and 158 deletions

View File

@ -10,9 +10,9 @@
:webrtc="webrtc" :webrtc="webrtc"
:screenWidth="state.screen_size.width" :screenWidth="state.screen_size.width"
:screenHeight="state.screen_size.height" :screenHeight="state.screen_size.height"
:scrollSensitivity="5"
:scrollInvert="true"
:isControling="state.is_controlling" :isControling="state.is_controlling"
:scrollSensitivity="state.scroll.sensitivity"
:scrollInvert="state.scroll.invert"
/> />
</div> </div>
</div> </div>
@ -72,11 +72,10 @@
@Ref('container') readonly _container!: HTMLElement @Ref('container') readonly _container!: HTMLElement
@Ref('video') public readonly video!: HTMLVideoElement @Ref('video') public readonly video!: HTMLVideoElement
public events = new EventEmitter()
private observer = new ResizeObserver(this.onResize.bind(this))
private websocket = new NekoWebSocket() private websocket = new NekoWebSocket()
private webrtc = new NekoWebRTC() private webrtc = new NekoWebRTC()
private messages = new NekoMessages(this.events) private observer = new ResizeObserver(this.onResize.bind(this))
public events = new NekoMessages(this.websocket)
private state = { private state = {
id: null, id: null,
@ -86,6 +85,10 @@
height: 720, height: 720,
rate: 30, rate: 30,
}, },
scroll: {
sensitivity: 10,
invert: true,
},
is_controlling: false, is_controlling: false,
websocket: 'disconnected', websocket: 'disconnected',
webrtc: 'disconnected', webrtc: 'disconnected',
@ -100,6 +103,12 @@
}, },
} }
public screen = {
size: (width: number, height: number, rate: number) => {
this.websocket.send('screen/set', { width, height, rate })
},
}
public connect(url: string, password: string) { public connect(url: string, password: string) {
if (this.websocket.connected) { if (this.websocket.connected) {
throw new Error('client already connected') throw new Error('client already connected')
@ -120,6 +129,10 @@
// Update canvas on resize // Update canvas on resize
this.observer.observe(this._component) this.observer.observe(this._component)
this.events.on('control.host', (id: string | null) => {
Vue.set(this.state, 'is_controlling', id != null && id === this.state.id)
})
// WebSocket // WebSocket
this.websocket.on('message', async (event: string, payload: any) => { this.websocket.on('message', async (event: string, payload: any) => {
switch (event) { switch (event) {
@ -135,43 +148,26 @@
Vue.set(this.state, 'screen_size', payload) Vue.set(this.state, 'screen_size', payload)
this.onResize() this.onResize()
break break
case 'control/release':
if (payload.id === this.state.id) {
Vue.set(this.state, 'is_controlling', false)
}
break
case 'control/locked':
if (payload.id === this.state.id) {
Vue.set(this.state, 'is_controlling', true)
}
break
default:
// @ts-ignore
if (typeof this.messages[event] === 'function') {
// @ts-ignore
this.messages[event](payload)
} else {
console.log(`unhandled websocket event '${event}':`, payload)
}
} }
}) })
this.websocket.on('connecting', () => { this.websocket.on('connecting', () => {
Vue.set(this.state, 'websocket', 'connecting') Vue.set(this.state, 'websocket', 'connecting')
this.events.emit('system.websocket', 'connecting')
}) })
this.websocket.on('connected', () => { this.websocket.on('connected', () => {
Vue.set(this.state, 'websocket', 'connected') Vue.set(this.state, 'websocket', 'connected')
this.events.emit('system.websocket', 'connected')
}) })
this.websocket.on('disconnected', () => { this.websocket.on('disconnected', () => {
Vue.set(this.state, 'websocket', 'disconnected') Vue.set(this.state, 'websocket', 'disconnected')
this.events.emit('system.websocket', 'disconnected')
this.webrtc.disconnect() this.webrtc.disconnect()
}) })
// WebRTC // WebRTC
this.webrtc.on('track', (event: RTCTrackEvent) => { this.webrtc.on('track', (event: RTCTrackEvent) => {
const { track, streams } = event const { track, streams } = event
if (track.kind === 'audio') { if (track.kind === 'audio') return
return
}
// Create stream // Create stream
if ('srcObject' in this.video) { if ('srcObject' in this.video) {
@ -185,13 +181,15 @@
}) })
this.webrtc.on('connecting', () => { this.webrtc.on('connecting', () => {
Vue.set(this.state, 'webrtc', 'connecting') Vue.set(this.state, 'webrtc', 'connecting')
this.events.emit('system.webrtc', 'connecting')
}) })
this.webrtc.on('connected', () => { this.webrtc.on('connected', () => {
Vue.set(this.state, 'webrtc', 'connected') Vue.set(this.state, 'webrtc', 'connected')
this.events.emit('connect') this.events.emit('system.webrtc', 'connected')
}) })
this.webrtc.on('disconnected', () => { this.webrtc.on('disconnected', () => {
Vue.set(this.state, 'webrtc', 'disconnected') Vue.set(this.state, 'webrtc', 'disconnected')
this.events.emit('system.webrtc', 'disconnected')
}) })
} }

View File

@ -1,9 +1,7 @@
import Vue from 'vue' import { Member, ScreenConfigurations } from '../types/structs'
import { Member } from '../types/structs'
import { EVENT } from '../types/events' import { EVENT } from '../types/events'
import { import {
DisconnectPayload, DisconnectPayload,
SignalProvidePayload,
MemberListPayload, MemberListPayload,
MemberDisconnectPayload, MemberDisconnectPayload,
MemberPayload, MemberPayload,
@ -18,175 +16,142 @@ import {
} from '../types/messages' } from '../types/messages'
import EventEmitter from 'eventemitter3' import EventEmitter from 'eventemitter3'
import { NekoWebSocket } from './websocket'
export class NekoMessages { export interface NekoEvents {
_eventEmmiter: EventEmitter ['system.websocket']: (state: 'connected' | 'connecting' | 'disconnected') => void
['system.webrtc']: (state: 'connected' | 'connecting' | 'disconnected') => void
constructor(eventEmitter: EventEmitter) { ['system.connect']: () => void
this._eventEmmiter = eventEmitter ['system.disconnect']: (message: string) => void
['control.host']: (id: string | null) => void
['member.list']: (members: Member[]) => void
['member.connected']: (id: string) => void
['member.disconnected']: (id: string) => void
['control.request']: (id: string) => void
['control.requesting']: (id: string) => void
['clipboard.update']: (text: string) => void
['screen.configuration']: (configurations: ScreenConfigurations) => void
['screen.size']: (width: number, height: number, rate: number) => void
['broadcast.status']: (url: string, isActive: boolean) => void
['member.ban']: (id: string, target: string) => void
['member.kick']: (id: string, target: string) => void
['member.muted']: (id: string, target: string) => void
['member.unmuted']: (id: string, target: string) => void
['room.locked']: (id: string) => void
['room.unlocked']: (id: string) => void
} }
private emit(event: string, ...payload: any) { export class NekoMessages extends EventEmitter<NekoEvents> {
this._eventEmmiter.emit(event, ...payload) constructor(websocket: NekoWebSocket) {
super()
websocket.on('message', async (event: string, payload: any) => {
// @ts-ignore
if (typeof this[event] === 'function') {
// @ts-ignore
this[event](payload)
} else {
console.log(`unhandled websocket event '${event}':`, payload)
}
})
} }
///////////////////////////// /////////////////////////////
// System Events // System Events
///////////////////////////// /////////////////////////////
public [EVENT.SYSTEM.DISCONNECT]({ message }: DisconnectPayload) { protected [EVENT.SYSTEM.DISCONNECT]({ message }: DisconnectPayload) {
console.log('EVENT.SYSTEM.DISCONNECT') console.log('EVENT.SYSTEM.DISCONNECT')
this.emit('disconnect', message) this.emit('system.disconnect', message)
//this.onDisconnected(new Error(message))
//this.$vue.$swal({
// title: this.$vue.$t('connection.disconnected'),
// text: message,
// icon: 'error',
// confirmButtonText: this.$vue.$t('connection.button_confirm') as string,
//})
} }
///////////////////////////// /////////////////////////////
// Member Events // Member Events
///////////////////////////// /////////////////////////////
public [EVENT.MEMBER.LIST]({ members }: MemberListPayload) { protected [EVENT.MEMBER.LIST]({ members }: MemberListPayload) {
console.log('EVENT.MEMBER.LIST') console.log('EVENT.MEMBER.LIST')
this.emit('member.list', members) this.emit('member.list', members)
//this.$accessor.user.setMembers(members) //user.setMembers(members)
} }
public [EVENT.MEMBER.CONNECTED](member: MemberPayload) { protected [EVENT.MEMBER.CONNECTED](member: MemberPayload) {
console.log('EVENT.MEMBER.CONNECTED') console.log('EVENT.MEMBER.CONNECTED')
this.emit('member.connected', member.id) this.emit('member.connected', member.id)
//this.$accessor.user.addMember(member) //user.addMember(member)
} }
public [EVENT.MEMBER.DISCONNECTED]({ id }: MemberDisconnectPayload) { protected [EVENT.MEMBER.DISCONNECTED]({ id }: MemberDisconnectPayload) {
console.log('EVENT.MEMBER.DISCONNECTED') console.log('EVENT.MEMBER.DISCONNECTED')
this.emit('member.disconnected', id) this.emit('member.disconnected', id)
//this.$accessor.user.delMember(id) //user.delMember(id)
} }
///////////////////////////// /////////////////////////////
// Control Events // Control Events
///////////////////////////// /////////////////////////////
public [EVENT.CONTROL.LOCKED]({ id }: ControlPayload) { protected [EVENT.CONTROL.LOCKED]({ id }: ControlPayload) {
console.log('EVENT.CONTROL.LOCKED') console.log('EVENT.CONTROL.LOCKED')
this.emit('host.change', id) this.emit('control.host', id)
//this.$accessor.remote.setHost(id) //remote.setHost(id)
//this.$accessor.remote.changeKeyboard() //remote.changeKeyboard()
//
//const member = this.member(id)
//if (!member) {
// return
//}
//
//if (this.id === id) {
// this.$vue.$notify({
// group: 'neko',
// type: 'info',
// title: this.$vue.$t('notifications.controls_taken', { name: this.$vue.$t('you') }) as string,
// duration: 5000,
// speed: 1000,
// })
//}
} }
public [EVENT.CONTROL.RELEASE]({ id }: ControlPayload) { protected [EVENT.CONTROL.RELEASE]({ id }: ControlPayload) {
console.log('EVENT.CONTROL.RELEASE') console.log('EVENT.CONTROL.RELEASE')
this.emit('host.change', null) this.emit('control.host', null)
//this.$accessor.remote.reset() //remote.reset()
//const member = this.member(id)
//if (!member) {
// return
//}
//
//if (this.id === id) {
// this.$vue.$notify({
// group: 'neko',
// type: 'info',
// title: this.$vue.$t('notifications.controls_released', { name: this.$vue.$t('you') }) as string,
// duration: 5000,
// speed: 1000,
// })
//}
} }
public [EVENT.CONTROL.REQUEST]({ id }: ControlPayload) { protected [EVENT.CONTROL.REQUEST]({ id }: ControlPayload) {
console.log('EVENT.CONTROL.REQUEST') console.log('EVENT.CONTROL.REQUEST')
this.emit('control.request', id) this.emit('control.request', id)
//const member = this.member(id)
//if (!member) {
// return
//}
//
//this.$vue.$notify({
// group: 'neko',
// type: 'info',
// title: this.$vue.$t('notifications.controls_has', { name: member.displayname }) as string,
// text: this.$vue.$t('notifications.controls_has_alt') as string,
// duration: 5000,
// speed: 1000,
//})
} }
public [EVENT.CONTROL.REQUESTING]({ id }: ControlPayload) { protected [EVENT.CONTROL.REQUESTING]({ id }: ControlPayload) {
console.log('EVENT.CONTROL.REQUESTING') console.log('EVENT.CONTROL.REQUESTING')
this.emit('control.requesting', id) this.emit('control.requesting', id)
//const member = this.member(id)
//if (!member || member.ignored) {
// return
//}
//
//this.$vue.$notify({
// group: 'neko',
// type: 'info',
// title: this.$vue.$t('notifications.controls_requesting', { name: member.displayname }) as string,
// duration: 5000,
// speed: 1000,
//})
} }
public [EVENT.CONTROL.GIVE]({ id, target }: ControlTargetPayload) { protected [EVENT.CONTROL.GIVE]({ id, target }: ControlTargetPayload) {
console.log('EVENT.CONTROL.GIVE') console.log('EVENT.CONTROL.GIVE')
this.emit('host.change', target) this.emit('control.host', target)
//this.$accessor.remote.setHost(target) //remote.setHost(target)
//this.$accessor.remote.changeKeyboard() //remote.changeKeyboard()
} }
public [EVENT.CONTROL.CLIPBOARD]({ text }: ControlClipboardPayload) { protected [EVENT.CONTROL.CLIPBOARD]({ text }: ControlClipboardPayload) {
console.log('EVENT.CONTROL.CLIPBOARD') console.log('EVENT.CONTROL.CLIPBOARD')
this.emit('clipboard.update', text) this.emit('clipboard.update', text)
//this.$accessor.remote.setClipboard(text) //remote.setClipboard(text)
} }
///////////////////////////// /////////////////////////////
// Screen Events // Screen Events
///////////////////////////// /////////////////////////////
public [EVENT.SCREEN.CONFIGURATIONS]({ configurations }: ScreenConfigurationsPayload) { protected [EVENT.SCREEN.CONFIGURATIONS]({ configurations }: ScreenConfigurationsPayload) {
console.log('EVENT.SCREEN.CONFIGURATIONS') console.log('EVENT.SCREEN.CONFIGURATIONS')
this.emit('screen.configuration', configurations) this.emit('screen.configuration', configurations)
//this.$accessor.video.setConfigurations(configurations) //video.setConfigurations(configurations)
} }
public [EVENT.SCREEN.RESOLUTION]({ id, width, height, rate }: ScreenResolutionPayload) { protected [EVENT.SCREEN.RESOLUTION]({ id, width, height, rate }: ScreenResolutionPayload) {
console.log('EVENT.SCREEN.RESOLUTION') console.log('EVENT.SCREEN.RESOLUTION')
this.emit('screen.size', width, height, rate) this.emit('screen.size', width, height, rate)
//this.$accessor.video.setResolution({ width, height, rate }) //video.setResolution({ width, height, rate })
} }
///////////////////////////// /////////////////////////////
// Broadcast Events // Broadcast Events
///////////////////////////// /////////////////////////////
public [EVENT.BROADCAST.STATUS](payload: BroadcastStatusPayload) { protected [EVENT.BROADCAST.STATUS](payload: BroadcastStatusPayload) {
console.log('EVENT.BROADCAST.STATUS') console.log('EVENT.BROADCAST.STATUS')
this.emit('broadcast.status', payload) this.emit('broadcast.status', payload.url, payload.isActive)
//this.$accessor.settings.broadcastStatus(payload) //settings.broadcastStatus(payload)
} }
///////////////////////////// /////////////////////////////
// Admin Events // Admin Events
///////////////////////////// /////////////////////////////
public [EVENT.ADMIN.BAN]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.BAN]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.BAN') console.log('EVENT.ADMIN.BAN')
@ -194,7 +159,7 @@ export class NekoMessages {
// TODO // TODO
} }
public [EVENT.ADMIN.KICK]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.KICK]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.KICK') console.log('EVENT.ADMIN.KICK')
@ -202,62 +167,57 @@ export class NekoMessages {
// TODO // TODO
} }
public [EVENT.ADMIN.MUTE]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.MUTE]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.MUTE') console.log('EVENT.ADMIN.MUTE')
this.emit('member.muted', id, target) this.emit('member.muted', id, target)
//this.$accessor.user.setMuted({ id: target, muted: true }) //user.setMuted({ id: target, muted: true })
} }
public [EVENT.ADMIN.UNMUTE]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.UNMUTE]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.UNMUTE') console.log('EVENT.ADMIN.UNMUTE')
this.emit('member.unmuted', id, target) this.emit('member.unmuted', id, target)
//this.$accessor.user.setMuted({ id: target, muted: false }) //user.setMuted({ id: target, muted: false })
} }
public [EVENT.ADMIN.LOCK]({ id }: AdminPayload) { protected [EVENT.ADMIN.LOCK]({ id }: AdminPayload) {
console.log('EVENT.ADMIN.LOCK') console.log('EVENT.ADMIN.LOCK')
this.emit('room.locked', id) this.emit('room.locked', id)
//this.$accessor.setLocked(true) //setLocked(true)
} }
public [EVENT.ADMIN.UNLOCK]({ id }: AdminPayload) { protected [EVENT.ADMIN.UNLOCK]({ id }: AdminPayload) {
console.log('EVENT.ADMIN.UNLOCK') console.log('EVENT.ADMIN.UNLOCK')
this.emit('room.unlocked', id) this.emit('room.unlocked', id)
//this.$accessor.setLocked(false) //setLocked(false)
} }
public [EVENT.ADMIN.CONTROL]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.CONTROL]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.CONTROL') console.log('EVENT.ADMIN.CONTROL')
this.emit('host.change', id) this.emit('control.host', id)
//this.$accessor.remote.setHost(id) //remote.setHost(id)
//this.$accessor.remote.changeKeyboard() //remote.changeKeyboard()
} }
public [EVENT.ADMIN.RELEASE]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.RELEASE]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.RELEASE') console.log('EVENT.ADMIN.RELEASE')
this.emit('host.change', null) this.emit('control.host', null)
//this.$accessor.remote.reset() //remote.reset()
} }
public [EVENT.ADMIN.GIVE]({ id, target }: AdminTargetPayload) { protected [EVENT.ADMIN.GIVE]({ id, target }: AdminTargetPayload) {
if (!target) return if (!target) return
console.log('EVENT.ADMIN.GIVE') console.log('EVENT.ADMIN.GIVE')
this.emit('host.change', target) this.emit('control.host', target)
//this.$accessor.remote.setHost(target) //remote.setHost(target)
//this.$accessor.remote.changeKeyboard() //remote.changeKeyboard()
} }
// Utilities
//public member(id: string): Member | undefined {
// return this.$accessor.user.members[id]
//}
} }

View File

@ -16,8 +16,8 @@ export const EVENT = {
RELEASE: 'control/release', RELEASE: 'control/release',
REQUEST: 'control/request', REQUEST: 'control/request',
REQUESTING: 'control/requesting', REQUESTING: 'control/requesting',
CLIPBOARD: 'control/clipboard',
GIVE: 'control/give', GIVE: 'control/give',
CLIPBOARD: 'control/clipboard',
KEYBOARD: 'control/keyboard', KEYBOARD: 'control/keyboard',
}, },
SCREEN: { SCREEN: {
@ -33,10 +33,10 @@ export const EVENT = {
ADMIN: { ADMIN: {
BAN: 'admin/ban', BAN: 'admin/ban',
KICK: 'admin/kick', KICK: 'admin/kick',
LOCK: 'admin/lock',
UNLOCK: 'admin/unlock',
MUTE: 'admin/mute', MUTE: 'admin/mute',
UNMUTE: 'admin/unmute', UNMUTE: 'admin/unmute',
LOCK: 'admin/lock',
UNLOCK: 'admin/unlock',
CONTROL: 'admin/control', CONTROL: 'admin/control',
RELEASE: 'admin/release', RELEASE: 'admin/release',
GIVE: 'admin/give', GIVE: 'admin/give',

View File

@ -1,13 +1,19 @@
interface ScreenSize { export interface ScreenSize {
width: number width: number
height: number height: number
rate: number rate: number
} }
export interface Scroll {
sensitivity: number
invert: boolean
}
export default interface State { export default interface State {
id: string | null id: string | null
display_name: string | null display_name: string | null
screen_size: ScreenSize screen_size: ScreenSize
scroll: Scroll
websocket: 'connected' | 'connecting' | 'disconnected' websocket: 'connected' | 'connecting' | 'disconnected'
webrtc: 'connected' | 'connecting' | 'disconnected' webrtc: 'connected' | 'connecting' | 'disconnected'
} }