aboutsummaryrefslogtreecommitdiffstats
path: root/res
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2023-01-07 08:20:41 +0300
committerRblSb <msrblsb@gmail.com>2023-01-07 08:20:41 +0300
commitb3d825acb9d12208cf44f2d4b385163a86c38695 (patch)
tree09fdb4c54753c30f46f0c2824b550bbfb1217572 /res
parentc677e281d3d74d5925e19eb0479c27f46a3c7857 (diff)
Hints, Open in App button, ui animations
Diffstat (limited to 'res')
-rw-r--r--res/client.js262
-rw-r--r--res/css/des.css99
-rw-r--r--res/index.html114
-rw-r--r--res/langs/en.json11
-rw-r--r--res/langs/ru.json11
5 files changed, 348 insertions, 149 deletions
diff --git a/res/client.js b/res/client.js
index b5be1ad..92ad1c7 100644
--- a/res/client.js
+++ b/res/client.js
@@ -547,20 +547,26 @@ client_Buttons.init = function(main) {
};
var smilesBtn = window.document.querySelector("#smilesbtn");
smilesBtn.onclick = function(e) {
- var smilesWrap = window.document.querySelector("#smileswrap");
- if(smilesWrap.children.length == 0) {
+ var wrap = window.document.querySelector("#smiles-wrap");
+ var list = window.document.querySelector("#smiles-list");
+ if(list.children.length == 0) {
return;
}
if(smilesBtn.classList.toggle("active")) {
- smilesWrap.style.display = "grid";
+ wrap.style.display = "block";
+ var tmp = client_Buttons.outerHeight(list);
+ wrap.style.height = tmp + "px";
} else {
- smilesWrap.style.display = "none";
+ wrap.style.height = "0";
+ wrap.addEventListener("transitionend",function(e) {
+ return wrap.style.display = "none";
+ },{ once : true});
}
- if(smilesWrap.firstElementChild.dataset.src == null) {
+ if(list.firstElementChild.dataset.src == null) {
return;
}
var _g = 0;
- var _g1 = smilesWrap.children;
+ var _g1 = list.children;
while(_g < _g1.length) {
var child = _g1[_g];
++_g;
@@ -595,13 +601,19 @@ client_Buttons.init = function(main) {
userlistToggle.onclick = function(e) {
var icon = userlistToggle.firstElementChild;
var isHidden = icon.getAttribute("name") == "chevron-forward";
+ var wrap = window.document.querySelector("#userlist-wrap");
var style = window.document.querySelector("#userlist").style;
if(isHidden) {
- style.display = "block";
icon.setAttribute("name","chevron-down");
+ style.display = "block";
+ var tmp = client_Buttons.outerHeight(wrap.firstElementChild);
+ wrap.style.height = tmp + "px";
+ wrap.style.marginBottom = "1rem";
} else {
- style.display = "none";
icon.setAttribute("name","chevron-forward");
+ style.display = "none";
+ wrap.style.height = "0";
+ wrap.style.marginBottom = "0rem";
}
client_Buttons.settings.isUserListHidden = !isHidden;
client_Settings.write(client_Buttons.settings);
@@ -609,7 +621,14 @@ client_Buttons.init = function(main) {
window.document.querySelector("#usercount").onclick = userlistToggle.onclick;
if(client_Buttons.settings.isUserListHidden) {
userlistToggle.onclick();
+ } else {
+ var wrap = window.document.querySelector("#userlist-wrap");
+ var tmp = client_Buttons.outerHeight(wrap.firstElementChild);
+ wrap.style.height = tmp + "px";
}
+ haxe_Timer.delay(function() {
+ window.document.querySelector("#userlist-wrap").style.transition = "200ms";
+ },0);
var toggleSynch = window.document.querySelector("#togglesynch");
toggleSynch.onclick = function(e) {
var icon = toggleSynch.firstElementChild;
@@ -672,11 +691,21 @@ client_Buttons.init = function(main) {
};
var showMediaUrl = window.document.querySelector("#showmediaurl");
showMediaUrl.onclick = function(e) {
- client_Buttons.showPlayerGroup(showMediaUrl);
+ if(client_Buttons.showPlayerGroup(showMediaUrl)) {
+ haxe_Timer.delay(function() {
+ window.document.querySelector("#addfromurl").scrollIntoView();
+ window.document.querySelector("#mediaurl").focus();
+ },100);
+ }
};
var showCustomEmbed = window.document.querySelector("#showcustomembed");
showCustomEmbed.onclick = function(e) {
- client_Buttons.showPlayerGroup(showCustomEmbed);
+ if(client_Buttons.showPlayerGroup(showCustomEmbed)) {
+ haxe_Timer.delay(function() {
+ window.document.querySelector("#customembed").scrollIntoView();
+ window.document.querySelector("#customembed-title").focus();
+ },100);
+ }
};
var mediaUrl = window.document.querySelector("#mediaurl");
mediaUrl.oninput = function() {
@@ -692,7 +721,15 @@ client_Buttons.init = function(main) {
};
var showOptions = window.document.querySelector("#showoptions");
showOptions.onclick = function(e) {
- return client_Buttons.toggleGroup(showOptions);
+ var isActive = client_Buttons.toggleGroup(showOptions);
+ var tmp = isActive ? "1" : "0";
+ window.document.querySelector("#optionsPanel").style.opacity = tmp;
+ return haxe_Timer.delay(function() {
+ if(showOptions.classList.contains("active") != isActive) {
+ return;
+ }
+ window.document.querySelector("#optionsPanel").classList.toggle("collapse",!isActive);
+ },isActive ? 0 : 200);
};
window.document.querySelector("#exitBtn").onclick = function(e) {
if((main.personal.group & 2) != 0) {
@@ -721,14 +758,27 @@ client_Buttons.showPlayerGroup = function(el) {
}
client_Buttons.toggleGroup(group);
}
- client_Buttons.toggleGroup(el);
+ return client_Buttons.toggleGroup(el);
};
client_Buttons.toggleGroup = function(el) {
el.classList.toggle("collapsed");
var id = el.dataset.target;
- window.document.querySelector(id).classList.toggle("collapse");
+ var target = window.document.querySelector(id);
+ if(target.classList.toggle("collapse")) {
+ target.style.height = "0";
+ } else {
+ if(target.style.height == "") {
+ target.style.height = "0";
+ }
+ var tmp = client_Buttons.outerHeight(target.firstElementChild);
+ target.style.height = tmp + "px";
+ }
return el.classList.toggle("active");
};
+client_Buttons.outerHeight = function(el) {
+ var style = window.getComputedStyle(el);
+ return el.getBoundingClientRect().height + parseFloat(style.marginTop) + parseFloat(style.marginBottom);
+};
client_Buttons.swapPlayerAndChat = function() {
client_Buttons.settings.isSwapped = window.document.querySelector("body").classList.toggle("swap");
var sizes = window.document.body.style.gridTemplateColumns.split(" ");
@@ -1157,7 +1207,7 @@ var client_Main = function() {
if(this.host == "") {
this.host = "localhost";
}
- client_Settings.init({ version : 3, name : "", hash : "", isExtendedPlayer : false, playerSize : 1, chatSize : 300, synchThreshold : 2, isSwapped : false, isUserListHidden : true, latestLinks : [], latestSubs : [], hotkeysEnabled : true},$bind(this,this.settingsPatcher));
+ client_Settings.init({ version : 4, name : "", hash : "", isExtendedPlayer : false, playerSize : 1, chatSize : 300, synchThreshold : 2, isSwapped : false, isUserListHidden : true, latestLinks : [], latestSubs : [], hotkeysEnabled : true, showHintList : true},$bind(this,this.settingsPatcher));
this.settings = client_Settings.read();
this.initListeners();
this.onTimeGet = new haxe_Timer(this.settings.synchThreshold * 1000);
@@ -1180,38 +1230,40 @@ client_Main.__name__ = true;
client_Main.main = function() {
new client_Main();
};
-client_Main.serverMessage = function(type,text,isText) {
+client_Main.chatMessageConnected = function() {
+ var div = window.document.createElement("div");
+ div.className = "server-msg-reconnect";
+ div.textContent = Lang.get("msgConnected");
+ var msgBuf = window.document.querySelector("#messagebuffer");
+ msgBuf.appendChild(div);
+ msgBuf.scrollTop = msgBuf.scrollHeight;
+};
+client_Main.chatMessageDisconnected = function() {
+ var div = window.document.createElement("div");
+ div.className = "server-msg-disconnect";
+ div.textContent = Lang.get("msgDisconnected");
+ var msgBuf = window.document.querySelector("#messagebuffer");
+ msgBuf.appendChild(div);
+ msgBuf.scrollTop = msgBuf.scrollHeight;
+};
+client_Main.serverMessage = function(text,isText,withTimestamp) {
+ if(withTimestamp == null) {
+ withTimestamp = true;
+ }
if(isText == null) {
isText = true;
}
- var msgBuf = window.document.querySelector("#messagebuffer");
var div = window.document.createElement("div");
var time = HxOverrides.dateStr(new Date()).split(" ")[1];
- switch(type) {
- case 1:
- div.className = "server-msg-reconnect";
- div.textContent = Lang.get("msgConnected");
- break;
- case 2:
- div.className = "server-msg-disconnect";
- div.textContent = Lang.get("msgDisconnected");
- break;
- case 3:
- div.className = "server-whisper";
- div.textContent = time + text + " " + Lang.get("entered");
- break;
- case 4:
- div.className = "server-whisper";
- div.innerHTML = "<div class=\"head\">\n\t\t\t\t\t<div class=\"server-whisper\"></div>\n\t\t\t\t\t<span class=\"timestamp\">" + time + "</span>\n\t\t\t\t</div>";
- var textDiv = div.querySelector(".server-whisper");
- if(isText) {
- textDiv.textContent = text;
- } else {
- textDiv.innerHTML = text;
- }
- break;
- default:
+ div.className = "server-whisper";
+ div.innerHTML = "<div class=\"head\">\n\t\t\t<div class=\"server-whisper\"></div>\n\t\t\t<span class=\"timestamp\">" + (withTimestamp ? time : "") + "</span>\n\t\t</div>";
+ var textDiv = div.querySelector(".server-whisper");
+ if(isText) {
+ textDiv.textContent = text;
+ } else {
+ textDiv.innerHTML = text;
}
+ var msgBuf = window.document.querySelector("#messagebuffer");
msgBuf.appendChild(div);
msgBuf.scrollTop = msgBuf.scrollHeight;
};
@@ -1225,6 +1277,9 @@ client_Main.prototype = {
data.latestSubs = [];
break;
case 3:
+ data.showHintList = true;
+ break;
+ case 4:
throw haxe_Exception.thrown("skipped version " + version);
default:
throw haxe_Exception.thrown("skipped version " + version);
@@ -1252,12 +1307,12 @@ client_Main.prototype = {
this.ws = new WebSocket("" + protocol + "//" + this.host + colonPort + path);
this.ws.onmessage = $bind(this,this.onMessage);
this.ws.onopen = function() {
- client_Main.serverMessage(1);
+ client_Main.chatMessageConnected();
return _gthis.isConnected = true;
};
this.ws.onclose = function() {
if(_gthis.isConnected) {
- client_Main.serverMessage(2);
+ client_Main.chatMessageDisconnected();
}
_gthis.isConnected = false;
_gthis.player.pause();
@@ -1414,7 +1469,7 @@ client_Main.prototype = {
}
this.player.getVideoData({ url : url, atEnd : atEnd},function(data) {
if(data.duration == 0) {
- client_Main.serverMessage(4,Lang.get("addVideoError"));
+ client_Main.serverMessage(Lang.get("addVideoError"));
return;
}
if(data.title == null) {
@@ -1443,7 +1498,7 @@ client_Main.prototype = {
var isTemp = window.document.querySelector("#customembed").querySelector(".add-temp").checked;
this.player.getIframeData({ url : iframe, atEnd : atEnd},function(data) {
if(data.duration == 0) {
- client_Main.serverMessage(4,Lang.get("addVideoError"));
+ client_Main.serverMessage(Lang.get("addVideoError"));
return;
}
if(title.length > 0) {
@@ -1492,7 +1547,7 @@ client_Main.prototype = {
var data = JSON.parse(e.data);
if(this.config != null && this.config.isVerbose) {
var t = data.type;
- haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 390, className : "client.Main", methodName : "onMessage", customParams : [Reflect.field(data,t.charAt(0).toLowerCase() + HxOverrides.substr(t,1,null))]});
+ haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 394, className : "client.Main", methodName : "onMessage", customParams : [Reflect.field(data,t.charAt(0).toLowerCase() + HxOverrides.substr(t,1,null))]});
}
client_JsApi.fireOnceEvent(data);
switch(data.type) {
@@ -1627,7 +1682,7 @@ client_Main.prototype = {
break;
case "ServerMessage":
var id = data.serverMessage.textId;
- client_Main.serverMessage(4,id == "usernameError" ? StringTools.replace(Lang.get(id),"$MAX","" + this.config.maxLoginLength) : Lang.get(id));
+ client_Main.serverMessage(id == "usernameError" ? StringTools.replace(Lang.get(id),"$MAX","" + this.config.maxLoginLength) : Lang.get(id));
break;
case "SetLeader":
ClientTools.setLeader(this.clients,data.setLeader.clientName);
@@ -1713,7 +1768,7 @@ client_Main.prototype = {
this.setLeaderButton((this.personal.group & 4) != 0);
this.setPlaylistLock(connected.isPlaylistOpen);
this.clearChat();
- client_Main.serverMessage(1);
+ client_Main.chatMessageConnected();
var _g = 0;
var _g1 = connected.history;
while(_g < _g1.length) {
@@ -1723,6 +1778,80 @@ client_Main.prototype = {
}
this.player.setItems(connected.videoList,connected.itemPos);
this.onUserGroupChanged();
+ if(this.settings.showHintList) {
+ this.showChatHintList();
+ }
+ }
+ ,showChatHintList: function() {
+ var _gthis = this;
+ var text = Lang.get("hintListStart");
+ var addVideos = "<button id=\"addVideosHintButton\">" + Lang.get("addVideos") + "</button>";
+ text += "</br>" + StringTools.replace(Lang.get("hintListAddVideo"),"$addVideos",addVideos);
+ var requestLeader = "<button id=\"requestLeaderHintButton\">" + Lang.get("requestLeader") + "</button>";
+ text += "</br>" + StringTools.replace(Lang.get("hintListRequestLeader"),"$requestLeader",requestLeader);
+ if(client_Utils.isTouch()) {
+ text += " " + Lang.get("hintListRequestLeaderTouch");
+ } else {
+ text += " " + Lang.get("hintListRequestLeaderMouse");
+ }
+ if(client_Utils.isAndroid()) {
+ var openInAppLink = "<button id=\"openInApp\">" + Lang.get("openInApp") + "</button>";
+ text += "</br>" + StringTools.replace(Lang.get("hintListOpenInApp"),"$openInApp",openInAppLink);
+ }
+ var hideThisMessage = "<button id=\"hideHintList\">" + Lang.get("hideThisMessage") + "</button>";
+ text += "</br>" + StringTools.replace(Lang.get("hintListHide"),"$hideThisMessage",hideThisMessage);
+ client_Main.serverMessage(text,false,false);
+ window.document.querySelector("#addVideosHintButton").onclick = function(e) {
+ var addBtn = window.document.querySelector("#showmediaurl");
+ addBtn.scrollIntoView();
+ return haxe_Timer.delay(function() {
+ if(!window.document.querySelector("#addfromurl").classList.contains("collapse")) {
+ window.document.querySelector("#mediaurl").focus();
+ return;
+ }
+ addBtn.onclick();
+ },300);
+ };
+ window.document.querySelector("#requestLeaderHintButton").onclick = function(e) {
+ window.scrollTo(0,0);
+ if(client_Utils.isTouch()) {
+ window.document.querySelector("#leader_btn").classList.add("hint");
+ haxe_Timer.delay(function() {
+ window.document.querySelector("#leader_btn").classList.remove("hint");
+ },1000);
+ }
+ };
+ window.document.querySelector("#requestLeaderHintButton").onpointerenter = function(e) {
+ if(client_Utils.isTouch()) {
+ return;
+ }
+ window.document.querySelector("#leader_btn").classList.add("hint");
+ };
+ window.document.querySelector("#requestLeaderHintButton").onpointerleave = function(e) {
+ window.document.querySelector("#leader_btn").classList.remove("hint");
+ };
+ if(client_Utils.isAndroid()) {
+ window.document.querySelector("#openInApp").onclick = function(e) {
+ var isRedirected = false;
+ window.addEventListener("blur",function(e) {
+ isRedirected = true;
+ return isRedirected;
+ },{ once : true});
+ window.setTimeout(function() {
+ if(isRedirected || window.document.hidden) {
+ return;
+ }
+ window.location.href = "https://github.com/RblSb/SyncTubeApp#readme";
+ },500);
+ window.location.href = "synctube://" + $global.location.href;
+ return false;
+ };
+ }
+ window.document.querySelector("#hideHintList").onclick = function(e) {
+ window.document.querySelector("#hideHintList").parentElement.remove();
+ _gthis.settings.showHintList = false;
+ client_Settings.write(_gthis.settings);
+ };
}
,onUserGroupChanged: function() {
var button = window.document.querySelector("#queue_next");
@@ -1789,18 +1918,18 @@ client_Main.prototype = {
this.filters.push({ regex : new EReg("(^| )" + this.escapeRegExp(emote.name) + "(?!\\S)","g"), replace : "$1<" + tag + " class=\"channel-emote\" src=\"" + emote.image + "\" title=\"" + emote.name + "\"/>"});
}
window.document.querySelector("#smilesbtn").classList.remove("active");
- var smilesWrap = window.document.querySelector("#smileswrap");
- smilesWrap.style.display = "none";
- smilesWrap.onclick = function(e) {
+ window.document.querySelector("#smiles-wrap").style.display = "none";
+ var smilesList = window.document.querySelector("#smiles-list");
+ smilesList.onclick = function(e) {
var el = e.target;
- if(el == smilesWrap) {
+ if(el == smilesList) {
return;
}
var form = window.document.querySelector("#chatline");
form.value += " " + el.title;
form.focus();
};
- smilesWrap.textContent = "";
+ smilesList.textContent = "";
var _g = 0;
var _g1 = config.emotes;
while(_g < _g1.length) {
@@ -1811,7 +1940,7 @@ client_Main.prototype = {
el.className = "smile-preview";
el.dataset.src = emote.image;
el.title = emote.name;
- smilesWrap.appendChild(el);
+ smilesList.appendChild(el);
}
}
,onLogin: function(data,clientName) {
@@ -1992,6 +2121,9 @@ client_Main.prototype = {
case "fb":case "flashback":
this.send({ type : "Flashback"});
return false;
+ case "help":
+ this.showChatHintList();
+ return true;
case "kick":
this.mergeRedundantArgs(args,0,1);
this.send({ type : "KickClient", kickClient : { name : args[0]}});
@@ -2071,12 +2203,7 @@ client_Main.prototype = {
this.onBlinkTab.run();
}
,setLeaderButton: function(flag) {
- var leaderBtn = window.document.querySelector("#leader_btn");
- if(flag) {
- leaderBtn.classList.add("success-bg");
- } else {
- leaderBtn.classList.remove("success-bg");
- }
+ window.document.querySelector("#leader_btn").classList.toggle("success-bg",flag);
}
,setPlaylistLock: function(isOpen) {
this.isPlaylistOpen = isOpen;
@@ -2084,13 +2211,11 @@ client_Main.prototype = {
var icon = lockPlaylist.firstElementChild;
if(isOpen) {
lockPlaylist.title = Lang.get("playlistOpen");
- lockPlaylist.classList.add("btn-success");
lockPlaylist.classList.add("success");
lockPlaylist.classList.remove("danger");
icon.setAttribute("name","lock-open");
} else {
lockPlaylist.title = Lang.get("playlistLocked");
- lockPlaylist.classList.add("btn-danger");
lockPlaylist.classList.add("danger");
lockPlaylist.classList.remove("success");
icon.setAttribute("name","lock-closed");
@@ -2339,11 +2464,7 @@ client_Player.prototype = {
var btn = item.querySelector(".qbtn-tmp");
btn.title = isTemp ? Lang.get("makePermanent") : Lang.get("makeTemporary");
btn.firstElementChild.setAttribute("name",isTemp ? "lock-open" : "lock-closed");
- if(isTemp) {
- item.classList.add("queue_temp");
- } else {
- item.classList.remove("queue_temp");
- }
+ item.classList.toggle("queue_temp",isTemp);
}
,removeItem: function(url) {
this.removeElementItem(url);
@@ -2615,7 +2736,7 @@ client_Player.prototype = {
}
};
http.onError = function(msg) {
- haxe_Log.trace(msg,{ fileName : "src/client/Player.hx", lineNumber : 477, className : "client.Player", methodName : "skipAd"});
+ haxe_Log.trace(msg,{ fileName : "src/client/Player.hx", lineNumber : 476, className : "client.Player", methodName : "skipAd"});
};
http.request();
}
@@ -2679,6 +2800,9 @@ client_Utils.isIOS = function() {
return true;
}
};
+client_Utils.isAndroid = function() {
+ return $global.navigator.userAgent.toLowerCase().indexOf("android") > -1;
+};
client_Utils.nodeFromString = function(div) {
var wrapper = window.document.createElement("div");
wrapper.innerHTML = div;
@@ -3208,7 +3332,7 @@ client_players_RawSubs.convertAssTime = function(time) {
};
client_players_RawSubs.isProxyError = function(text) {
if(StringTools.startsWith(text,"Proxy error:")) {
- client_Main.serverMessage(4,"Failed to add subs: proxy error");
+ client_Main.serverMessage("Failed to add subs: proxy error");
haxe_Log.trace("Failed to add subs: " + text,{ fileName : "src/client/players/RawSubs.hx", lineNumber : 191, className : "client.players.RawSubs", methodName : "isProxyError"});
return true;
}
@@ -3378,7 +3502,7 @@ client_players_Youtube.prototype = {
loadJson(dataUrl);
}
,youtubeApiError: function(error) {
- client_Main.serverMessage(4,"Error " + error.code + ": " + error.message,false);
+ client_Main.serverMessage("Error " + error.code + ": " + error.message,false);
}
,getRemoteDataFallback: function(url,callback) {
var _gthis = this;
diff --git a/res/css/des.css b/res/css/des.css
index aa02177..968ad39 100644
--- a/res/css/des.css
+++ b/res/css/des.css
@@ -11,6 +11,7 @@
--foreground: #bbb;
--accent: #0055ff;
--success: #009632;
+ --leader-hint: #00963288;
--warning: #ffb800;
--error: #ff0800;
--border: #333;
@@ -19,6 +20,8 @@
html {
box-sizing: border-box;
+ scroll-behavior: smooth;
+ -webkit-tap-highlight-color: transparent;
}
*,
@@ -122,6 +125,10 @@ button {
button:not(:first-child) {
margin-left: .5rem;
}
+.server-whisper button {
+ margin-left: 0;
+ font-style: italic;
+}
button:hover,
button.active {
@@ -129,6 +136,16 @@ button.active {
color: var(--foreground);
}
+/* Disable hover on touch devices */
+@media (hover: none) {
+ button:hover:not(.active) {
+ background-color: var(--background-chat);
+ }
+ .info header button:hover:not(.active) {
+ background-color: transparent;
+ }
+}
+
button:hover ion-icon,
button.active ion-icon,
button:focus,
@@ -159,6 +176,18 @@ button span {
pointer-events: none;
}
+#leader_btn {
+ border: .125rem solid;
+ border-color: transparent;
+ transition: border-color ease-in-out 800ms;
+}
+#leader_btn.hint {
+ border-radius: .5rem;
+ border: .125rem solid;
+ color: var(--foreground);
+ border-color: var(--leader-hint);
+}
+
/* Input */
label {
@@ -227,14 +256,19 @@ button.danger-bg:focus {
text-align: center;
}
-.collapse {
- display: none;
- visibility: hidden;
+.collapsible {
+ overflow: hidden;
+ transition: height 200ms;
}
-.collapse.in {
- display: block;
- visibility: visible;
+.collapse-list {
+ padding: 1rem;
+ margin: 0 auto;
+ max-width: 32rem;
+}
+
+.collapse {
+ height: 0;
}
.display-flex {
@@ -374,15 +408,8 @@ header h4 {
flex-grow: 1;
}
-#addfromurl,
-#customembed {
- padding: 1rem;
- margin: 0 auto;
- max-width: 32rem;
-}
-
-#addfromurl>*,
-#customembed>* {
+#addfromurl>*>*,
+#customembed>*>* {
margin-bottom: 1rem;
}
@@ -396,8 +423,8 @@ header h4 {
flex-grow: 2;
}
-#customembed>input,
-#customembed>textarea {
+#customembed>*>input,
+#customembed>*>textarea {
display: block;
width: 100%;
}
@@ -481,16 +508,23 @@ footer#footer {
/* Users online */
-#userlist {
+#userlist-wrap {
overflow-y: auto;
background-color: var(--background-video);
border-right: 0;
- padding: 1rem;
border-radius: 1rem;
- height: 12rem;
+ flex-shrink: 0;
+
+ transition: 0ms;
+ height: 0;
margin-bottom: 1rem;
}
+#userlist {
+ padding: 1rem;
+ height: 11rem;
+}
+
.userlist_item {
display: flex;
align-items: center;
@@ -544,19 +578,21 @@ footer#footer {
left: 1rem;
bottom: 1rem;
right: 1rem;
+ transition: opacity 200ms;
+ opacity: 0;
}
-#optionsPanel div {
+#optionsList div {
margin-bottom: .5rem;
}
-#optionsPanel div:not(:first-child) {
+#optionsList div:not(:first-child) {
border-top: .063rem solid;
border-color: var(--border);
padding-top: 1rem;
}
-#optionsPanel li button {
+#optionsList li button {
padding: 1rem 0;
display: flex;
align-items: stretch;
@@ -565,7 +601,7 @@ footer#footer {
text-align: left;
}
-#optionsPanel li button:hover {
+#optionsList li button:hover {
background-color: var(--background-chat);
}
@@ -631,15 +667,20 @@ footer#footer {
/* Emotes */
-#smileswrap {
+#smiles-wrap {
display: none;
- background: rgba(0, 0, 0, 0.7);
+ height: 0;
width: 100%;
- height: 12rem;
- padding: 1rem;
+ background: rgba(0, 0, 0, 0.7);
border-radius: 1rem;
overflow-y: scroll;
text-align: center;
+}
+
+#smiles-list {
+ display: grid;
+ height: 12rem;
+ padding: 1rem;
grid-template-columns: repeat(auto-fit, minmax(4rem, 1fr));
grid-gap: .5rem;
gap: .5rem;
@@ -807,6 +848,6 @@ html {
}
.mobile-view #optionsPanel {
- top: 2rem;
+ top: 2.2rem;
bottom: 0;
}
diff --git a/res/index.html b/res/index.html
index 3892a41..20df11c 100644
--- a/res/index.html
+++ b/res/index.html
@@ -82,37 +82,41 @@
</span>
</div>
<!-- Add video -->
- <div class="collapse" id="addfromurl" aria-expanded="false">
- <div class="display-flex">
- <button id="insert_template" title="${addTemplateUrl}">
- <div>&gt;</div>
- </button>
- <input id="mediaurl" type="text" placeholder="${addVideoFromUrl}">
- </div>
- <div id="mediatitleblock" class="display-flex" style="display: none;">
- <input id="mediatitle" type="text" placeholder="${optionalTitle}">
- </div>
- <div id="subsurlblock" class="display-flex" style="display: none;">
- <input id="subsurl" type="text" placeholder="${subtitlesUrlOptional}">
- </div>
- <div>
- <label>
- <input class="add-temp" type="checkbox" checked="checked">${addAsTemporary}
- </label>
- <button id="queue_next">${queueNext}</button>
- <button id="queue_end">${queueLast}</button>
+ <div id="addfromurl" class="collapsible collapse" aria-expanded="false">
+ <div class="collapse-list">
+ <div class="display-flex">
+ <button id="insert_template" title="${addTemplateUrl}">
+ <div>&gt;</div>
+ </button>
+ <input id="mediaurl" type="text" placeholder="${addVideoFromUrl}">
+ </div>
+ <div id="mediatitleblock" class="display-flex" style="display: none;">
+ <input id="mediatitle" type="text" placeholder="${optionalTitle}">
+ </div>
+ <div id="subsurlblock" class="display-flex" style="display: none;">
+ <input id="subsurl" type="text" placeholder="${subtitlesUrlOptional}">
+ </div>
+ <div>
+ <label>
+ <input class="add-temp" type="checkbox" checked="checked">${addAsTemporary}
+ </label>
+ <button id="queue_next">${queueNext}</button>
+ <button id="queue_end">${queueLast}</button>
+ </div>
</div>
</div>
- <div class="collapse" id="customembed" aria-expanded="false">
- <input id="customembed-title" type="text" placeholder="${optionalTitle}">
- <textarea id="customembed-content" rows="5"
- placeholder="${pasteEmbedCodeAndClick} '${queueNext}' ${or} '${queueLast}'. &#10;${acceptableEmbedCodesAre} &lt;iframe&gt; ${or} &lt;object&gt;. &#10;${customEmbedsCannotBeSynchronized}."></textarea>
- <div>
- <label>
- <input class="add-temp" type="checkbox" checked="checked">${addAsTemporary}
- </label>
- <button id="ce_queue_next">${queueNext}</button>
- <button id="ce_queue_end">${queueLast}</button>
+ <div class="collapsible collapse" id="customembed" aria-expanded="false">
+ <div class="collapse-list">
+ <input id="customembed-title" type="text" placeholder="${optionalTitle}">
+ <textarea id="customembed-content" rows="5"
+ placeholder="${pasteEmbedCodeAndClick} '${queueNext}' ${or} '${queueLast}'. &#10;${acceptableEmbedCodesAre} &lt;iframe&gt; ${or} &lt;object&gt;. &#10;${customEmbedsCannotBeSynchronized}."></textarea>
+ <div>
+ <label>
+ <input class="add-temp" type="checkbox" checked="checked">${addAsTemporary}
+ </label>
+ <button id="ce_queue_next">${queueNext}</button>
+ <button id="ce_queue_end">${queueLast}</button>
+ </div>
</div>
</div>
<!-- Queue -->
@@ -142,35 +146,41 @@
<span>
<button id="leader_btn" class="unselectable" title="${leaderDesc}">${leader}</button>
<!-- Settings button -->
- <button id="showoptions" class="collapsed" data-toggle="collapse" data-target="#optionsPanel"
+ <button id="showoptions" class="collapsed" data-toggle="collapse" data-target="#optionsList"
aria-expanded="false">
<ion-icon name="settings-sharp"></ion-icon>
</button>
</span>
</div>
<!-- User list -->
- <div id="userlist"></div>
+ <div id="userlist-wrap" class="collapsible">
+ <div id="userlist"></div>
+ </div>
<!-- Settings list -->
<ul id="optionsPanel" class="collapse" aria-expanded="false">
- <div>
- <h4>${account}</h4>
- <li><button id="exitBtn">${login}</button></li>
- </div>
- <div>
- <h4>${general}</h4>
- <li><button id="hotkeysBtn"><span>${hotkeys}</span></button></li>
- <li><button id="swapLayoutBtn" title="${swapLayout}"><span>${swapLayout}</span></button></li>
- </div>
- <div>
- <h4>${video}</h4>
- <li><button id="synchThresholdBtn"><span>${synchThreshold}</span></button></li>
- <li><button id="setVideoUrlBtn"><span>${setVideoUrl}</span></button></li>
- <li><button id="selectLocalVideoBtn"><span>${selectLocalVideo}</span></button></li>
- <li><button id="removeVideoBtn"><span>${removeVideo}</span></button></li>
- </div>
- <div id="adminMenu" style="display: none;">
- <h4>${chat}</h4>
- <li><button id="clearchatbtn"><span>${clearChat}</span></button></li>
+ <div id="optionsList" class="collapse">
+ <div>
+ <div>
+ <h4>${account}</h4>
+ <li><button id="exitBtn">${login}</button></li>
+ </div>
+ <div>
+ <h4>${general}</h4>
+ <li><button id="hotkeysBtn"><span>${hotkeys}</span></button></li>
+ <li><button id="swapLayoutBtn" title="${swapLayout}"><span>${swapLayout}</span></button></li>
+ </div>
+ <div>
+ <h4>${video}</h4>
+ <li><button id="synchThresholdBtn"><span>${synchThreshold}</span></button></li>
+ <li><button id="setVideoUrlBtn"><span>${setVideoUrl}</span></button></li>
+ <li><button id="selectLocalVideoBtn"><span>${selectLocalVideo}</span></button></li>
+ <li><button id="removeVideoBtn"><span>${removeVideo}</span></button></li>
+ </div>
+ <div id="adminMenu" style="display: none;">
+ <h4>${chat}</h4>
+ <li><button id="clearchatbtn"><span>${clearChat}</span></button></li>
+ </div>
+ </div>
</div>
</ul>
<!-- Messages -->
@@ -182,7 +192,9 @@
<ion-icon name="happy"></ion-icon>
</button>
</div>
- <div id="smileswrap"></div>
+ <div id="smiles-wrap" class="collapsible">
+ <div id="smiles-list"></div>
+ </div>
<!-- Guest login -->
<div id="guestlogin" style="display: none;">
<label>${enterAsGuest}</label>
diff --git a/res/langs/en.json b/res/langs/en.json
index e7ea8f4..90a77aa 100644
--- a/res/langs/en.json
+++ b/res/langs/en.json
@@ -5,6 +5,17 @@
"joined": "joined",
"online": "online",
"nothingPlaying": "Nothing Playing",
+ "hintListStart": "Welcome to SyncTube! Here you can:",
+ "hintListAddVideo": "$addVideos to watch together",
+ "hintListRequestLeader": "$requestLeader to pause and rewind videos for everyone",
+ "hintListRequestLeaderMouse": "(also use right mouse button for quick pause)",
+ "hintListRequestLeaderTouch": "(also use long tap for quick pause)",
+ "hintListOpenInApp": "$openInApp this server for better Android experience",
+ "hintListHide": "$hideThisMessage and send <b>/help</b> in chat to see it again",
+ "addVideos": "Add Videos",
+ "requestLeader": "Request Leader",
+ "openInApp": "Open in App",
+ "hideThisMessage": "Hide this message",
"usernameError": "Username length must be from 1 to $MAX characters and don't repeat another's. Characters &^<>'\" are not allowed.",
"passwordMatchError": "Wrong password.",
"accessError": "Access Error.",
diff --git a/res/langs/ru.json b/res/langs/ru.json
index 637e883..7615efd 100644
--- a/res/langs/ru.json
+++ b/res/langs/ru.json
@@ -5,6 +5,17 @@
"joined": "вошел",
"online": "онлайн",
"nothingPlaying": "Ничего не играет",
+ "hintListStart": "Добро пожаловать на SyncTube! Здесь вы можете:",
+ "hintListAddVideo": "$addVideos для совместного просмотра",
+ "hintListRequestLeader": "$requestLeader для всеобщей паузы и перемотки видео",
+ "hintListRequestLeaderMouse": "(кстати, правая кнопка мыши сразу сделает паузу)",
+ "hintListRequestLeaderTouch": "(кстати, удерживайте кнопку для быстрой паузы)",
+ "hintListOpenInApp": "$openInApp этот сервер для лучшего опыта на Android",
+ "hintListHide": "$hideThisMessage и отправлять <b>`/help`</b> в чат чтобы прочесть его снова",
+ "addVideos": "Добавлять видео",
+ "requestLeader": "Запрашивать лидера",
+ "openInApp": "Открыть в приложении",
+ "hideThisMessage": "Скрыть это сообщение",
"usernameError": "Ник должен быть от 1 до $MAX символов и не повторять чужие. Символы &^<>'\" запрещены.",
"passwordMatchError": "Неправильный пароль.",
"accessError": "Ошибка доступа.",
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage