Draggable resizable video panels
This commit is contained in:
parent
5f2456103b
commit
368902e801
|
@ -33,7 +33,11 @@ It is very much in the style of the old-school Flash based webcam chat rooms of
|
|||
|
||||
Some important features still lacking:
|
||||
|
||||
* Operator controls (kick/ban users)
|
||||
* Operator commands
|
||||
* [x] /kick users
|
||||
* [x] /nsfw to mark someone's camera
|
||||
* [ ] /ban users
|
||||
* [ ] /op users (give temporary mod control)
|
||||
|
||||
# Configuration
|
||||
|
||||
|
|
28
pkg/banned_users.go
Normal file
28
pkg/banned_users.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package barertc
|
||||
|
||||
import "time"
|
||||
|
||||
/* Functions to handle banned users */
|
||||
|
||||
/*
|
||||
BanList holds (in memory) knowledge of currently banned users.
|
||||
|
||||
All bans are reset if the chat server is rebooted. Otherwise each ban
|
||||
comes with a duration - default is 24 hours by the operator can specify
|
||||
a duration with a ban. If the server is not rebooted, bans will be lifted
|
||||
after they expire.
|
||||
|
||||
Bans are against usernames and will also block a JWT token from
|
||||
authenticating if they are currently banned.
|
||||
*/
|
||||
type BanList struct {
|
||||
Active []Ban
|
||||
}
|
||||
|
||||
// Ban is an entry on the ban list.
|
||||
type Ban struct {
|
||||
Username string
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
//
|
|
@ -1,6 +1,10 @@
|
|||
package barertc
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProcessCommand parses a chat message for "/commands"
|
||||
func (s *Server) ProcessCommand(sub *Subscriber, msg Message) bool {
|
||||
|
@ -18,21 +22,10 @@ func (s *Server) ProcessCommand(sub *Subscriber, msg Message) bool {
|
|||
if sub.JWTClaims != nil && sub.JWTClaims.IsAdmin {
|
||||
switch words[0] {
|
||||
case "/kick":
|
||||
if len(words) == 1 {
|
||||
sub.ChatServer("Usage: `/kick username` to remove the user from the chat room.")
|
||||
}
|
||||
username := words[1]
|
||||
other, err := s.GetSubscriber(username)
|
||||
if err != nil {
|
||||
sub.ChatServer("/kick: username not found: %s", username)
|
||||
} else {
|
||||
other.ChatServer("You have been kicked from the chat room by %s", sub.Username)
|
||||
other.SendJSON(Message{
|
||||
Action: ActionKick,
|
||||
})
|
||||
s.DeleteSubscriber(other)
|
||||
sub.ChatServer("%s has been kicked from the room", username)
|
||||
}
|
||||
s.KickCommand(words, sub)
|
||||
return true
|
||||
case "/ban":
|
||||
s.BanCommand(words, sub)
|
||||
return true
|
||||
case "/nsfw":
|
||||
if len(words) == 1 {
|
||||
|
@ -63,3 +56,62 @@ func (s *Server) ProcessCommand(sub *Subscriber, msg Message) bool {
|
|||
// Not handled.
|
||||
return false
|
||||
}
|
||||
|
||||
// KickCommand handles the `/kick` operator command.
|
||||
func (s *Server) KickCommand(words []string, sub *Subscriber) {
|
||||
if len(words) == 1 {
|
||||
sub.ChatServer("Usage: `/kick username` to remove the user from the chat room.")
|
||||
}
|
||||
username := words[1]
|
||||
other, err := s.GetSubscriber(username)
|
||||
if err != nil {
|
||||
sub.ChatServer("/kick: username not found: %s", username)
|
||||
} else {
|
||||
other.ChatServer("You have been kicked from the chat room by %s", sub.Username)
|
||||
other.SendJSON(Message{
|
||||
Action: ActionKick,
|
||||
})
|
||||
s.DeleteSubscriber(other)
|
||||
sub.ChatServer("%s has been kicked from the room", username)
|
||||
}
|
||||
}
|
||||
|
||||
// BanCommand handles the `/ban` operator command.
|
||||
func (s *Server) BanCommand(words []string, sub *Subscriber) {
|
||||
if len(words) == 1 {
|
||||
sub.ChatServer(
|
||||
"Usage: `/ban username` to remove the user from the chat room for 24 hours (default).\n\n" +
|
||||
"Set another duration (in hours, fractions supported) like: `/ban username 0.5` for a 30-minute ban.",
|
||||
)
|
||||
}
|
||||
|
||||
// Parse the command.
|
||||
var (
|
||||
username = words[1]
|
||||
duration = 24 * time.Hour
|
||||
)
|
||||
if len(words) >= 3 {
|
||||
if dur, err := strconv.ParseFloat(words[2], 64); err == nil {
|
||||
if dur < 1 {
|
||||
duration = time.Duration(dur*60) * time.Second
|
||||
} else {
|
||||
duration = time.Duration(dur) * time.Hour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: banning, for now it just kicks.
|
||||
_ = duration
|
||||
|
||||
other, err := s.GetSubscriber(username)
|
||||
if err != nil {
|
||||
sub.ChatServer("/ban: username not found: %s", username)
|
||||
} else {
|
||||
other.ChatServer("You have been kicked from the chat room by %s", sub.Username)
|
||||
other.SendJSON(Message{
|
||||
Action: ActionKick,
|
||||
})
|
||||
s.DeleteSubscriber(other)
|
||||
sub.ChatServer("%s has been kicked from the room", username)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,18 +147,21 @@ body {
|
|||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
display: flex;
|
||||
/* display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: left;
|
||||
align-items: left; */
|
||||
}
|
||||
|
||||
.video-feeds > .feed {
|
||||
position: relative;
|
||||
flex: 0 0 168px;
|
||||
/* flex: 0 0 168px; */
|
||||
float: left;
|
||||
width: 168px;
|
||||
height: 112px;
|
||||
background-color: black;
|
||||
margin: 3px;
|
||||
overflow: hidden;
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.video-feeds.x1 > .feed {
|
||||
|
@ -193,7 +196,7 @@ body {
|
|||
.video-feeds > .feed > .controls {
|
||||
position: absolute;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
right: 4px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
}
|
||||
|
||||
|
|
|
@ -188,14 +188,20 @@ const app = Vue.createApp({
|
|||
history.pushState(null, "", location.href.split("?")[0]);
|
||||
|
||||
// XX: always show login dialog to test if this helps iOS devices.
|
||||
this.loginModal.visible = true;
|
||||
/*
|
||||
// this.loginModal.visible = true;
|
||||
if (!this.username) {
|
||||
this.loginModal.visible = true;
|
||||
} else {
|
||||
this.signIn();
|
||||
}
|
||||
*/
|
||||
},
|
||||
watch: {
|
||||
"webcam.videoScale": () => {
|
||||
document.querySelectorAll(".video-feeds > .feed").forEach(node => {
|
||||
node.style.width = null;
|
||||
node.style.height = null;
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
chatHistory() {
|
||||
|
|
|
@ -463,11 +463,12 @@
|
|||
<!-- Video Feeds-->
|
||||
|
||||
<!-- My video -->
|
||||
<div class="feed" v-show="webcam.active">
|
||||
<video class="feed"
|
||||
id="localVideo"
|
||||
v-show="webcam.active"
|
||||
autoplay muted>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<!-- Others' videos -->
|
||||
<div class="feed" v-for="(stream, username) in WebRTC.streams" v-bind:key="username">
|
||||
|
@ -509,7 +510,17 @@
|
|||
<div class="feed">
|
||||
hi
|
||||
</div>
|
||||
<div class="feed">
|
||||
hi
|
||||
</div>
|
||||
<div class="feed">
|
||||
hi
|
||||
</div>
|
||||
<div class="feed">
|
||||
hi
|
||||
</div>
|
||||
-->
|
||||
|
||||
</div>
|
||||
<div class="card-content" id="chatHistory">
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user