aboutsummaryrefslogtreecommitdiff
path: root/source/client/user.ts
blob: f4f33e104bd618892422595f7d3684519d378905 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { log } from "./logger"
import { Room } from "./room"


export abstract class User {
    name: string
    room: Room

    el: HTMLElement
    media_el?: HTMLElement

    display?: { audio_status_el: HTMLElement, video_status_el: HTMLElement }

    local: boolean = false

    stream: MediaStream = new MediaStream()

    constructor(room: Room, name: string) {
        this.name = name
        this.room = room
        this.el = document.createElement("div")
        this.el.classList.add("user")
        this.room.el.append(this.el)
        this.setup_view()
        setTimeout(() => this.update_view(), 1)
    }

    add_track(t: MediaStreamTrack) {
        this.stream.addTrack(t)
        this.update_view()
        t.onended = () => {
            log("media", "track ended", t)
            this.stream.removeTrack(t)
            this.update_view()
        }
        t.onmute = () => {
            log("media", "track muted", t)
            this.stream.removeTrack(t)
            this.update_view()
        }
        t.onunmute = () => {
            log("media", "track unmuted", t)
            this.stream.addTrack(t)
            this.update_view()
        }
    }

    setup_view() {
        const info_el = document.createElement("div")
        info_el.classList.add("info")
        const name_el = document.createElement("span")
        name_el.textContent = this.name
        name_el.classList.add("name")
        const audio_status_el = document.createElement("span")
        const video_status_el = document.createElement("span")
        video_status_el.classList.add("status", "video-status")
        audio_status_el.classList.add("status", "audio-status")
        audio_status_el.textContent = "A"
        video_status_el.textContent = "V"
        info_el.append(audio_status_el, video_status_el, name_el)
        this.display = { video_status_el, audio_status_el }
        this.el.append(info_el)
    }

    update_view() {
        if (this.stream.getAudioTracks().length > 0)
            this.display?.audio_status_el.classList.add("enabled")
        else this.display?.audio_status_el.classList.remove("enabled")

        if (this.stream.getVideoTracks().length > 0)
            this.display?.video_status_el.classList.add("enabled")
        else this.display?.video_status_el.classList.remove("enabled")

        if (this.media_el) this.el.removeChild(this.media_el)
        this.media_el = this.create_media_view()
        this.el.appendChild(this.media_el)
    }

    create_media_view() {
        const has_video = this.stream.getVideoTracks().length > 0
        const has_audio = this.stream.getAudioTracks().length > 0
        if (this.local && !has_video) return document.createElement("div")
        const media_el = has_video ? document.createElement("video") : document.createElement("audio")
        media_el.classList.add("media")
        media_el.autoplay = true
        if (has_video) media_el.toggleAttribute("playsinline")
        media_el.srcObject = this.stream
        if (has_video) media_el.addEventListener("click", () => {
            media_el.classList.remove("maximized")
        })
        if (this.local) media_el.muted = true

        const controls_el = document.createElement("div")
        controls_el.classList.add("media-controls")
        if (has_video) {
            const pip_el = document.createElement("input")
            pip_el.type = "button"
            pip_el.addEventListener("click", () => {
                //@ts-ignore
                media_el.requestPictureInPicture()
            })
            pip_el.value = "Picture-in-Picture"
            const max_el = document.createElement("input")
            max_el.type = "button"
            max_el.addEventListener("click", () => {
                media_el.classList.add("maximized")
            })
            max_el.value = "Maximize"
            controls_el.append(max_el, pip_el)
        }
        if (has_audio) {
            // TODO volume controls
        }

        const el = document.createElement("div")
        el.classList.add("media-container")
        el.append(media_el, controls_el)
        return el
    }
}