Sort the WhoList + Mutual Video options
* The who list now sorts alphabetically instead of random * New user controls when they share video: * Require users to also be sharing before they open ours * We auto-open a viewer's video when they open ours
This commit is contained in:
parent
3f6e2193c8
commit
4a2fc9c923
|
@ -261,6 +261,8 @@ func (s *Server) OnMe(sub *Subscriber, msg Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub.VideoActive = msg.VideoActive
|
sub.VideoActive = msg.VideoActive
|
||||||
|
sub.VideoMutual = msg.VideoMutual
|
||||||
|
sub.VideoMutualOpen = msg.VideoMutualOpen
|
||||||
sub.VideoNSFW = msg.NSFW
|
sub.VideoNSFW = msg.NSFW
|
||||||
sub.ChatStatus = msg.ChatStatus
|
sub.ChatStatus = msg.ChatStatus
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,11 @@ type Message struct {
|
||||||
WhoList []WhoList `json:"whoList,omitempty"`
|
WhoList []WhoList `json:"whoList,omitempty"`
|
||||||
|
|
||||||
// Sent on `me` actions along with Username
|
// Sent on `me` actions along with Username
|
||||||
VideoActive bool `json:"videoActive,omitempty"` // user tells us their cam status
|
VideoActive bool `json:"videoActive,omitempty"` // user tells us their cam status
|
||||||
ChatStatus string `json:"status,omitempty"` // online vs. away
|
VideoMutual bool `json:"videoMutual,omitempty"` // user wants mutual viewers
|
||||||
NSFW bool `json:"nsfw,omitempty"` // user tags their video NSFW
|
VideoMutualOpen bool `json:"videoMutualOpen,omitempty"`
|
||||||
|
ChatStatus string `json:"status,omitempty"` // online vs. away
|
||||||
|
NSFW bool `json:"nsfw,omitempty"` // user tags their video NSFW
|
||||||
|
|
||||||
// Sent on `open` actions along with the (other) Username.
|
// Sent on `open` actions along with the (other) Username.
|
||||||
OpenSecret string `json:"openSecret,omitempty"`
|
OpenSecret string `json:"openSecret,omitempty"`
|
||||||
|
@ -66,10 +68,12 @@ const (
|
||||||
|
|
||||||
// WhoList is a member entry in the chat room.
|
// WhoList is a member entry in the chat room.
|
||||||
type WhoList struct {
|
type WhoList struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
VideoActive bool `json:"videoActive,omitempty"`
|
VideoActive bool `json:"videoActive,omitempty"`
|
||||||
NSFW bool `json:"nsfw,omitempty"`
|
VideoMutual bool `json:"videoMutual,omitempty"`
|
||||||
Status string `json:"status"`
|
VideoMutualOpen bool `json:"videoMutualOpen,omitempty"`
|
||||||
|
NSFW bool `json:"nsfw,omitempty"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
|
||||||
// JWT auth extra settings.
|
// JWT auth extra settings.
|
||||||
Operator bool `json:"op"`
|
Operator bool `json:"op"`
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -20,18 +21,20 @@ import (
|
||||||
// Subscriber represents a connected WebSocket session.
|
// Subscriber represents a connected WebSocket session.
|
||||||
type Subscriber struct {
|
type Subscriber struct {
|
||||||
// User properties
|
// User properties
|
||||||
ID int // ID assigned by server
|
ID int // ID assigned by server
|
||||||
Username string
|
Username string
|
||||||
VideoActive bool
|
VideoActive bool
|
||||||
VideoNSFW bool
|
VideoMutual bool
|
||||||
ChatStatus string
|
VideoMutualOpen bool
|
||||||
JWTClaims *jwt.Claims
|
VideoNSFW bool
|
||||||
authenticated bool // has passed the login step
|
ChatStatus string
|
||||||
conn *websocket.Conn
|
JWTClaims *jwt.Claims
|
||||||
ctx context.Context
|
authenticated bool // has passed the login step
|
||||||
cancel context.CancelFunc
|
conn *websocket.Conn
|
||||||
messages chan []byte
|
ctx context.Context
|
||||||
closeSlow func()
|
cancel context.CancelFunc
|
||||||
|
messages chan []byte
|
||||||
|
closeSlow func()
|
||||||
|
|
||||||
muteMu sync.RWMutex
|
muteMu sync.RWMutex
|
||||||
booted map[string]struct{} // usernames booted off your camera
|
booted map[string]struct{} // usernames booted off your camera
|
||||||
|
@ -308,8 +311,16 @@ func (s *Server) SendTo(username string, msg Message) error {
|
||||||
func (s *Server) SendWhoList() {
|
func (s *Server) SendWhoList() {
|
||||||
var (
|
var (
|
||||||
subscribers = s.IterSubscribers()
|
subscribers = s.IterSubscribers()
|
||||||
|
usernames = []string{} // distinct and sorted usernames
|
||||||
|
userSub = map[string]*Subscriber{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for _, sub := range subscribers {
|
||||||
|
usernames = append(usernames, sub.Username)
|
||||||
|
userSub[sub.Username] = sub
|
||||||
|
}
|
||||||
|
sort.Strings(usernames)
|
||||||
|
|
||||||
// Build the WhoList for each subscriber.
|
// Build the WhoList for each subscriber.
|
||||||
// TODO: it's the only way to fake videoActive for booted user views.
|
// TODO: it's the only way to fake videoActive for booted user views.
|
||||||
for _, sub := range subscribers {
|
for _, sub := range subscribers {
|
||||||
|
@ -318,16 +329,19 @@ func (s *Server) SendWhoList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var users = []WhoList{}
|
var users = []WhoList{}
|
||||||
for _, user := range subscribers {
|
for _, un := range usernames {
|
||||||
|
user := userSub[un]
|
||||||
if user.ChatStatus == "hidden" {
|
if user.ChatStatus == "hidden" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
who := WhoList{
|
who := WhoList{
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
Status: user.ChatStatus,
|
Status: user.ChatStatus,
|
||||||
VideoActive: user.VideoActive,
|
VideoActive: user.VideoActive,
|
||||||
NSFW: user.VideoNSFW,
|
VideoMutual: user.VideoMutual,
|
||||||
|
VideoMutualOpen: user.VideoMutualOpen,
|
||||||
|
NSFW: user.VideoNSFW,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this person had booted us, force their camera to "off"
|
// If this person had booted us, force their camera to "off"
|
||||||
|
|
|
@ -249,4 +249,9 @@ body {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 14px;
|
top: 14px;
|
||||||
left: 16px;
|
left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cursors */
|
||||||
|
.cursor-notallowed {
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
|
@ -91,6 +91,8 @@ const app = Vue.createApp({
|
||||||
stream: null, // MediaStream object
|
stream: null, // MediaStream object
|
||||||
muted: false, // our outgoing mic is muted, not by default
|
muted: false, // our outgoing mic is muted, not by default
|
||||||
nsfw: false, // user has flagged their camera to be NSFW
|
nsfw: false, // user has flagged their camera to be NSFW
|
||||||
|
mutual: false, // user wants viewers to share their own videos
|
||||||
|
mutualOpen: false, // user wants to open video mutually
|
||||||
|
|
||||||
// Who all is watching me? map of users.
|
// Who all is watching me? map of users.
|
||||||
watching: {},
|
watching: {},
|
||||||
|
@ -331,6 +333,8 @@ const app = Vue.createApp({
|
||||||
this.ws.conn.send(JSON.stringify({
|
this.ws.conn.send(JSON.stringify({
|
||||||
action: "me",
|
action: "me",
|
||||||
videoActive: this.webcam.active,
|
videoActive: this.webcam.active,
|
||||||
|
videoMutual: this.webcam.mutual,
|
||||||
|
videoMutualOpen: this.webcam.mutualOpen,
|
||||||
status: this.status,
|
status: this.status,
|
||||||
nsfw: this.webcam.nsfw,
|
nsfw: this.webcam.nsfw,
|
||||||
}));
|
}));
|
||||||
|
@ -682,6 +686,15 @@ const app = Vue.createApp({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are the offerer, and this member wants to auto-open our camera
|
||||||
|
// then add our own stream to the connection.
|
||||||
|
if (isOfferer && this.whoMap[username].videoMutualOpen && this.webcam.active) {
|
||||||
|
let stream = this.webcam.stream;
|
||||||
|
stream.getTracks().forEach(track => {
|
||||||
|
pc.addTrack(track, stream)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// If we are the offerer, begin the connection.
|
// If we are the offerer, begin the connection.
|
||||||
if (isOfferer) {
|
if (isOfferer) {
|
||||||
pc.createOffer({
|
pc.createOffer({
|
||||||
|
@ -953,6 +966,14 @@ const app = Vue.createApp({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this user requests mutual viewership...
|
||||||
|
if (user.videoMutual && !this.webcam.active) {
|
||||||
|
this.ChatClient(
|
||||||
|
`<strong>${user.username}</strong> has requested that you should share your own camera too before opening theirs.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.sendOpen(user.username);
|
this.sendOpen(user.username);
|
||||||
|
|
||||||
// Responsive CSS -> go to chat panel to see the camera
|
// Responsive CSS -> go to chat panel to see the camera
|
||||||
|
@ -995,6 +1016,16 @@ const app = Vue.createApp({
|
||||||
// Inform backend we have closed it.
|
// Inform backend we have closed it.
|
||||||
this.sendWatch(username, false);
|
this.sendWatch(username, false);
|
||||||
},
|
},
|
||||||
|
unMutualVideo() {
|
||||||
|
// If we had our camera on to watch a video of someone who wants mutual cameras,
|
||||||
|
// and then we turn ours off: we should unfollow the ones with mutual video.
|
||||||
|
for (let row of this.whoList) {
|
||||||
|
let username = row.username;
|
||||||
|
if (row.videoMutual && this.WebRTC.pc[username] != undefined) {
|
||||||
|
this.closeVideo(username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Show who watches our video.
|
// Show who watches our video.
|
||||||
showViewers() {
|
showViewers() {
|
||||||
|
@ -1052,6 +1083,9 @@ const app = Vue.createApp({
|
||||||
this.closeVideo(username, "answerer");
|
this.closeVideo(username, "answerer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hang up on mutual cameras.
|
||||||
|
this.unMutualVideo();
|
||||||
|
|
||||||
// Tell backend our camera state.
|
// Tell backend our camera state.
|
||||||
this.sendMe();
|
this.sendMe();
|
||||||
},
|
},
|
||||||
|
|
|
@ -228,8 +228,8 @@
|
||||||
<i class="fa fa-eye"></i> see who is watching will be at the top of the page.
|
<i class="fa fa-eye"></i> see who is watching will be at the top of the page.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="block">
|
<p class="block mb-1">
|
||||||
If your camera will be featuring "<abbr title="Not Safe For Work">NSFW</abbr>" or sexual content, please mark it as such by
|
If your camera will be featuring "<abbr title="Not Safe For Work">Explicit</abbr>" or sexual content, please mark it as such by
|
||||||
clicking on the <i class="fa fa-fire has-text-danger"></i> button or checking the box below to start with it enabled.
|
clicking on the <i class="fa fa-fire has-text-danger"></i> button or checking the box below to start with it enabled.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -237,7 +237,28 @@
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
v-model="webcam.nsfw">
|
v-model="webcam.nsfw">
|
||||||
Check this box if your webcam will <em>definitely</em> be NSFW. 😈
|
Check this box if your webcam will <em>definitely</em> be Explicit. 😈
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="block mb-1">
|
||||||
|
<label class="label">Mutual webcam options:</label>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="field mb-1">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
v-model="webcam.mutual">
|
||||||
|
People must be sharing their own camera before they can open mine
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox"
|
||||||
|
:disabled="!webcam.mutual"
|
||||||
|
v-model="webcam.mutualOpen">
|
||||||
|
When someone opens my camera, I also open their camera automatically
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -778,8 +799,12 @@
|
||||||
:class="{
|
:class="{
|
||||||
'is-danger is-outlined': u.videoActive && u.nsfw,
|
'is-danger is-outlined': u.videoActive && u.nsfw,
|
||||||
'is-info is-outlined': u.videoActive && !u.nsfw,
|
'is-info is-outlined': u.videoActive && !u.nsfw,
|
||||||
|
'cursor-notallowed': u.videoActive && u.videoMutual && !webcam.active,
|
||||||
}"
|
}"
|
||||||
title="Open video stream"
|
:title="`Open video stream` +
|
||||||
|
(u.videoActive && u.videoMutual ? '; mutual video sharing required' : '') +
|
||||||
|
(u.videoActive && u.videoMutualOpen ? '; will auto-open your video' : '')"
|
||||||
|
|
||||||
@click="openVideo(u)">
|
@click="openVideo(u)">
|
||||||
<i class="fa fa-video"></i>
|
<i class="fa fa-video"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user