aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2020-02-22 06:35:57 +0300
committerRblSb <msrblsb@gmail.com>2020-02-22 06:35:57 +0300
commit08d068181045f91f0aa160f9b088a18048b948ec (patch)
treea995230c279a5a1fd849dc5202301d15474bca61 /src
parent4e0859a0f902e84cfafb38103e0be9f5b81d7abe (diff)
More client buttons
Diffstat (limited to 'src')
-rw-r--r--src/Client.hx49
-rw-r--r--src/ClientTools.hx6
-rw-r--r--src/Types.hx2
-rw-r--r--src/client/Buttons.hx156
-rw-r--r--src/client/Main.hx197
-rw-r--r--src/client/MobileView.hx13
-rw-r--r--src/client/Player.hx2
-rw-r--r--src/client/Split.hx7
-rw-r--r--src/server/Main.hx37
9 files changed, 334 insertions, 135 deletions
diff --git a/src/Client.hx b/src/Client.hx
index e44417a..7aa14c5 100644
--- a/src/Client.hx
+++ b/src/Client.hx
@@ -5,33 +5,70 @@ import js.npm.ws.WebSocket;
#elseif js
import js.html.WebSocket;
#end
+import haxe.EnumFlags;
+
+enum ClientGroup {
+ User;
+ Leader;
+ Admin;
+}
typedef ClientData = {
name:String,
- isLeader:Bool
+ group:Int
}
class Client {
+ #if nodejs
public final ws:WebSocket;
+ public final id:Int;
+ #end
public var name:String;
- public var isLeader:Bool;
+ public var group:EnumFlags<ClientGroup>;
+ public var isLeader(get, set):Bool;
+ public var isAdmin(get, set):Bool;
- public function new(?ws:WebSocket, name:String, isLeader = false) {
+ public function new(?ws:WebSocket, ?id:Int, name:String, group:Int) {
+ #if nodejs
this.ws = ws;
+ this.id = id;
+ #end
this.name = name;
- this.isLeader = isLeader;
+ this.group = new EnumFlags(group);
+ }
+
+ inline function get_isLeader():Bool {
+ return group.has(Leader);
+ }
+
+ inline function set_isLeader(flag:Bool):Bool {
+ return setGroupFlag(Leader, flag);
+ }
+
+ inline function get_isAdmin():Bool {
+ return group.has(Admin);
+ }
+
+ inline function set_isAdmin(flag:Bool):Bool {
+ return setGroupFlag(Admin, flag);
+ }
+
+ function setGroupFlag(type:ClientGroup, flag:Bool):Bool {
+ if (flag) group.set(type);
+ else group.unset(type);
+ return flag;
}
public function getData():ClientData {
return {
name: name,
- isLeader: isLeader
+ group: group.toInt()
}
}
public static function fromData(data:ClientData):Client {
- return new Client(data.name, data.isLeader);
+ return new Client(data.name, data.group);
}
}
diff --git a/src/ClientTools.hx b/src/ClientTools.hx
index 4503ada..26faab6 100644
--- a/src/ClientTools.hx
+++ b/src/ClientTools.hx
@@ -16,11 +16,13 @@ class ClientTools {
return false;
}
- public static function getByName(clients:Array<Client>, name:String):Null<Client> {
+ public static function getByName(
+ clients:Array<Client>, name:String, ?def:Client
+ ):Null<Client> {
for (client in clients) {
if (client.name == name) return client;
}
- return null;
+ return def;
}
}
diff --git a/src/Types.hx b/src/Types.hx
index 03f18bd..8a0bde2 100644
--- a/src/Types.hx
+++ b/src/Types.hx
@@ -54,6 +54,7 @@ typedef WsEvent = {
?isUnknownClient:Bool,
},
?logout:{
+ oldClientName:String,
clientName:String,
clients:Array<ClientData>,
},
@@ -105,4 +106,5 @@ enum abstract WsEventType(String) {
var GetTime;
var SetTime;
var SetLeader;
+ var ClearChat;
}
diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx
new file mode 100644
index 0000000..a5fbfa4
--- /dev/null
+++ b/src/client/Buttons.hx
@@ -0,0 +1,156 @@
+package client;
+
+import js.html.KeyboardEvent;
+import js.html.InputElement;
+import js.html.ButtonElement;
+import js.html.Element;
+import client.Main.ge;
+import js.Browser.window;
+import js.html.Event;
+
+class Buttons {
+
+ static final personalHistory:Array<String> = [];
+ static var personalHistoryId = -1;
+ static var split:Split;
+
+ public static function init(main:Main):Void {
+ initChatInput(main);
+
+ final smilesBtn = ge("#smilesbtn");
+ smilesBtn.onclick = e -> {
+ smilesBtn.classList.toggle("active");
+ final smilesWrap = ge("#smileswrap");
+ if (smilesBtn.classList.contains("active"))
+ smilesWrap.style.display = "block";
+ else smilesWrap.style.display = "none";
+ }
+
+ ge("#clearchatbtn").style.display = "inline-block";
+ ge("#clearchatbtn").onclick = e -> {
+ if (main.isAdmin()) main.send({type: ClearChat});
+ }
+ final userList = ge("#userlist");
+ userList.onclick = e -> {
+ if (!main.isAdmin()) return;
+ var el:Element = cast e.target;
+ if (userList == el) return;
+ if (!el.classList.contains("userlist_item"))
+ el = el.parentElement;
+ var name = "";
+ if (el.children.length == 1) {
+ name = el.lastElementChild.innerText;
+ }
+ main.send({
+ type: SetLeader,
+ setLeader: {
+ clientName: name
+ }
+ });
+ }
+
+ split = new Split(["#chatwrap", "#videowrap"], {
+ sizes: [40, 60],
+ onDragEnd: () -> {
+ window.dispatchEvent(new Event("resize"));
+ },
+ minSize: 185,
+ snapOffset: 0
+ });
+
+ final userlistToggle = ge("#userlisttoggle");
+ userlistToggle.onclick = e -> {
+ final style = ge("#userlist").style;
+ if (style.display == "none") {
+ userlistToggle.classList.add("glyphicon-chevron-down");
+ userlistToggle.classList.remove("glyphicon-chevron-right");
+ style.display = "block";
+ } else {
+ userlistToggle.classList.add("glyphicon-chevron-right");
+ userlistToggle.classList.remove("glyphicon-chevron-down");
+ style.display = "none";
+ }
+ }
+ ge("#usercount").onclick = userlistToggle.onclick;
+
+ final extendPlayer = ge("#extendplayer");
+ extendPlayer.onclick = e -> {
+ if (extendPlayer.classList.contains("active")) {
+ split.setSizes([40, 60]);
+ ge('#userlist').style.width = "90px";
+ } else {
+ split.setSizes([20, 80]);
+ ge('#userlist').style.width = "80px";
+ }
+ extendPlayer.classList.toggle("active");
+ window.dispatchEvent(new Event('resize'));
+ }
+
+ final showMediaUrl:ButtonElement = cast ge("#showmediaurl");
+ showMediaUrl.onclick = e -> {
+ ge("#showmediaurl").classList.toggle("collapsed");
+ ge("#showmediaurl").classList.toggle("active");
+ ge("#addfromurl").classList.toggle("collapse");
+ }
+
+ window.onresize = onVideoResize;
+ window.dispatchEvent(new Event("resize"));
+ }
+
+ static function onVideoResize():Void {
+ final player = ge("#ytapiplayer");
+ final height = player.offsetHeight - ge("#chatline").offsetHeight;
+ ge("#messagebuffer").style.height = '${height}px';
+ ge("#userlist").style.height = '${height}px';
+ }
+
+
+ static function initChatInput(main:Main):Void {
+ final guestName:InputElement = cast ge("#guestname");
+ guestName.onkeydown = e -> {
+ if (guestName.value.length == 0) return;
+ if (e.keyCode == 13) main.send({
+ type: Login,
+ login: {
+ clientName: guestName.value
+ }
+ });
+ }
+
+ final chatLine:InputElement = cast ge("#chatline");
+ chatLine.onkeydown = function(e:KeyboardEvent) {
+ switch (e.keyCode) {
+ case 13: // Enter
+ if (chatLine.value.length == 0) return;
+ main.send({
+ type: Message,
+ message: {
+ clientName: "",
+ text: chatLine.value
+ }
+ });
+ personalHistory.push(chatLine.value);
+ if (personalHistory.length > 50) personalHistory.shift();
+ personalHistoryId = -1;
+ chatLine.value = "";
+ case 38: // Up
+ personalHistoryId--;
+ if (personalHistoryId == -2) {
+ personalHistoryId = personalHistory.length - 1;
+ if (personalHistoryId == -1) return;
+ } else if (personalHistoryId == -1) personalHistoryId++;
+ chatLine.value = personalHistory[personalHistoryId];
+ case 40: // Down
+ if (personalHistoryId == -1) return;
+ personalHistoryId++;
+ if (personalHistoryId > personalHistory.length - 1) {
+ personalHistoryId = -1;
+ chatLine.value = "";
+ return;
+ }
+ chatLine.value = personalHistory[personalHistoryId];
+ }
+ }
+ }
+
+}
diff --git a/src/client/Main.hx b/src/client/Main.hx
index 5b2dc4c..f9232eb 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -19,12 +19,10 @@ using ClientTools;
class Main {
final clients:Array<Client> = [];
- final personalHistory:Array<String> = [];
var pageTitle = document.title;
var config:Null<Config>;
final filters:Array<{regex:EReg, replace:String}> = [];
- var personal:Null<Client>;
- var personalHistoryId = -1;
+ var personal = new Client("Unknown", 0);
var isConnected = false;
var ws:WebSocket;
final player:Player;
@@ -70,67 +68,13 @@ class Main {
}
function initListeners():Void {
- final smilesBtn = ge("#smilesbtn");
- smilesBtn.onclick = e -> {
- final smilesWrap = ge("#smileswrap");
- if (smilesWrap.style.display == "")
- smilesWrap.style.display = "block";
- else smilesWrap.style.display = "";
- }
-
- final guestName:InputElement = cast ge("#guestname");
- guestName.onkeydown = (e:KeyboardEvent) -> {
- if (guestName.value.length == 0) return;
- if (e.keyCode == 13) send({
- type: Login,
- login: {
- clientName: guestName.value
- }
- });
- }
-
- final chatLine:InputElement = cast ge("#chatline");
- chatLine.onkeydown = function(e:KeyboardEvent) {
- switch (e.keyCode) {
- case 13: // Enter
- if (chatLine.value.length == 0) return;
- send({
- type: Message,
- message: {
- clientName: "",
- text: chatLine.value
- }
- });
- personalHistory.push(chatLine.value);
- if (personalHistory.length > 50) personalHistory.shift();
- personalHistoryId = -1;
- chatLine.value = "";
- case 38: // Up
- personalHistoryId--;
- if (personalHistoryId == -2) {
- personalHistoryId = personalHistory.length - 1;
- if (personalHistoryId == -1) return;
- } else if (personalHistoryId == -1) personalHistoryId++;
- chatLine.value = personalHistory[personalHistoryId];
- case 40: // Down
- if (personalHistoryId == -1) return;
- personalHistoryId++;
- if (personalHistoryId > personalHistory.length - 1) {
- personalHistoryId = -1;
- chatLine.value = "";
- return;
- }
- chatLine.value = personalHistory[personalHistoryId];
- }
- }
-
+ Buttons.init(this);
MobileView.init();
- final leaderBtn:InputElement = cast ge("#leader_btn");
+ final leaderBtn = ge("#leader_btn");
leaderBtn.onclick = (e) -> {
- if (personal == null) return;
- if (!personal.isLeader) leaderBtn.classList.add('label-success');
- else leaderBtn.classList.remove('label-success');
+ // change button style before answer
+ setLeaderButton(!personal.isLeader);
final name = personal.isLeader ? "" : personal.name;
send({
type: SetLeader,
@@ -140,12 +84,7 @@ class Main {
});
}
- final showMediaUrl:ButtonElement = cast ge("#showmediaurl");
- showMediaUrl.onclick = (e:MouseEvent) -> {
- ge("#showmediaurl").classList.toggle("collapsed");
- ge("#showmediaurl").classList.toggle("active");
- ge("#addfromurl").classList.toggle("collapse");
- }
+ // TODO next/end
ge("#queue_next").onclick = (e:MouseEvent) -> addVideoUrl();
ge("#queue_end").onclick = (e:MouseEvent) -> addVideoUrl();
ge("#mediaurl").onkeydown = function(e:KeyboardEvent) {
@@ -153,26 +92,32 @@ class Main {
}
}
- public function isLeader():Bool {
- return personal != null && personal.isLeader;
+ public inline function isLeader():Bool {
+ return personal.isLeader;
+ }
+
+ public inline function isAdmin():Bool {
+ return personal.isAdmin;
}
function addVideoUrl():Void {
final mediaUrl:InputElement = cast ge("#mediaurl");
final url = mediaUrl.value;
- final name = personal == null ? "Unknown" : personal.name;
+ var name = url.substr(url.lastIndexOf('/') + 1);
+ final matchName = ~/^(.+)\./;
+ if (matchName.match(name)) name = matchName.matched(1);
+ else name = Lang.get("rawVideo");
+
getRemoteVideoDuration(mediaUrl.value, (duration:Float) -> {
send({
- type: AddVideo,
- addVideo: {
+ type: AddVideo, addVideo: {
item: {
url: url,
- title: Lang.get("rawVideo"),
- author: name,
+ title: name,
+ author: personal.name,
duration: duration
}
- }
- });
+ }});
});
mediaUrl.value = "";
}
@@ -181,8 +126,11 @@ class Main {
final player:Element = ge("#ytapiplayer");
final video = document.createVideoElement();
video.src = src;
+ // TODO catch errors on AddVideo and getRemoteVideoDuration
+ video.onerror = e -> {
+ callback(0);
+ }
video.onloadedmetadata = () -> {
- trace(video.duration);
player.removeChild(video);
callback(video.duration);
}
@@ -201,30 +149,7 @@ class Main {
trace('Event: ${data.type}', untyped data[t]);
switch (data.type) {
case Connected:
- setConfig(data.connected.config);
- if (data.connected.isUnknownClient) {
- updateClients(data.connected.clients);
- ge("#guestlogin").style.display = "block";
- ge("#chatline").style.display = "none";
- } else {
- onLogin(data.connected.clients, data.connected.clientName);
- }
- final guestName:InputElement = cast ge("#guestname");
- if (guestName.value.length > 0) send({
- type: Login,
- login: {
- clientName: guestName.value
- }
- });
- for (message in data.connected.history) {
- addMessage(message.name, message.text, message.time);
- }
- final list = data.connected.videoList;
- if (list.length == 0) return;
- player.setVideo(list[0]);
- for (video in data.connected.videoList) {
- player.addVideoItem(video);
- }
+ onConnected(data);
case Login:
onLogin(data.login.clients, data.login.clientName);
case LoginError:
@@ -233,12 +158,11 @@ class Main {
serverMessage(4, text);
case Logout:
updateClients(data.logout.clients);
- personal = null;
- ge("#guestlogin").style.display = "block";
- ge("#chatline").style.display = "none";
+ personal = new Client(data.logout.clientName, 0);
+ showGuestLoginPanel();
case UpdateClients:
updateClients(data.updateClients.clients);
- if (personal != null) personal = clients.getByName(personal.name);
+ personal = clients.getByName(personal.name, personal);
case Message:
addMessage(data.message.clientName, data.message.text);
case AddVideo:
@@ -280,10 +204,38 @@ class Main {
case SetLeader:
clients.setLeader(data.setLeader.clientName);
updateUserList();
- final leaderBtn:InputElement = cast ge("#leader_btn");
- if (isLeader()) leaderBtn.classList.add('label-success');
- else leaderBtn.classList.remove('label-success');
+ setLeaderButton(isLeader());
if (isLeader()) player.setTime(player.getTime(), false);
+ case ClearChat:
+ ge("#messagebuffer").innerHTML = "";
+ }
+ }
+
+ function onConnected(data:WsEvent):Void {
+ final connected = data.connected;
+ setConfig(connected.config);
+ if (connected.isUnknownClient) {
+ updateClients(connected.clients);
+ personal = clients.getByName(connected.clientName, personal);
+ showGuestLoginPanel();
+ } else {
+ onLogin(connected.clients, connected.clientName);
+ }
+ final guestName:InputElement = cast ge("#guestname");
+ if (guestName.value.length > 0) send({
+ type: Login,
+ login: {
+ clientName: guestName.value
+ }
+ });
+ for (message in connected.history) {
+ addMessage(message.name, message.text, message.time);
+ }
+ final list = connected.videoList;
+ if (list.length == 0) return;
+ player.setVideo(list[0]);
+ for (video in connected.videoList) {
+ player.addVideoItem(video);
}
}
@@ -327,8 +279,18 @@ class Main {
function onLogin(data:Array<ClientData>, clientName:String):Void {
updateClients(data);
- personal = clients.getByName(clientName);
- if (personal == null) return;
+ final newPersonal = clients.getByName(clientName);
+ if (newPersonal == null) return;
+ personal = newPersonal;
+ hideGuestLoginPanel();
+ }
+
+ function showGuestLoginPanel():Void {
+ ge("#guestlogin").style.display = "block";
+ ge("#chatline").style.display = "none";
+ }
+
+ function hideGuestLoginPanel():Void {
ge("#guestlogin").style.display = "none";
ge("#chatline").style.display = "block";
}
@@ -376,10 +338,10 @@ class Main {
final list = new StringBuf();
for (client in clients) {
- // final klass = client.isLeader ? "userlist_owner" : "userlist_item";
- final klass = "userlist_item";
+ list.add('<div class="userlist_item">');
if (client.isLeader) list.add('<span class="glyphicon glyphicon-star-empty"></span>');
- list.add('<span class="$klass">${client.name}</span></br>');
+ final klass = client.isAdmin ? "userlist_owner" : "";
+ list.add('<span class="$klass">${client.name}</span></div>');
}
final userlist = ge("#userlist");
userlist.innerHTML = list.toString();
@@ -418,7 +380,7 @@ class Main {
while (msgBuf.children.length > 200) msgBuf.removeChild(msgBuf.firstChild);
msgBuf.scrollTop = msgBuf.scrollHeight;
}
- if (personal != null && personal.name == name) {
+ if (personal.name == name) {
msgBuf.scrollTop = msgBuf.scrollHeight;
}
if (document.hidden && onBlinkTab == null) {
@@ -432,6 +394,13 @@ class Main {
}
}
+ function setLeaderButton(flag:Bool):Void {
+ final leaderBtn = ge("#leader_btn");
+ if (isLeader()) {
+ leaderBtn.classList.add("label-success");
+ } else leaderBtn.classList.remove("label-success");
+ }
+
function escapeRegExp(regex:String):String {
return ~/([.*+?^${}()|[\]\\])/g.replace(regex, "\\$1");
}
diff --git a/src/client/MobileView.hx b/src/client/MobileView.hx
index 558df61..976958b 100644
--- a/src/client/MobileView.hx
+++ b/src/client/MobileView.hx
@@ -1,25 +1,24 @@
package client;
-import js.html.InputElement;
import js.Browser.document;
import client.Main.ge;
class MobileView {
public static function init():Void {
- final mvbtn:InputElement = cast ge("#mv_btn");
- mvbtn.onclick = (e) -> {
- final mobile_view = toggleFullScreen();
- if (mobile_view) {
+ final mvbtn = ge("#mv_btn");
+ mvbtn.onclick = e -> {
+ final mobileView = toggleFullScreen();
+ if (mobileView) {
document.body.classList.add('mobile-view');
- mvbtn.classList.add('label-success');
+ mvbtn.classList.add('active');
final vwrap = ge("#videowrap");
if (vwrap.children[0] == ge("currenttitle")) {
vwrap.appendChild(vwrap.children[0]);
}
} else {
document.body.classList.remove('mobile-view');
- mvbtn.classList.remove('label-success');
+ mvbtn.classList.remove('active');
final vwrap = ge("videowrap");
if (vwrap.children[0] != ge("currenttitle")) {
vwrap.insertBefore(vwrap.children[1],vwrap.children[0]);
diff --git a/src/client/Player.hx b/src/client/Player.hx
index efdb71a..19e0c84 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -66,6 +66,7 @@ class Player {
}
player.innerHTML = "";
player.appendChild(video);
+ ge("#currenttitle").innerHTML = item.title;
}
public function addVideoItem(item:VideoItem):Void {
@@ -108,6 +109,7 @@ class Player {
public function removeVideo():Void {
player.removeChild(video);
video = null;
+ ge("#currenttitle").innerHTML = Lang.get("nothingPlaying");
}
public function removeItem(url:String):Void {
diff --git a/src/client/Split.hx b/src/client/Split.hx
new file mode 100644
index 0000000..5d9d249
--- /dev/null
+++ b/src/client/Split.hx
@@ -0,0 +1,7 @@
+package client;
+
+@:native("Split")
+extern class Split {
+ function new(divs:Array<String>, opts:Dynamic):Void;
+ function setSizes(sizes:Array<Int>):Void;
+}
diff --git a/src/server/Main.hx b/src/server/Main.hx
index bc52718..45be2af 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -21,6 +21,7 @@ class Main {
final wss:WSServer;
final config:Config;
final clients:Array<Client> = [];
+ final freeIds:Array<Int> = [];
final videoList:Array<VideoItem> = [];
final videoTimer = new VideoTimer();
final messages:Array<Message> = [];
@@ -72,8 +73,12 @@ class Main {
function onConnect(ws:WebSocket, req):Void {
final ip = req.connection.remoteAddress;
- trace('Client connected ($ip)');
- final client = new Client(ws, "Unknown", false);
+ final id = freeIds.length > 0 ? freeIds.shift() : clients.length;
+ final name = 'Guest ${id + 1}';
+ trace('$name connected ($ip)');
+ final isAdmin = req.connection.localAddress == ip;
+ final client = new Client(ws, id, name, 0);
+ if (isAdmin) client.group.set(Admin);
clients.push(client);
if (clients.length == 1)
if (videoTimer.isPaused()) videoTimer.play();
@@ -98,6 +103,7 @@ class Main {
});
ws.on("close", err -> {
trace('Client ${client.name} disconnected');
+ sortedPush(freeIds, client.id);
clients.remove(client);
sendClientList();
if (client.isLeader) {
@@ -107,6 +113,17 @@ class Main {
});
}
+ function sortedPush(ids:Array<Int>, id:Int):Void {
+ for (i in 0...ids.length) {
+ final n = ids[i];
+ if (id < n) {
+ ids.insert(i, id);
+ return;
+ }
+ }
+ ids.push(id);
+ }
+
function onMessage(client:Client, data:WsEvent):Void {
switch (data.type) {
case Connected:
@@ -131,11 +148,13 @@ class Main {
case LoginError:
case Logout:
final oldName = client.name;
- client.name = "Unknown";
+ final id = clients.indexOf(client) + 1;
+ client.name = 'Guest $id';
send(client, {
type: data.type,
logout: {
- clientName: oldName,
+ oldClientName: oldName,
+ clientName: client.name,
clients: clientList()
}
});
@@ -150,7 +169,7 @@ class Main {
data.message.clientName = client.name;
final time = "[" + new Date().toTimeString().split(" ")[0] + "] ";
messages.push({text: text, name: client.name, time: time});
- if (messages.length > config.serverChatHistory) messages.pop();
+ if (messages.length > config.serverChatHistory) messages.shift();
broadcast(data);
case AddVideo:
videoList.push(data.addVideo.item);
@@ -202,7 +221,11 @@ class Main {
broadcastExcept(client, data);
case SetLeader:
clients.setLeader(data.setLeader.clientName);
- sendClientList();
+ broadcast({
+ type: SetLeader, setLeader: {
+ clientName: data.setLeader.clientName
+ }
+ });
if (videoList.length == 0) return;
if (!clients.hasLeader()) {
if (videoTimer.isPaused()) videoTimer.play();
@@ -212,6 +235,8 @@ class Main {
}
});
}
+ case ClearChat:
+ if (client.isAdmin) broadcast(data);
}
}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage