A lot of updates
parent
6d2ecc3c69
commit
e9384bcf18
|
@ -68,6 +68,8 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
||||||
|
@ -114,39 +116,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.marker-text-unread {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 1px;
|
|
||||||
background-color: #a814147F;
|
|
||||||
|
|
||||||
opacity: 1;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
width: 24px;
|
|
||||||
|
|
||||||
background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */
|
|
||||||
background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */
|
|
||||||
background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
&.hidden {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include transition(opacity $button_hover_animation_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-type {
|
.channel-type {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -308,6 +277,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.channel .container-channel, &.client, &.server {
|
||||||
|
.marker-text-unread {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 1px;
|
||||||
|
background-color: #a814147F;
|
||||||
|
|
||||||
|
opacity: 1;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
width: 24px;
|
||||||
|
|
||||||
|
background: -moz-linear-gradient(left, rgba(168,20,20,.18) 0%, rgba(168,20,20,0) 100%); /* FF3.6-15 */
|
||||||
|
background: -webkit-linear-gradient(left, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* Chrome10-25,Safari5.1-6 */
|
||||||
|
background: linear-gradient(to right, rgba(168,20,20,.18) 0%,rgba(168,20,20,0) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include transition(opacity $button_hover_animation_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
background-color: lightgray;
|
background-color: lightgray;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,6 +474,10 @@ $client_info_avatar_size: 10em;
|
||||||
//padding: .5em 1em;
|
//padding: .5em 1em;
|
||||||
color: #565353;
|
color: #565353;
|
||||||
|
|
||||||
|
&.type-disconnect_self {
|
||||||
|
color: #565353; /* not really critical at all */
|
||||||
|
}
|
||||||
|
|
||||||
&.type-new {
|
&.type-new {
|
||||||
color: darkred; /* TODO: Evaluate color */
|
color: darkred; /* TODO: Evaluate color */
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,16 @@ $animation_length: .5s;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a[href], a[href]:visited {
|
a[href], a[href]:visited {
|
||||||
color: #353535!important;
|
color: #353535!important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,293 @@
|
||||||
|
@import "mixin";
|
||||||
|
@import "properties";
|
||||||
|
|
||||||
|
//TODO: Resize style!
|
||||||
|
.modal-body.modal-ban-client {
|
||||||
|
padding: 0!important;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column!important;
|
||||||
|
justify-content: stretch!important;
|
||||||
|
|
||||||
|
//min-width: 30em!important;
|
||||||
|
max-height: calc(100vh - 10em);
|
||||||
|
width: 40em;
|
||||||
|
|
||||||
|
min-height: 20em;
|
||||||
|
|
||||||
|
.container-tooltip {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
width: 1.6em;
|
||||||
|
|
||||||
|
margin-left: .5em;
|
||||||
|
margin-right: .25em;
|
||||||
|
|
||||||
|
font-size: .9em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
|
||||||
|
align-self: center;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-info {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
padding: .5em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
min-width: 4em;
|
||||||
|
width: 10em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #557edc;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-height: 2em;
|
||||||
|
|
||||||
|
padding: .5em;
|
||||||
|
|
||||||
|
border-radius: 0.2em;
|
||||||
|
border: 1px solid #111112;
|
||||||
|
background-color: #121213;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
@include chat-scrollbar-vertical();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-duration {
|
||||||
|
margin: 1em;
|
||||||
|
margin-top: 0em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-value {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.input-boxed.value {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
min-width: 6em;
|
||||||
|
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 7em;
|
||||||
|
padding-left: .5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-reason {
|
||||||
|
margin: 1em;
|
||||||
|
margin-top: 0em;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
min-height: 5em;
|
||||||
|
max-height: 22.5em;
|
||||||
|
|
||||||
|
border-radius: .2em;
|
||||||
|
border: 1px solid #111112;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 2.5em;
|
||||||
|
|
||||||
|
background-color: #17171a;
|
||||||
|
font-size: .8em;
|
||||||
|
|
||||||
|
padding: .25em;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
padding: .5em;
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-left: .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-radius: .2em;
|
||||||
|
border: 1px solid #111112;
|
||||||
|
|
||||||
|
background-color: #121213;
|
||||||
|
|
||||||
|
height: 2em;
|
||||||
|
width: 2em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
&.button-bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.button-italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.button-underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.button-color {
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #0f0f0f;
|
||||||
|
@include transition(background-color $button_hover_animation_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .input-boxed {
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
min-height: 2.5em;
|
||||||
|
height: 5em;
|
||||||
|
max-height: 20em;
|
||||||
|
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top: 1px solid #111112;
|
||||||
|
|
||||||
|
|
||||||
|
overflow-x: hidden;;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
resize: vertical;
|
||||||
|
|
||||||
|
@include chat-scrollbar-vertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-within {
|
||||||
|
background-color: #131b22;
|
||||||
|
//border-color: #284262;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-criteria {
|
||||||
|
margin: 1em;
|
||||||
|
margin-top: 0em;
|
||||||
|
padding: .5em;
|
||||||
|
|
||||||
|
border-radius: 0.2em;
|
||||||
|
border: 1px solid #111112;
|
||||||
|
background-color: #121213;
|
||||||
|
|
||||||
|
.criteria {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
a {
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-width: 4em;
|
||||||
|
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #557edc;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
margin: 1em;
|
||||||
|
margin-top: 0em;
|
||||||
|
|
||||||
|
button:not(:first-of-type) {
|
||||||
|
margin-left: 1em;
|
||||||
|
width: 6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
.bancreate {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.frame-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
select.form-control {
|
|
||||||
height: 2rem!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row {
|
|
||||||
margin-right: 0;
|
|
||||||
margin-left: 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: stretch;
|
|
||||||
|
|
||||||
div:first-of-type {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div:nth-of-type(2) {
|
|
||||||
min-width: 150px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group, .form-row {
|
|
||||||
flex-grow: 0;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
&.container-reason {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.container-global {
|
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
.input-global {
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.container-buttons {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
@import "mixin";
|
||||||
|
@import "properties";
|
||||||
|
|
||||||
|
.modal-body.modal-channel-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
min-width: 30em!important;
|
||||||
|
max-height: calc(100vh - 10em);
|
||||||
|
padding: 0em!important;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-left: .5em;
|
||||||
|
padding-right: .5em;
|
||||||
|
|
||||||
|
.column {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
min-width: 6em;
|
||||||
|
width: 10em;
|
||||||
|
|
||||||
|
margin-right: .5em;
|
||||||
|
margin-left: .5em;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #557edc;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.audio-encrypted {
|
||||||
|
/* looks better */
|
||||||
|
.value {
|
||||||
|
height: 1.6em;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-description {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
min-height: 8em; /* description plus title */
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #557edc;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
.button-copy {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
margin-top: .1em; /* looks a bit better */
|
||||||
|
margin-left: .5em;
|
||||||
|
border-radius: .2em;
|
||||||
|
|
||||||
|
width: 1.3em;
|
||||||
|
height: 1.3em;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
div {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #313135;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include transition($button_hover_animation_time ease-in-out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
display: block;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
|
||||||
|
border-radius: 0.2em;
|
||||||
|
border: 1px solid #212324;
|
||||||
|
background-color: #3a3b3f;
|
||||||
|
|
||||||
|
padding: .5em;
|
||||||
|
|
||||||
|
height: max-content;
|
||||||
|
min-height: 6em;
|
||||||
|
max-height: 40em;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
@include chat-scrollbar-vertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-value {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
font-size: 1.25em;
|
||||||
|
height: (6em / 1.25); /* min value height and a bit more */
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-buttons {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,21 @@
|
||||||
|
|
||||||
min-width: 25em;
|
min-width: 25em;
|
||||||
max-height: calc(100vh - 10rem);
|
max-height: calc(100vh - 10rem);
|
||||||
|
min-height: 10em;
|
||||||
|
|
||||||
|
width: 30em;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: stretch;
|
justify-content: stretch;
|
||||||
|
|
||||||
|
background-color: #2f2f35;
|
||||||
|
padding: .5em!important;
|
||||||
|
|
||||||
.group-assignment-list {
|
.group-assignment-list {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-height: 6em;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -33,12 +41,15 @@
|
||||||
.group-list {
|
.group-list {
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
min-height: 4em;
|
||||||
|
|
||||||
border: none;
|
|
||||||
border-radius: $border_radius_middle;
|
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
border: 1px #161616 solid;
|
||||||
|
border-radius: $border_radius_middle;
|
||||||
|
background-color: #28292b;
|
||||||
|
|
||||||
@include chat-scrollbar-vertical();
|
@include chat-scrollbar-vertical();
|
||||||
|
|
||||||
.group-entry {
|
.group-entry {
|
||||||
|
@ -47,7 +58,30 @@
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: max-content;
|
justify-content: stretch;
|
||||||
|
|
||||||
|
height: 1.75em;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
min-width: 6em;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-right: .25em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-container {
|
.icon-container {
|
||||||
|
@ -60,79 +94,6 @@
|
||||||
a {
|
a {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox {
|
|
||||||
align-self: center;
|
|
||||||
height: 8px;
|
|
||||||
|
|
||||||
margin-top: 1px;
|
|
||||||
margin-left: 1px;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 18px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 22px;
|
|
||||||
|
|
||||||
/* Hide the browser's default checkbox */
|
|
||||||
input {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkmark {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
background-color: #eee;
|
|
||||||
margin-right: 4px;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
left: 5px;
|
|
||||||
top: 1px;
|
|
||||||
width: 6px;
|
|
||||||
height: 12px;
|
|
||||||
border: solid white;
|
|
||||||
border-width: 0 3px 3px 0;
|
|
||||||
-webkit-transform: rotate(45deg);
|
|
||||||
-ms-transform: rotate(45deg);
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover:not(.disabled) input ~ .checkmark {
|
|
||||||
background-color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked ~ .checkmark {
|
|
||||||
background-color: #2196F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:checked ~ .checkmark:after {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
|
|
||||||
.checkmark {
|
|
||||||
background-color: #00000055;
|
|
||||||
&:after {
|
|
||||||
border-color: #00000055;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -745,7 +745,7 @@ label.disabled > .radio-button, .radio-button.disabled, .radio-button:disabled {
|
||||||
label.disabled > .checkbox, .checkbox:disabled, .checkbox.disabled {
|
label.disabled > .checkbox, .checkbox:disabled, .checkbox.disabled {
|
||||||
&.checkbox, > .checkbox {
|
&.checkbox, > .checkbox {
|
||||||
pointer-events: none!important;
|
pointer-events: none!important;
|
||||||
background-color: #222227;
|
background-color: #1a1a1e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<meta name="og:description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
|
<meta name="og:description" content="The TeaSpeak Web client is a in the browser running client for the VoIP communication software TeaSpeak." />
|
||||||
<meta name="og:url" content="https://web.teaspeak.de/">
|
<meta name="og:url" content="https://web.teaspeak.de/">
|
||||||
<meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png">
|
<!-- WHAT THE HELL? <meta name="og:image" content="https://www.whatsapp.com/img/whatsapp-promo.png"> -->
|
||||||
|
|
||||||
<!-- TODO Needs some fix -->
|
<!-- TODO Needs some fix -->
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
|
@ -208,22 +208,31 @@
|
||||||
serveredit_1.png https://www.hypixel-koo.cf/tsapoijdsadpoijsadsapj.png
|
serveredit_1.png https://www.hypixel-koo.cf/tsapoijdsadpoijsadsapj.png
|
||||||
serveredit_2.png https://www.hypixel-koo.cf/tsandljsandljsamndoj3oiwejlkjmnlksandljsadmnlmsadnlsa.png
|
serveredit_2.png https://www.hypixel-koo.cf/tsandljsandljsamndoj3oiwejlkjmnlksandljsadmnlmsadnlsa.png
|
||||||
serveredit_3.png https://www.hypixel-koo.cf/toiuhsadouhgdsapoiugdsapouhdsapouhdsaouhwouhwwouhwwoiuhwoihwwoihwoijhwwoknw.png
|
serveredit_3.png https://www.hypixel-koo.cf/toiuhsadouhgdsapoiugdsapouhdsapouhdsaouhwouhwwouhwwoiuhwoihwwoihwoijhwwoknw.png
|
||||||
|
|
||||||
|
Query accounts: https://puu.sh/EhvkJ/7551f548e3.png
|
||||||
|
Channel info: https://puu.sh/EhuVH/1e21540589.png
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!-- <img src="http://puu.sh/E6NXv/eb2f19c7c3.png"> -->
|
<!-- <img src="http://puu.sh/E6NXv/eb2f19c7c3.png"> -->
|
||||||
<!-- <img src="http://puu.sh/E9jT6/302912ae34.png"> -->
|
<!-- <img src="http://puu.sh/E9jT6/302912ae34.png"> -->
|
||||||
<!-- <img src="http://puu.sh/E9jTe/b41f6386de.png"> -->
|
<!-- <img src="http://puu.sh/E9jTe/b41f6386de.png"> -->
|
||||||
<!-- <img src="img/style/ban-list.png"> -->
|
<!-- <img src="img/style/ban-list.png"> -->
|
||||||
<img src="http://puu.sh/E9jTe/b41f6386de.png">
|
<!-- <img src="http://puu.sh/E9jTe/b41f6386de.png"> -->
|
||||||
|
<img src="https://puu.sh/EhuVH/1e21540589.png">
|
||||||
</div>
|
</div>
|
||||||
<button class="toggle-spoiler-style" style="height: 30px; width: 100px; z-index: 100000000; position: absolute; bottom: 2px;">toggle style</button>
|
<button class="toggle-spoiler-style" style="height: 30px; width: 100px; z-index: 100000000; position: absolute; bottom: 2px;">toggle style</button>
|
||||||
<script>
|
<script>
|
||||||
setTimeout(() => {
|
const init = (jQuery) => {
|
||||||
$("#spoiler-style").hide();
|
if(typeof jQuery === "undefined") {
|
||||||
$(".toggle-spoiler-style").on('click', () => {
|
setTimeout(() => init($), 1000);
|
||||||
$("#spoiler-style").toggle();
|
return;
|
||||||
|
}
|
||||||
|
jQuery("#spoiler-style").hide();
|
||||||
|
jQuery(".toggle-spoiler-style").on('click', () => {
|
||||||
|
jQuery("#spoiler-style").toggle();
|
||||||
});
|
});
|
||||||
}, 2500);
|
};
|
||||||
|
setTimeout(() => init($), 1000);
|
||||||
</script>
|
</script>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -208,11 +208,14 @@
|
||||||
<div class="container-bottom">
|
<div class="container-bottom">
|
||||||
<div class="container-server-log" id="server-log"></div>
|
<div class="container-server-log" id="server-log"></div>
|
||||||
<div class="container-footer">
|
<div class="container-footer">
|
||||||
<div class="hide-small">
|
<span>
|
||||||
Open source on <a href="https://github.com/TeaSpeak/TeaSpeak-Web"
|
<a>{{tr "Version:" /}} {{>app_version}}</a>
|
||||||
style="display: inline-block; position: relative">github.com</a>
|
<div class="hide-small">
|
||||||
</div>
|
(Open source on
|
||||||
<a>{{tr "Version:" /}} {{>app_version}}</a>
|
<a target="_blank" href="https://github.com/TeaSpeak/TeaSpeak-Web"
|
||||||
|
style="display: inline-block; position: relative">github.com</a>)
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- Selection info -->
|
</div> <!-- Selection info -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -2302,442 +2305,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
<script class="jsrender-template" id="tmpl_settings_old" type="text/html">
|
|
||||||
<x-tab>
|
|
||||||
<x-entry>
|
|
||||||
<x-tag>{{tr "General"/}}</x-tag>
|
|
||||||
<x-content>
|
|
||||||
<div class="settings-general">
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "TeaSpeak Forum Connection" /}}</div>
|
|
||||||
<div class="content settings-teaspeak-forum">
|
|
||||||
<div class="not-connected">
|
|
||||||
<div>{{tr "You're currently not connected with your TeaSpeak Forum account"
|
|
||||||
/}}
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-primary btn-raised button-login">{{tr "login" /}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<!-- TODO new style! -->
|
|
||||||
<div class="connected">
|
|
||||||
<div class="connected-info">{{tr "You're connected via TeaSpeak forum" /}}</div>
|
|
||||||
<div class="container-info-action">
|
|
||||||
<div class="container-info">
|
|
||||||
<div class="property username">
|
|
||||||
<div class="key">{{tr "Username:" /}}</div>
|
|
||||||
<div class="value">WolverinDEV</div>
|
|
||||||
</div>
|
|
||||||
<div class="property premium">
|
|
||||||
<div class="key">{{tr "Premium:" /}}</div>
|
|
||||||
<div class="value">YES</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="container-actions">
|
|
||||||
<button class="button-logout">logout</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--
|
|
||||||
<div class="property synchronized">
|
|
||||||
<div class="key">{{tr "Synchronized:" /}}</div>
|
|
||||||
<div class="value">NO <button class="button-enable-disable-sync">{{tr "enable synchronization" /}}</button></div>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</x-content>
|
|
||||||
</x-entry>
|
|
||||||
<x-entry>
|
|
||||||
<x-tag>{{tr "Audio" /}}</x-tag>
|
|
||||||
<x-content>
|
|
||||||
<div class="settings_audio">
|
|
||||||
<div class="settings-device-error alert alert-warning alert-dismissible fade show"
|
|
||||||
role="alert">
|
|
||||||
<div class="message"></div>
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "Microphone" /}}</div>
|
|
||||||
<div class="content settings-microphone {{if !voice_available}}disabled{{/if}}">
|
|
||||||
{{if voice_available}}
|
|
||||||
<div class="form-row settings-device settings-device-microphone">
|
|
||||||
<div class="form-group settings-device-select">
|
|
||||||
<label for="select-settings-microphone-device" class="bmd-label-static">{{tr
|
|
||||||
"Device:" /}}</label>
|
|
||||||
<select id="select-settings-microphone-device"
|
|
||||||
class="form-control audio-select-microphone"></select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group bmd-form-group">
|
|
||||||
<button class="btn btn-secondary button-device-update">update</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-vad-container">
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "Voice Activity Detection"/}}</div>
|
|
||||||
<div class="content">
|
|
||||||
<fieldset>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="select-settings-vad-type-1" value="pt"
|
|
||||||
name="vad_type" class="custom-control-input">
|
|
||||||
<label class="custom-control-label"
|
|
||||||
for="select-settings-vad-type-1">{{tr "Always active"
|
|
||||||
/}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="select-settings-vad-type-2" value="vad"
|
|
||||||
name="vad_type" class="custom-control-input">
|
|
||||||
<label class="custom-control-label"
|
|
||||||
for="select-settings-vad-type-2">{{tr "Voice activity
|
|
||||||
detection"/}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-radio">
|
|
||||||
<input type="radio" id="select-settings-vad-type-3" value="ppt"
|
|
||||||
name="vad_type" class="custom-control-input">
|
|
||||||
<label class="custom-control-label"
|
|
||||||
for="select-settings-vad-type-3">{{tr "Push to
|
|
||||||
talk"/}}</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-vad-impl">
|
|
||||||
<div class="settings-vad-impl-entry setting-vad-pt">
|
|
||||||
{{tr "There are no setting entries for an <b>always</b> online voice
|
|
||||||
detection."/}}
|
|
||||||
</div>
|
|
||||||
<div class="settings-vad-impl-entry setting-vad-ppt">
|
|
||||||
<div class="property ppt-key">
|
|
||||||
<div class="key">{{tr "Push to talk key:"/}}</div>
|
|
||||||
<div class="value">
|
|
||||||
<button type="button"
|
|
||||||
class="btn btn-raised btn-primary vat_ppt_key">{{tr
|
|
||||||
"Uninitialised"/}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group ppt-delay">
|
|
||||||
<label for="input-settings-ppt-delay" class="bmd-label-static">{{tr
|
|
||||||
"Key release delay:" /}}</label>
|
|
||||||
<input id="input-settings-ppt-delay" class="form-control value"
|
|
||||||
type="number" min="0" max="5000"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-vad-impl-entry setting-vad-vad">
|
|
||||||
<div>{{tr "Voice activity threshold (<a
|
|
||||||
class='vad_vad_slider_value'>20</a>%)"/}}
|
|
||||||
</div>
|
|
||||||
<div class="vad_vad_threshold_selector">
|
|
||||||
<div class="vad_vad_bar">
|
|
||||||
<div class="container-hider">
|
|
||||||
<div class="hider vad_vad_bar_filler"></div>
|
|
||||||
</div>
|
|
||||||
<input type="range" min="0" max="100" value="50"
|
|
||||||
class="vad_vad_slider">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<div>{{tr "Voice had been disabled" /}}</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "Speaker" /}}</div>
|
|
||||||
<div class="content settings-speaker">
|
|
||||||
<div class="settings-device settings-device-speaker">
|
|
||||||
<div class="form-group settings-device-select">
|
|
||||||
<label for="select-settings-speaker-device" class="bmd-label-static">{{tr
|
|
||||||
"Device:" /}}</label>
|
|
||||||
<select id="select-settings-speaker-device"
|
|
||||||
class="form-control audio-select-speaker"></select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group bmd-form-group">
|
|
||||||
<button class="btn btn-secondary button-device-update">update</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container-master-volume">
|
|
||||||
<div class="key">Master volume:</div>
|
|
||||||
<div class="value master-volume">
|
|
||||||
<input type="range" min="0" max="100" value="100">
|
|
||||||
<a>(66%)</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="group_box sound">
|
|
||||||
<div class="header">{{tr "Sound Settings" /}}</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="sound-settings">
|
|
||||||
<div class="property">
|
|
||||||
<div class="key">Sound Master volume:</div>
|
|
||||||
<div class="value sound-master-volume">
|
|
||||||
<input type="range" min="0" max="100" value="100">
|
|
||||||
<a>(66%)</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="key">
|
|
||||||
{{tr "Overlap same sounds:" /}}
|
|
||||||
</div>
|
|
||||||
<div class="value overlap-sounds">
|
|
||||||
<div class="switch">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" checked>
|
|
||||||
<div class="help-tip-container"> <!-- lets be absolute -->
|
|
||||||
<div class="help-tip tip-right tip-small">
|
|
||||||
<p>
|
|
||||||
{{tr "This options deferments if a sound overlaps
|
|
||||||
itself when played twice.<br>" +
|
|
||||||
"An example would be when you move multiple clients,
|
|
||||||
you hear that sound n-times.<br>" +
|
|
||||||
"If this option is disabled, you hear that sound
|
|
||||||
just once."
|
|
||||||
/}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="property">
|
|
||||||
<div class="key muted-sounds">
|
|
||||||
{{tr "Mute sounds when output is muted:" /}}
|
|
||||||
</div>
|
|
||||||
<div class="value muted-sounds">
|
|
||||||
<div class="switch">
|
|
||||||
<label>
|
|
||||||
<input type="checkbox" checked>
|
|
||||||
<div class="help-tip-container"> <!-- lets be absolute -->
|
|
||||||
<div class="help-tip tip-right tip-small">
|
|
||||||
<p>
|
|
||||||
{{tr "Mute all system sounds, when you've muted your
|
|
||||||
output.<br>If this option isn't disabled you'll
|
|
||||||
still receive system sounds like 'user joined your
|
|
||||||
channel'."/}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sound-list">
|
|
||||||
<div class="sound-list-header">
|
|
||||||
<div class="column sound-name">{{tr "Name" /}}</div>
|
|
||||||
<div class="column sound-activated">{{tr "Activated" /}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="sound-list-entries-container">
|
|
||||||
<div class="sound-list-entries">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group sound-list-filter">
|
|
||||||
<label for="input-settings-sounds-filter" class="bmd-label-floating">{{tr
|
|
||||||
"Filter" /}}</label>
|
|
||||||
<input id="input-settings-sounds-filter" class="form-control" type="text"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</x-content>
|
|
||||||
</x-entry>
|
|
||||||
<x-entry>
|
|
||||||
<x-tag>
|
|
||||||
<div class="container-tabname-translations">{{tr "Translations" /}}
|
|
||||||
<div class="country flag-en"></div>
|
|
||||||
</div>
|
|
||||||
</x-tag>
|
|
||||||
<x-content>
|
|
||||||
<div class="settings-translations">
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "Available translations" /}}</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="setting-list">
|
|
||||||
<div class="list">
|
|
||||||
</div>
|
|
||||||
<div class="management">
|
|
||||||
<div class="loading">Loading...</div>
|
|
||||||
<div class="space"></div>
|
|
||||||
<button class="btn btn-secondary button-add-repository">{{tr "Add
|
|
||||||
repository" /}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="restart-note">
|
|
||||||
<p>
|
|
||||||
{{tr "Attention: These settings get only affected after a restart or
|
|
||||||
reload!" /}}
|
|
||||||
</p>
|
|
||||||
<button class="button-reload">{{tr "reload now" /}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</x-content>
|
|
||||||
</x-entry>
|
|
||||||
<x-entry>
|
|
||||||
<x-tag class="tab-profiles">
|
|
||||||
{{tr "Profiles" /}}
|
|
||||||
</x-tag>
|
|
||||||
<x-content>
|
|
||||||
<div class="settings-profiles">
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "Available profiles" /}}</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="profile-list">
|
|
||||||
<div class="list">
|
|
||||||
</div>
|
|
||||||
<div class="management">
|
|
||||||
<button class="btn btn-primary button-set-default">{{tr "Set selected as
|
|
||||||
default" /}}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-danger button-delete">{{tr "Delete selected" /}}
|
|
||||||
</button>
|
|
||||||
<div class="space"></div>
|
|
||||||
<button class="btn btn-success button-add-profile">{{tr "Create profile"
|
|
||||||
/}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="group_box">
|
|
||||||
<div class="header">{{tr "Profile settings" /}}</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="profile-settings">
|
|
||||||
<div class="general-settings">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="input-settings-profile-name" class="bmd-label-static">{{tr
|
|
||||||
"Profile name:" /}}</label>
|
|
||||||
<input id="input-settings-profile-name"
|
|
||||||
class="form-control setting-name">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="input-settings-profile-default-name"
|
|
||||||
class="bmd-label-static">{{tr "Default nickname:" /}}</label>
|
|
||||||
<input id="input-settings-profile-default-name"
|
|
||||||
class="form-control setting-default-nickname">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<!-- TODO use random ids -->
|
|
||||||
<label for="input-settings-profile-default-password"
|
|
||||||
class="bmd-label-floating">{{tr "Default server password"
|
|
||||||
/}}</label>
|
|
||||||
<input type="password" id="input-settings-profile-default-password"
|
|
||||||
class="form-control setting-default-password">
|
|
||||||
</div>
|
|
||||||
<div class="form-group select-container">
|
|
||||||
<label for="input-settings-profile-identity-type"
|
|
||||||
class="bmd-label-static">{{tr "Identify Type:" /}}</label>
|
|
||||||
<select id="input-settings-profile-identity-type" class="form-control">
|
|
||||||
<option name="identity-type" value="teaforo">{{tr "Forum Account"
|
|
||||||
/}}
|
|
||||||
</option>
|
|
||||||
<option name="identity-type" value="teamspeak">{{tr "TeamSpeak"
|
|
||||||
/}}
|
|
||||||
</option>
|
|
||||||
<option name="identity-type" value="nickname">{{tr "Nickname (Debug
|
|
||||||
purposes only!)" /}}
|
|
||||||
</option> <!-- Only available on localhost for debug -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="identity-settings identity-settings-teamspeak">
|
|
||||||
<!--
|
|
||||||
<div class="import">
|
|
||||||
{{tr "Please enter your exported TS3 Identity string bellow or select your exported Identity"/}}<br>
|
|
||||||
<div style="width: 100%; display: flex; justify-content: stretch; flex-direction: row">
|
|
||||||
<input placeholder="Identity string" style="min-width: 60%; flex-shrink: 1; flex-grow: 2; margin: 5px;" class="identity_string">
|
|
||||||
<div style="max-width: 200px; flex-grow: 1; flex-shrink: 4; margin: 5px"><input style="display: flex; width: 100%;" class="identity_file" type="file"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
<div class="identity-info" style="display: none">
|
|
||||||
<div class="form-group unique-id">
|
|
||||||
<label>{{tr "UniqueID:" /}}</label>
|
|
||||||
<input class="form-control" type="text"
|
|
||||||
value="xxjnc14LmvTk+Lyrm8OOeo4tOqw=" readonly/>
|
|
||||||
</div>
|
|
||||||
<div class="form-row level">
|
|
||||||
<div class="form-group container-input">
|
|
||||||
<label>{{tr "Level:" /}}</label>
|
|
||||||
<input class="form-control" type="text" value="39" readonly/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group bmd-form-group">
|
|
||||||
<button class="btn btn-raised button-improve">{{tr "Improve"
|
|
||||||
/}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="identity-undefined">
|
|
||||||
<div>{{tr "You have'nt generated/imported an identity.<br>Generate a new
|
|
||||||
one or import one." /}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="manage">
|
|
||||||
<button class="btn btn-primary button-generate">{{tr "Generate new"
|
|
||||||
/}}
|
|
||||||
</button>
|
|
||||||
<div class="export-import">
|
|
||||||
<button class="btn btn-secondary button-import">{{tr "Import
|
|
||||||
identity" /}}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary button-export">{{tr "Export identity"
|
|
||||||
/}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="identity-settings identity-settings-teaforo">
|
|
||||||
<div class="connected">
|
|
||||||
{{tr "You're using your forum account as verification"/}}
|
|
||||||
</div>
|
|
||||||
<div class="disconnected">
|
|
||||||
<!-- TODO tr -->
|
|
||||||
<!-- <a href="#" class="native-teaforo-login">here</a> -->
|
|
||||||
<div class="error-forum-not-connected alert alert-warning fade show"
|
|
||||||
role="alert">
|
|
||||||
You cant use your TeaSpeak forum account. You're not connected with
|
|
||||||
your forum Account!<br>
|
|
||||||
Setup your connection <a class="forum-setup" href="#">here</a>.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="identity-settings identity-settings-nickname">
|
|
||||||
<a>
|
|
||||||
{{tr "This is just for debug and uses the name as unique identifier" /}}
|
|
||||||
</a>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="input-settings-profile-name-name"
|
|
||||||
class="bmd-label-floating">{{tr "Username" /}}</label>
|
|
||||||
<input id="input-settings-profile-name-name"
|
|
||||||
class="form-control setting-name">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="settings-profile-error alert alert-warning fade show" role="alert">
|
|
||||||
<div class="message"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</x-content>
|
|
||||||
</x-entry>
|
|
||||||
</x-tab>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script class="jsrender-template" id="tmpl_settings-sound_entry" type="text/html">
|
<script class="jsrender-template" id="tmpl_settings-sound_entry" type="text/html">
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
|
@ -3140,10 +2707,9 @@
|
||||||
<div class="group-list">
|
<div class="group-list">
|
||||||
{{for groups}}
|
{{for groups}}
|
||||||
<div class="group-entry">
|
<div class="group-entry">
|
||||||
<label class="ccheckbox {{if disabled}}disabled{{/if}}">
|
<label class="checkbox {{if default}}disabled{{/if}}">
|
||||||
<input type="checkbox" group-id="{{:id}}" {{if default}}default disabled="true" {{/if}} {{if
|
<input type="checkbox" group-id="{{:id}}" {{if default}}default disabled="true" {{/if}}>
|
||||||
assigned}}checked{{/if}}>
|
<div class="mark"></div>
|
||||||
<span class="checkmark"></span>
|
|
||||||
</label>
|
</label>
|
||||||
<node key="icon_{{>id}}"></node>
|
<node key="icon_{{>id}}"></node>
|
||||||
<a>{{>name}} ({{>id}})</a>
|
<a>{{>name}} ({{>id}})</a>
|
||||||
|
@ -3564,21 +3130,38 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script class="jsrender-template" id="tmpl_client_ban" type="text/html">
|
<script class="jsrender-template" id="tmpl_client_ban" type="text/html">
|
||||||
<div class="align_column">
|
<div> <!-- for the renderer -->
|
||||||
<div class="align_column" style="margin: 5px">
|
<div class="container-info">
|
||||||
<a>{{tr "Name:" /}}</a>
|
<div class="container container-name">
|
||||||
<input value="{{>client_name}}" readonly>
|
<div class="title">{{tr "Nickname" /}}</div>
|
||||||
|
<div class="value">
|
||||||
|
{{for entries}}
|
||||||
|
{{>name}}<br>
|
||||||
|
{{/for}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container container-unique-id">
|
||||||
|
<div class="title">{{tr "Unique ID" /}}</div>
|
||||||
|
<div class="value">
|
||||||
|
{{for entries}}
|
||||||
|
{{>unique_id}}<br>
|
||||||
|
{{/for}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="align_column" style="margin: 5px">
|
<div class="container-duration">
|
||||||
<a>{{tr "Reason:" /}}</a>
|
<a>{{tr "Duration" /}}</a>
|
||||||
<textarea style="height: 32px; resize: vertical; max-height: 150px; min-height: 32px"
|
<div class="container-value">
|
||||||
maxlength="512" class="ban_reason"></textarea>
|
<div class="input-boxed value">
|
||||||
</div>
|
<input type="number" value="1" min="1" />
|
||||||
<div class="align_row" style="margin: 5px; justify-content: space-between">
|
<div class="container-tooltip tooltip-max-time">
|
||||||
<a>{{tr "Duration:" /}}</a>
|
<img src="img/icon_tooltip.svg"/>
|
||||||
<div class="align_row">
|
<div class="tooltip">
|
||||||
<input type="number" value="1" class="ban_duration" style="margin-right: 7px" min="1">
|
<a class="max">error: max duration</a>
|
||||||
<select class="ban_duration_type">
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<select class="input-boxed">
|
||||||
<option value="sec">{{tr "seconds"/}}</option>
|
<option value="sec">{{tr "seconds"/}}</option>
|
||||||
<option value="min">{{tr "minutes"/}}</option>
|
<option value="min">{{tr "minutes"/}}</option>
|
||||||
<option value="hours">{{tr "hours"/}}</option>
|
<option value="hours">{{tr "hours"/}}</option>
|
||||||
|
@ -3587,38 +3170,44 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="group_box container-ban-type">
|
<div class="container-reason">
|
||||||
<div class="header">{{tr "Ban client by"/}}</div>
|
<div class="toolbar">
|
||||||
<div class="content ban-types">
|
<div class="button button-bold">B</div>
|
||||||
<div>
|
<div class="button button-italic">I</div>
|
||||||
<input type="checkbox" class="ban-type-nickname">
|
<div class="button button-underline">U</div>
|
||||||
<a>{{tr "Nickname"/}}</a>
|
<label class="button button-color">
|
||||||
<div class="help-tip tip-right tip-small" checked>
|
<input type="color" value="#FF0000">
|
||||||
<p>
|
<a class="rainbow-letter">C</a>
|
||||||
{{tr "Bans the client by his current nickname.<br>" +
|
</label>
|
||||||
"The currently nickname cant be used until the ban expired"/}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" class="ban-type-hardware-id" checked>
|
|
||||||
<a>{{tr "Hardware ID" /}}</a>
|
|
||||||
<div class="help-tip tip-right tip-small">
|
|
||||||
<p>
|
|
||||||
{{tr "Bans the client by his hardware id.<br>" +
|
|
||||||
"The hardware id has different meanings, depends on the users agent<br>" +
|
|
||||||
"TeaClient: The hardware id will be equal to the mac address<br>" +
|
|
||||||
"TeaWeb: The TeaSpeak web client hasn't a hardware id, it will be random<br>" +
|
|
||||||
"TeamSpeak 3 client: The hardware id will be a result of some hashes from hardware
|
|
||||||
specific properties" /}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" class="ban-type-ip" checked>
|
|
||||||
<a>{{tr "IP Address" /}}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<textarea class="input-boxed ban-reason" placeholder="{{tr 'Ban reason' /}}"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="container-criteria">
|
||||||
|
<div class="criteria nickname">
|
||||||
|
<a>{{tr "Nickname and Unique ID" /}}</a>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox">
|
||||||
|
<div class="mark"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="criteria ip-address">
|
||||||
|
<a>{{tr "IP-Address" /}}</a>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox">
|
||||||
|
<div class="mark"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="criteria hardware-id">
|
||||||
|
<a>{{tr "Hardware ID" /}}</a>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox">
|
||||||
|
<div class="mark"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-buttons">
|
||||||
|
<button class="btn btn-danger button-cancel">{{tr "Cancel" /}}</button>
|
||||||
|
<button class="btn btn-success button-apply">{{tr "Ok" /}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
@ -5285,6 +4874,60 @@
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script class="jsrender-template" id="tmpl_channel_info" type="text/html">
|
||||||
|
<div> <!-- Important for the renderer -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="column channel-type">
|
||||||
|
<a class="title">{{tr "Channel Type" /}}</a>
|
||||||
|
<div class="value">error: channel type</div>
|
||||||
|
</div>
|
||||||
|
<div class="column chat-mode">
|
||||||
|
<a class="title">{{tr "Chat mode" /}}</a>
|
||||||
|
<div class="value">error: chat mode</div>
|
||||||
|
</div>
|
||||||
|
<div class="column current-clients">
|
||||||
|
<a class="title">{{tr "Current clients" /}}</a>
|
||||||
|
<div class="value">error: current clients</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="column audio-codec">
|
||||||
|
<a class="title">{{tr "Audio Codec" /}}</a>
|
||||||
|
<div class="value">error: audio codec</div>
|
||||||
|
</div>
|
||||||
|
<div class="column audio-encrypted">
|
||||||
|
<a class="title">{{tr "Audio encrypted" /}}</a>
|
||||||
|
<div class="value">error: audio encrypted</div>
|
||||||
|
</div>
|
||||||
|
<div class="column flag-password">
|
||||||
|
<a class="title">{{tr "Password protected" /}}</a>
|
||||||
|
<div class="value">error: password protected</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row topic"> <!-- only visible if set! -->
|
||||||
|
<div class="column">
|
||||||
|
<a class="title">{{tr "Topic" /}}</a>
|
||||||
|
<div class="value">error: channel topic</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-description"> <!-- only visible if set -->
|
||||||
|
<a class="title">
|
||||||
|
{{tr "Description" /}}
|
||||||
|
<div class="button-copy">
|
||||||
|
<div class="icon client-copy"></div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="value">
|
||||||
|
error: channel description
|
||||||
|
</div>
|
||||||
|
<div class="no-value">{{tr "Channel has no description" /}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="container-buttons">
|
||||||
|
<button class="btn btn-success button-update">{{tr "Refresh" /}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
<script class="jsrender-template" id="tmpl_server_info" type="text/html">
|
<script class="jsrender-template" id="tmpl_server_info" type="text/html">
|
||||||
<div> <!-- required for the renderer -->
|
<div> <!-- required for the renderer -->
|
||||||
<div class="container-top hostbanner">
|
<div class="container-top hostbanner">
|
||||||
|
@ -5395,7 +5038,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a class="key">{{tr "Voice data encoded" /}}</a>
|
<a class="key">{{tr "Voice data encryption" /}}</a>
|
||||||
<div class="value server-voice-encryption">
|
<div class="value server-voice-encryption">
|
||||||
error: voice encryption
|
error: voice encryption
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -437,7 +437,8 @@ namespace connection {
|
||||||
|
|
||||||
client.updateVariables(...updates);
|
client.updateVariables(...updates);
|
||||||
|
|
||||||
if(!old_channel) {
|
/* if its a new client join, or a system reason (like we joined) */
|
||||||
|
if(!old_channel || reason_id == 2) {
|
||||||
/* client new join */
|
/* client new join */
|
||||||
const conversation_manager = this.connection_handler.side_bar.private_conversations();
|
const conversation_manager = this.connection_handler.side_bar.private_conversations();
|
||||||
const conversation = conversation_manager.find_conversation({
|
const conversation = conversation_manager.find_conversation({
|
||||||
|
@ -448,6 +449,8 @@ namespace connection {
|
||||||
create: false,
|
create: false,
|
||||||
attach: true
|
attach: true
|
||||||
});
|
});
|
||||||
|
if(conversation)
|
||||||
|
client.flag_text_unread = conversation.is_unread();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(client instanceof LocalClientEntry) {
|
if(client instanceof LocalClientEntry) {
|
||||||
|
@ -754,6 +757,9 @@ namespace connection {
|
||||||
|
|
||||||
if(target_own) {
|
if(target_own) {
|
||||||
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
|
||||||
|
const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"]));
|
||||||
|
if(client) /* the client itself might be invisible */
|
||||||
|
client.flag_text_unread = conversation.is_unread();
|
||||||
} else {
|
} else {
|
||||||
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
|
||||||
}
|
}
|
||||||
|
@ -802,6 +808,7 @@ namespace connection {
|
||||||
timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]),
|
timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]),
|
||||||
message: json["msg"]
|
message: json["msg"]
|
||||||
});
|
});
|
||||||
|
this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/// <reference path="ui/frames/chat.ts" />
|
/// <reference path="ui/frames/chat.ts" />
|
||||||
/// <reference path="ui/modal/ModalConnect.ts" />
|
/// <reference path="ui/modal/ModalConnect.ts" />
|
||||||
/// <reference path="ui/modal/ModalCreateChannel.ts" />
|
/// <reference path="ui/modal/ModalCreateChannel.ts" />
|
||||||
/// <reference path="ui/modal/ModalBanCreate.ts" />
|
|
||||||
/// <reference path="ui/modal/ModalBanClient.ts" />
|
/// <reference path="ui/modal/ModalBanClient.ts" />
|
||||||
/// <reference path="ui/modal/ModalYesNo.ts" />
|
/// <reference path="ui/modal/ModalYesNo.ts" />
|
||||||
/// <reference path="ui/modal/ModalBanList.ts" />
|
/// <reference path="ui/modal/ModalBanList.ts" />
|
||||||
|
@ -289,6 +288,37 @@ interface Window {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function execute_default_connect() {
|
||||||
|
if(settings.static(Settings.KEY_FLAG_CONNECT_DEFAULT, false) && settings.static(Settings.KEY_CONNECT_ADDRESS, "")) {
|
||||||
|
const profile_uuid = settings.static(Settings.KEY_CONNECT_PROFILE, (profiles.default_profile() || {id: 'default'}).id);
|
||||||
|
const profile = profiles.find_profile(profile_uuid) || profiles.default_profile();
|
||||||
|
const address = settings.static(Settings.KEY_CONNECT_ADDRESS, "");
|
||||||
|
const username = settings.static(Settings.KEY_CONNECT_USERNAME, "Another TeaSpeak user");
|
||||||
|
|
||||||
|
const password = settings.static(Settings.KEY_CONNECT_PASSWORD, "");
|
||||||
|
const password_hashed = settings.static(Settings.KEY_FLAG_CONNECT_PASSWORD, false);
|
||||||
|
|
||||||
|
if(profile && profile.valid()) {
|
||||||
|
const connection = server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler();
|
||||||
|
connection.startConnection(address, profile, true, {
|
||||||
|
nickname: username,
|
||||||
|
password: password.length > 0 ? {
|
||||||
|
password: password,
|
||||||
|
hashed: password_hashed
|
||||||
|
} : undefined
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Modals.spawnConnectModal({},{
|
||||||
|
url: address,
|
||||||
|
enforce: true
|
||||||
|
}, {
|
||||||
|
profile: profile,
|
||||||
|
enforce: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
/*
|
/*
|
||||||
window.proxy_instance = new TestProxy({
|
window.proxy_instance = new TestProxy({
|
||||||
|
@ -357,6 +387,9 @@ function main() {
|
||||||
log.info(LogCategory.STATISTICS, tr("Received user count update: %o"), status);
|
log.info(LogCategory.STATISTICS, tr("Received user count update: %o"), status);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]);
|
||||||
|
|
||||||
|
|
||||||
(<any>window).test_upload = (message?: string) => {
|
(<any>window).test_upload = (message?: string) => {
|
||||||
message = message || "Hello World";
|
message = message || "Hello World";
|
||||||
|
|
||||||
|
@ -382,37 +415,8 @@ function main() {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
server_connections.set_active_connection_handler(server_connections.server_connection_handlers()[0]);
|
/* schedule it a bit later then the main because the main function is still within the loader */
|
||||||
|
setTimeout(execute_default_connect, 5);
|
||||||
if(settings.static(Settings.KEY_FLAG_CONNECT_DEFAULT, false) && settings.static(Settings.KEY_CONNECT_ADDRESS, "")) {
|
|
||||||
const profile_uuid = settings.static(Settings.KEY_CONNECT_PROFILE, (profiles.default_profile() || {id: 'default'}).id);
|
|
||||||
const profile = profiles.find_profile(profile_uuid) || profiles.default_profile();
|
|
||||||
const address = settings.static(Settings.KEY_CONNECT_ADDRESS, "");
|
|
||||||
const username = settings.static(Settings.KEY_CONNECT_USERNAME, "Another TeaSpeak user");
|
|
||||||
|
|
||||||
const password = settings.static(Settings.KEY_CONNECT_PASSWORD, "");
|
|
||||||
const password_hashed = settings.static(Settings.KEY_FLAG_CONNECT_PASSWORD, false);
|
|
||||||
|
|
||||||
if(profile && profile.valid()) {
|
|
||||||
const connection = server_connections.active_connection_handler() || server_connections.spawn_server_connection_handler();
|
|
||||||
connection.startConnection(address, profile, true, {
|
|
||||||
nickname: username,
|
|
||||||
password: password.length > 0 ? {
|
|
||||||
password: password,
|
|
||||||
hashed: password_hashed
|
|
||||||
} : undefined
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Modals.spawnConnectModal({},{
|
|
||||||
url: address,
|
|
||||||
enforce: true
|
|
||||||
}, {
|
|
||||||
profile: profile,
|
|
||||||
enforce: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const connection = server_connections.active_connection_handler();
|
const connection = server_connections.active_connection_handler();
|
||||||
/*
|
/*
|
||||||
|
@ -426,7 +430,15 @@ function main() {
|
||||||
//Modals.openClientInfo(connection.getClient());
|
//Modals.openClientInfo(connection.getClient());
|
||||||
//Modals.openServerInfoBandwidth(connection.channelTree.server);
|
//Modals.openServerInfoBandwidth(connection.channelTree.server);
|
||||||
|
|
||||||
Modals.openBanList(connection);
|
//Modals.openBanList(connection);
|
||||||
|
/*
|
||||||
|
Modals.spawnBanClient(connection,[
|
||||||
|
{name: "WolverinDEV", unique_id: "XXXX"},
|
||||||
|
{name: "WolverinDEV", unique_id: "XXXX"},
|
||||||
|
{name: "WolverinDEV", unique_id: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"},
|
||||||
|
{name: "WolverinDEV", unique_id: "YYY"}
|
||||||
|
], () => {});
|
||||||
|
*/
|
||||||
}, 4000);
|
}, 4000);
|
||||||
//Modals.spawnSettingsModal("identity-profiles");
|
//Modals.spawnSettingsModal("identity-profiles");
|
||||||
//Modals.spawnKeySelect(console.log);
|
//Modals.spawnKeySelect(console.log);
|
||||||
|
|
|
@ -51,7 +51,8 @@ class ChannelProperties {
|
||||||
//Only after request
|
//Only after request
|
||||||
channel_description: string = "";
|
channel_description: string = "";
|
||||||
|
|
||||||
channel_flag_conversation_private: boolean = false;
|
channel_flag_conversation_private: boolean = true; /* TeamSpeak mode */
|
||||||
|
channel_conversation_history_length: number = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelEntry {
|
class ChannelEntry {
|
||||||
|
@ -460,30 +461,32 @@ class ChannelEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
|
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
|
||||||
let channelCreate =
|
let channelCreate = !![
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_TEMPORARY).granted(1) ||
|
PermissionType.B_CHANNEL_CREATE_TEMPORARY,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT).granted(1) ||
|
PermissionType.B_CHANNEL_CREATE_SEMI_PERMANENT,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_CREATE_PERMANENT).granted(1);
|
PermissionType.B_CHANNEL_CREATE_PERMANENT
|
||||||
|
].find(e => this.channelTree.client.permissions.neededPermission(e).granted(1));
|
||||||
|
|
||||||
let channelModify =
|
let channelModify = !![
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAKE_DEFAULT,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAKE_PERMANENT,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAKE_SEMI_PERMANENT,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAKE_TEMPORARY,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NAME).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_NAME,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TOPIC).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_TOPIC,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_DESCRIPTION).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_DESCRIPTION,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_PASSWORD).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_PASSWORD,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_CODEC).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_CODEC,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_CODEC_QUALITY).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_CODEC_QUALITY,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_CODEC_LATENCY_FACTOR,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAXCLIENTS,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAXFAMILYCLIENTS).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAXFAMILYCLIENTS,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_SORTORDER).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_SORTORDER,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_NEEDED_TALK_POWER,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_MAKE_CODEC_ENCRYPTED,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY).granted(1) ||
|
PermissionType.B_CHANNEL_MODIFY_TEMP_DELETE_DELAY,
|
||||||
this.channelTree.client.permissions.neededPermission(PermissionType.B_ICON_MANAGE).granted(1);
|
PermissionType.B_ICON_MANAGE
|
||||||
|
].find(e => this.channelTree.client.permissions.neededPermission(e).granted(1));
|
||||||
|
|
||||||
let flagDelete = true;
|
let flagDelete = true;
|
||||||
if(this.clients(true).length > 0)
|
if(this.clients(true).length > 0)
|
||||||
|
@ -522,8 +525,7 @@ class ChannelEntry {
|
||||||
name: tr("Show channel info"),
|
name: tr("Show channel info"),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
trigger_close = false;
|
trigger_close = false;
|
||||||
|
Modals.openChannelInfo(this);
|
||||||
alert('TODO!');
|
|
||||||
},
|
},
|
||||||
icon_class: "client-about"
|
icon_class: "client-about"
|
||||||
},
|
},
|
||||||
|
|
|
@ -384,20 +384,7 @@ class ClientEntry {
|
||||||
type: contextmenu.MenuEntryType.ENTRY,
|
type: contextmenu.MenuEntryType.ENTRY,
|
||||||
icon_class: "client-permission_server_groups",
|
icon_class: "client-permission_server_groups",
|
||||||
name: "Server groups dialog",
|
name: "Server groups dialog",
|
||||||
callback: () => {
|
callback: () => this.open_assignment_modal()
|
||||||
Modals.createServerGroupAssignmentModal(this, (group, flag) => {
|
|
||||||
if(flag) {
|
|
||||||
return this.channelTree.client.serverConnection.send_command("servergroupaddclient", {
|
|
||||||
sgid: group.id,
|
|
||||||
cldbid: this.properties.client_database_id
|
|
||||||
}).then(result => true);
|
|
||||||
} else
|
|
||||||
return this.channelTree.client.serverConnection.send_command("servergroupdelclient", {
|
|
||||||
sgid: group.id,
|
|
||||||
cldbid: this.properties.client_database_id
|
|
||||||
}).then(result => true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
contextmenu.Entry.HR(),
|
contextmenu.Entry.HR(),
|
||||||
...server_groups
|
...server_groups
|
||||||
|
@ -430,6 +417,33 @@ class ClientEntry {
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open_assignment_modal() {
|
||||||
|
Modals.createServerGroupAssignmentModal(this, (groups, flag) => {
|
||||||
|
if(groups.length == 0) return Promise.resolve(true);
|
||||||
|
|
||||||
|
if(groups.length == 1) {
|
||||||
|
if(flag) {
|
||||||
|
return this.channelTree.client.serverConnection.send_command("servergroupaddclient", {
|
||||||
|
sgid: groups[0],
|
||||||
|
cldbid: this.properties.client_database_id
|
||||||
|
}).then(result => true);
|
||||||
|
} else
|
||||||
|
return this.channelTree.client.serverConnection.send_command("servergroupdelclient", {
|
||||||
|
sgid: groups[0],
|
||||||
|
cldbid: this.properties.client_database_id
|
||||||
|
}).then(result => true);
|
||||||
|
} else {
|
||||||
|
const data = groups.map(e => { return {sgid: e}; });
|
||||||
|
data[0]["cldbid"] = this.properties.client_database_id;
|
||||||
|
|
||||||
|
if(flag) {
|
||||||
|
return this.channelTree.client.serverConnection.send_command("clientaddservergroup", data, {flagset: ["continueonerror"]}).then(result => true);
|
||||||
|
} else
|
||||||
|
return this.channelTree.client.serverConnection.send_command("clientdelservergroup", data, {flagset: ["continueonerror"]}).then(result => true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
open_text_chat() {
|
open_text_chat() {
|
||||||
const chat = this.channelTree.client.side_bar;
|
const chat = this.channelTree.client.side_bar;
|
||||||
const conversation = chat.private_conversations().find_conversation({
|
const conversation = chat.private_conversations().find_conversation({
|
||||||
|
@ -441,7 +455,6 @@ class ClientEntry {
|
||||||
create: true
|
create: true
|
||||||
});
|
});
|
||||||
chat.private_conversations().set_selected_conversation(conversation);
|
chat.private_conversations().set_selected_conversation(conversation);
|
||||||
/* TODO: Check if auto switch to private conversations is enabled */
|
|
||||||
chat.show_private_conversations();
|
chat.show_private_conversations();
|
||||||
chat.private_conversations().try_input_focus();
|
chat.private_conversations().try_input_focus();
|
||||||
}
|
}
|
||||||
|
@ -458,7 +471,16 @@ class ClientEntry {
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.open_text_chat();
|
this.open_text_chat();
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
|
contextmenu.Entry.HR(),
|
||||||
|
{
|
||||||
|
type: contextmenu.MenuEntryType.ENTRY,
|
||||||
|
icon_class: "client-about",
|
||||||
|
name: tr("Show client info"),
|
||||||
|
callback: () => Modals.openClientInfo(this)
|
||||||
|
},
|
||||||
|
contextmenu.Entry.HR(),
|
||||||
|
{
|
||||||
type: contextmenu.MenuEntryType.ENTRY,
|
type: contextmenu.MenuEntryType.ENTRY,
|
||||||
icon_class: "client-poke",
|
icon_class: "client-poke",
|
||||||
name: tr("Poke client"),
|
name: tr("Poke client"),
|
||||||
|
@ -547,7 +569,10 @@ class ClientEntry {
|
||||||
name: tr("Ban client"),
|
name: tr("Ban client"),
|
||||||
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
Modals.spawnBanClient(this.properties.client_nickname, (data) => {
|
Modals.spawnBanClient(this.channelTree.client, [{
|
||||||
|
name: this.properties.client_nickname,
|
||||||
|
unique_id: this.properties.client_unique_identifier
|
||||||
|
}], (data) => {
|
||||||
this.channelTree.client.serverConnection.send_command("banclient", {
|
this.channelTree.client.serverConnection.send_command("banclient", {
|
||||||
uid: this.properties.client_unique_identifier,
|
uid: this.properties.client_unique_identifier,
|
||||||
banreason: data.reason,
|
banreason: data.reason,
|
||||||
|
@ -617,6 +642,14 @@ class ClientEntry {
|
||||||
.attr("client-id", this.clientId());
|
.attr("client-id", this.clientId());
|
||||||
|
|
||||||
|
|
||||||
|
/* unread marker */
|
||||||
|
{
|
||||||
|
container_client.append(
|
||||||
|
$.spawn("div")
|
||||||
|
.addClass("marker-text-unread hidden")
|
||||||
|
.attr("private-conversation", this._clientId)
|
||||||
|
);
|
||||||
|
}
|
||||||
container_client.append(
|
container_client.append(
|
||||||
$.spawn("div")
|
$.spawn("div")
|
||||||
.addClass("icon_client_state")
|
.addClass("icon_client_state")
|
||||||
|
@ -1055,6 +1088,10 @@ class ClientEntry {
|
||||||
this._info_connection_promise_resolve = undefined;
|
this._info_connection_promise_resolve = undefined;
|
||||||
this._info_connection_promise_reject = undefined;
|
this._info_connection_promise_reject = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set flag_text_unread(flag: boolean) {
|
||||||
|
this._tag.find(".marker-text-unread").toggleClass("hidden", !flag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalClientEntry extends ClientEntry {
|
class LocalClientEntry extends ClientEntry {
|
||||||
|
@ -1383,7 +1420,7 @@ class MusicClientEntry extends ClientEntry {
|
||||||
},
|
},
|
||||||
type: contextmenu.MenuEntryType.ENTRY
|
type: contextmenu.MenuEntryType.ENTRY
|
||||||
},
|
},
|
||||||
contextmenu.Entry.CLOSE(() => (trigger_close ? on_close : (() => {}))())
|
contextmenu.Entry.CLOSE(() => trigger_close && on_close())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,11 +130,13 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
||||||
tag.append($.spawn("div").addClass(icon));
|
tag.append($.spawn("div").addClass(icon));
|
||||||
tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
|
tag.append($.spawn("div").html($.isFunction(entry.name) ? entry.name() : entry.name));
|
||||||
|
|
||||||
if(entry.disabled || entry.invalidPermission) tag.addClass("disabled");
|
if(entry.disabled || entry.invalidPermission)
|
||||||
|
tag.addClass("disabled");
|
||||||
else {
|
else {
|
||||||
tag.click( () => {
|
tag.on('click', () => {
|
||||||
if($.isFunction(entry.callback))
|
if($.isFunction(entry.callback))
|
||||||
entry.callback();
|
entry.callback();
|
||||||
|
entry.callback = undefined; /* for some reason despawn_context_menu() causes a second click event? */
|
||||||
this.despawn_context_menu();
|
this.despawn_context_menu();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -151,9 +153,10 @@ class HTMLContextMenuProvider implements contextmenu.ContextMenuProvider {
|
||||||
if(entry.disabled || entry.invalidPermission)
|
if(entry.disabled || entry.invalidPermission)
|
||||||
tag.addClass("disabled");
|
tag.addClass("disabled");
|
||||||
else {
|
else {
|
||||||
tag.click( () => {
|
tag.on('click', () => {
|
||||||
if($.isFunction(entry.callback))
|
if($.isFunction(entry.callback))
|
||||||
entry.callback();
|
entry.callback();
|
||||||
|
entry.callback = undefined; /* for some reason despawn_context_menu() causes a second click event? */
|
||||||
this.despawn_context_menu();
|
this.despawn_context_menu();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,11 +470,11 @@ class ControlBar {
|
||||||
this.connection_handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
this.connection_handler.handleDisconnect(DisconnectReason.REQUESTED); //TODO message?
|
||||||
this.update_connection_state();
|
this.update_connection_state();
|
||||||
this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED);
|
this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED);
|
||||||
this.log.log(log.server.Type.DISCONNECTED, {});
|
this.connection_handler.log.log(log.server.Type.DISCONNECTED, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private on_token_use() {
|
private on_token_use() {
|
||||||
createInputModal(tr("Use token"), tr("Please enter your token/priviledge key"), message => message.length > 0, result => {
|
createInputModal(tr("Use token"), tr("Please enter your token/privilege key"), message => message.length > 0, result => {
|
||||||
if(!result) return;
|
if(!result) return;
|
||||||
if(this.connection_handler.serverConnection.connected)
|
if(this.connection_handler.serverConnection.connected)
|
||||||
this.connection_handler.serverConnection.send_command("tokenuse", {
|
this.connection_handler.serverConnection.send_command("tokenuse", {
|
||||||
|
|
|
@ -395,7 +395,7 @@ namespace top_menu {
|
||||||
item.icon("client-token_use");
|
item.icon("client-token_use");
|
||||||
item.click(() => {
|
item.click(() => {
|
||||||
//TODO: Fixeme use one method for the control bar and here!
|
//TODO: Fixeme use one method for the control bar and here!
|
||||||
createInputModal(tr("Use token"), tr("Please enter your token/priviledge key"), message => message.length > 0, result => {
|
createInputModal(tr("Use token"), tr("Please enter your token/privilege key"), message => message.length > 0, result => {
|
||||||
if(!result) return;
|
if(!result) return;
|
||||||
const scon = server_connections.active_connection_handler();
|
const scon = server_connections.active_connection_handler();
|
||||||
|
|
||||||
|
|
|
@ -908,6 +908,7 @@ test
|
||||||
OPEN,
|
OPEN,
|
||||||
CLOSED,
|
CLOSED,
|
||||||
DISCONNECTED,
|
DISCONNECTED,
|
||||||
|
DISCONNECTED_SELF,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DisplayedMessage = {
|
export type DisplayedMessage = {
|
||||||
|
@ -1096,7 +1097,7 @@ test
|
||||||
tag_message: basic_view_entry.html_tag,
|
tag_message: basic_view_entry.html_tag,
|
||||||
tag_timepointer: undefined,
|
tag_timepointer: undefined,
|
||||||
tag_unread: undefined
|
tag_unread: undefined
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1240,7 +1241,7 @@ test
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _build_spacer(message: string, type: "date" | "new" | "disconnect" | "reconnect" | "closed" | "error") : PrivateConversationViewSpacer {
|
private _build_spacer(message: string, type: "date" | "new" | "disconnect" | "disconnect_self" | "reconnect" | "closed" | "error") : PrivateConversationViewSpacer {
|
||||||
const tag = $("#tmpl_frame_chat_private_spacer").renderTag({
|
const tag = $("#tmpl_frame_chat_private_spacer").renderTag({
|
||||||
message: message
|
message: message
|
||||||
}).addClass("type-" + type);
|
}).addClass("type-" + type);
|
||||||
|
@ -1249,7 +1250,7 @@ test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _register_displayed_message(message: DisplayedMessage) {
|
private _register_displayed_message(message: DisplayedMessage, update_new: boolean) {
|
||||||
const message_date = new Date(message.timestamp);
|
const message_date = new Date(message.timestamp);
|
||||||
|
|
||||||
/* before := older message; after := newer message */
|
/* before := older message; after := newer message */
|
||||||
|
@ -1277,7 +1278,7 @@ test
|
||||||
while(this._displayed_messages.length > this._displayed_messages_length)
|
while(this._displayed_messages.length > this._displayed_messages_length)
|
||||||
this._destroy_displayed_message(this._displayed_messages.last(), true);
|
this._destroy_displayed_message(this._displayed_messages.last(), true);
|
||||||
|
|
||||||
const flag_new_message = index == 0 && (message.message_type === "spacer" || (<PrivateConversationViewMessage>message.message).sender === "partner");
|
const flag_new_message = update_new && index == 0 && (message.message_type === "spacer" || (<PrivateConversationViewMessage>message.message).sender === "partner");
|
||||||
|
|
||||||
/* Timeline for before - now */
|
/* Timeline for before - now */
|
||||||
{
|
{
|
||||||
|
@ -1411,6 +1412,10 @@ test
|
||||||
this._spacer_unread_message.tag_unread.html_tag.insertBefore(this._spacer_unread_message.tag_message);
|
this._spacer_unread_message.tag_unread.html_tag.insertBefore(this._spacer_unread_message.tag_message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const ctree = this.handle.handle.handle.channelTree;
|
||||||
|
if(ctree && ctree.tag_tree() && this.client_id)
|
||||||
|
ctree.tag_tree().find(".marker-text-unread[private-conversation='" + this.client_id + "']").addClass("hidden");
|
||||||
|
|
||||||
if(this._spacer_unread_message) {
|
if(this._spacer_unread_message) {
|
||||||
this._destroy_view_entry(this._spacer_unread_message.tag_unread);
|
this._destroy_view_entry(this._spacer_unread_message.tag_unread);
|
||||||
this._spacer_unread_message.tag_unread = undefined;
|
this._spacer_unread_message.tag_unread = undefined;
|
||||||
|
@ -1427,14 +1432,16 @@ test
|
||||||
|
|
||||||
is_unread() : boolean { return !!this._spacer_unread_message; }
|
is_unread() : boolean { return !!this._spacer_unread_message; }
|
||||||
|
|
||||||
private _append_state_change(state: "disconnect" | "reconnect" | "closed") {
|
private _append_state_change(state: "disconnect" | "disconnect_self" | "reconnect" | "closed") {
|
||||||
let message;
|
let message;
|
||||||
if(state == "closed")
|
if(state == "closed")
|
||||||
message = tr("Your chat partner has closed the conversation");
|
message = tr("Your chat partner has closed the conversation");
|
||||||
else if(state == "reconnect")
|
else if(state == "reconnect")
|
||||||
message = "Your chat partner has reconnected";
|
message = this._state === PrivateConversationState.DISCONNECTED_SELF ?tr("You've reconnected to the server") : tr("Your chat partner has reconnected");
|
||||||
|
else if(state === "disconnect")
|
||||||
|
message = tr("Your chat partner has disconnected");
|
||||||
else
|
else
|
||||||
message = "Your chat partner has disconnected";
|
message = tr("You've disconnected from the server");
|
||||||
|
|
||||||
const spacer = this._build_spacer(message, state);
|
const spacer = this._build_spacer(message, state);
|
||||||
this._register_displayed_message({
|
this._register_displayed_message({
|
||||||
|
@ -1444,7 +1451,7 @@ test
|
||||||
tag_message: spacer.html_tag,
|
tag_message: spacer.html_tag,
|
||||||
tag_timepointer: undefined,
|
tag_timepointer: undefined,
|
||||||
tag_unread: undefined
|
tag_unread: undefined
|
||||||
});
|
}, state === "disconnect");
|
||||||
}
|
}
|
||||||
|
|
||||||
state() : PrivateConversationState {
|
state() : PrivateConversationState {
|
||||||
|
@ -1462,6 +1469,8 @@ test
|
||||||
this._append_state_change("reconnect");
|
this._append_state_change("reconnect");
|
||||||
else if(state == PrivateConversationState.CLOSED)
|
else if(state == PrivateConversationState.CLOSED)
|
||||||
this._append_state_change("closed");
|
this._append_state_change("closed");
|
||||||
|
else if(state == PrivateConversationState.DISCONNECTED_SELF)
|
||||||
|
this._append_state_change("disconnect_self");
|
||||||
|
|
||||||
this._state = state;
|
this._state = state;
|
||||||
}
|
}
|
||||||
|
@ -1485,7 +1494,7 @@ test
|
||||||
tag_message: spacer.html_tag,
|
tag_message: spacer.html_tag,
|
||||||
tag_timepointer: undefined,
|
tag_timepointer: undefined,
|
||||||
tag_unread: undefined
|
tag_unread: undefined
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
call_message(message: string) {
|
call_message(message: string) {
|
||||||
|
@ -1574,7 +1583,10 @@ test
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_client_ids() {
|
clear_client_ids() {
|
||||||
this._conversations.forEach(e => e.client_id = 0);
|
this._conversations.forEach(e => {
|
||||||
|
e.client_id = 0;
|
||||||
|
e.set_state(PrivateConversationState.DISCONNECTED_SELF);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
html_tag() : JQuery { return this._html_tag; }
|
html_tag() : JQuery { return this._html_tag; }
|
||||||
|
|
|
@ -3,102 +3,178 @@
|
||||||
/// <reference path="../../proto.ts" />
|
/// <reference path="../../proto.ts" />
|
||||||
|
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
export function spawnBanClient(name: string | string[], callback: (data: {
|
export type BanEntry = {
|
||||||
|
name?: string;
|
||||||
|
unique_id: string;
|
||||||
|
}
|
||||||
|
export function spawnBanClient(client: ConnectionHandler, entries: BanEntry | BanEntry[], callback: (data: {
|
||||||
length: number,
|
length: number,
|
||||||
reason: string,
|
reason: string,
|
||||||
no_name: boolean,
|
no_name: boolean,
|
||||||
no_ip: boolean,
|
no_ip: boolean,
|
||||||
no_hwid: boolean
|
no_hwid: boolean
|
||||||
}) => void) {
|
}) => void) {
|
||||||
const connectModal = createModal({
|
const max_ban_time = client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value;
|
||||||
header: function() {
|
|
||||||
return tr("Ban client");
|
const permission_criteria_hwid = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_HWID).granted(1);
|
||||||
},
|
const permission_criteria_ip = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_IP).granted(1);
|
||||||
|
const permission_criteria_name = client.permissions.neededPermission(PermissionType.B_CLIENT_BAN_NAME).granted(1);
|
||||||
|
|
||||||
|
const modal = createModal({
|
||||||
|
header: Array.isArray(entries) ? tr("Ban clients") : tr("Ban client"),
|
||||||
body: function () {
|
body: function () {
|
||||||
let tag = $("#tmpl_client_ban").renderTag({
|
let template = $("#tmpl_client_ban").renderTag({entries: entries});
|
||||||
client_name: $.isArray(name) ? '"' + name.join('", "') + '"' : name
|
|
||||||
});
|
|
||||||
|
|
||||||
let maxTime = 0; //globalClient.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value;
|
let update_duration;
|
||||||
let unlimited = maxTime == 0 || maxTime == -1;
|
let update_button_ok;
|
||||||
if(unlimited) maxTime = 0;
|
const button_ok = template.find(".button-apply");
|
||||||
|
const button_cancel = template.find(".button-cancel");
|
||||||
|
|
||||||
let banTag = tag.find(".ban_duration_type");
|
const input_duration_value = template.find(".container-duration input").on('change keyup', () => update_duration());
|
||||||
let durationTag = tag.find(".ban_duration");
|
const input_duration_type = template.find(".container-duration select").on('change keyup', () => update_duration());
|
||||||
banTag.find("option[value=\"sec\"]").prop("disabled", !unlimited && 1 > maxTime)
|
|
||||||
.attr("duration-scale", 1)
|
|
||||||
.attr("duration-max", maxTime);
|
|
||||||
banTag.find("option[value=\"min\"]").prop("disabled", !unlimited && 60 > maxTime)
|
|
||||||
.attr("duration-scale", 60)
|
|
||||||
.attr("duration-max", maxTime / 60);
|
|
||||||
banTag.find("option[value=\"hours\"]").prop("disabled", !unlimited && 60 * 60 > maxTime)
|
|
||||||
.attr("duration-scale", 60 * 60)
|
|
||||||
.attr("duration-max", maxTime / (60 * 60));
|
|
||||||
banTag.find("option[value=\"days\"]").prop("disabled", !unlimited && 60 * 60 * 24 > maxTime)
|
|
||||||
.attr("duration-scale", 60 * 60 * 24)
|
|
||||||
.attr("duration-max", maxTime / (60 * 60 * 24));
|
|
||||||
banTag.find("option[value=\"perm\"]").prop("disabled", !unlimited)
|
|
||||||
.attr("duration-scale", 0);
|
|
||||||
|
|
||||||
durationTag.change(() => banTag.trigger('change'));
|
const container_reason = template.find(".container-reason");
|
||||||
|
|
||||||
banTag.change(event => {
|
const criteria_nickname = template.find(".criteria.nickname input")
|
||||||
let element = $((event.target as HTMLSelectElement).selectedOptions.item(0));
|
.prop('checked', permission_criteria_name).prop("disabled", !permission_criteria_name)
|
||||||
if(element.val() !== "perm") {
|
.firstParent(".checkbox").toggleClass("disabled", !permission_criteria_name);
|
||||||
durationTag.prop("disabled", false);
|
|
||||||
|
|
||||||
let current = durationTag.val() as number;
|
const criteria_ip_address = template.find(".criteria.ip-address input")
|
||||||
let max = parseInt(element.attr("duration-max"));
|
.prop('checked', permission_criteria_ip).prop("disabled", !permission_criteria_ip)
|
||||||
if (max > 0 && current > max)
|
.firstParent(".checkbox").toggleClass("disabled", !permission_criteria_ip);
|
||||||
durationTag.val(max);
|
|
||||||
else if(current <= 0)
|
|
||||||
durationTag.val(1);
|
|
||||||
durationTag.attr("max", max);
|
|
||||||
} else {
|
|
||||||
durationTag.prop("disabled", true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return tag;
|
const criteria_hardware_id = template.find(".criteria.hardware-id input")
|
||||||
|
.prop('checked', permission_criteria_hwid).prop("disabled", !permission_criteria_hwid)
|
||||||
|
.firstParent(".checkbox").toggleClass("disabled", !permission_criteria_hwid);
|
||||||
|
|
||||||
|
/* duration input handler */
|
||||||
|
{
|
||||||
|
const tooltip_duration_max = template.find(".tooltip-max-time a.max");
|
||||||
|
|
||||||
|
update_duration = () => {
|
||||||
|
const type = input_duration_type.val() as string;
|
||||||
|
const value = parseInt(input_duration_value.val() as string);
|
||||||
|
const disabled = input_duration_type.prop("disabled");
|
||||||
|
|
||||||
|
input_duration_value.prop("disabled", type === "perm" || disabled).firstParent(".input-boxed").toggleClass("disabled", type === "perm" || disabled);
|
||||||
|
if(type !== "perm") {
|
||||||
|
if(input_duration_value.attr("x-saved-value")) {
|
||||||
|
input_duration_value.val(parseInt(input_duration_value.attr("x-saved-value")));
|
||||||
|
input_duration_value.attr("x-saved-value", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const selected_option = input_duration_type.find("option[value='" + type + "']");
|
||||||
|
const max = parseInt(selected_option.attr("duration-max"));
|
||||||
|
|
||||||
|
input_duration_value.attr("max", max);
|
||||||
|
if((value > max && max != -1) || value < 1) {
|
||||||
|
input_duration_value.firstParent(".input-boxed").addClass("is-invalid");
|
||||||
|
} else {
|
||||||
|
input_duration_value.firstParent(".input-boxed").removeClass("is-invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(max != -1)
|
||||||
|
tooltip_duration_max.html(tr("You're allowed to ban a maximum of ") + "<b>" + max + " " + duration_data[type][max == 1 ? "1-text" : "text"] + "</b>");
|
||||||
|
else
|
||||||
|
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
|
||||||
|
} else {
|
||||||
|
if(value && !Number.isNaN(value))
|
||||||
|
input_duration_value.attr("x-saved-value", value);
|
||||||
|
input_duration_value.attr("placeholder", tr("for ever")).val(null);
|
||||||
|
tooltip_duration_max.html(tr("You're allowed to ban <b>permanent</b>."));
|
||||||
|
}
|
||||||
|
update_button_ok && update_button_ok();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* initialize ban time */
|
||||||
|
Promise.resolve(max_ban_time).catch(error => { /* TODO: Error handling? */ return 0; }).then(max_time => {
|
||||||
|
let unlimited = max_time == 0 || max_time == -1;
|
||||||
|
if(unlimited || typeof(max_time) === "undefined") max_time = 0;
|
||||||
|
|
||||||
|
for(const value of Object.keys(duration_data)) {
|
||||||
|
input_duration_type.find("option[value='" + value + "']")
|
||||||
|
.prop("disabled", !unlimited && max_time >= duration_data[value].scale)
|
||||||
|
.attr("duration-scale", duration_data[value].scale)
|
||||||
|
.attr("duration-max", unlimited ? -1 : Math.floor(max_time / duration_data[value].scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
input_duration_type.find("option[value='perm']")
|
||||||
|
.prop("disabled", !unlimited)
|
||||||
|
.attr("duration-scale", 0)
|
||||||
|
.attr("duration-max", -1);
|
||||||
|
update_duration();
|
||||||
|
});
|
||||||
|
|
||||||
|
update_duration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ban reason */
|
||||||
|
{
|
||||||
|
const input = container_reason.find("textarea");
|
||||||
|
|
||||||
|
const insert_tag = (open: string, close: string) => {
|
||||||
|
if(input.prop("disabled"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const node = input[0] as HTMLTextAreaElement;
|
||||||
|
if (node.selectionStart || node.selectionStart == 0) {
|
||||||
|
const startPos = node.selectionStart;
|
||||||
|
const endPos = node.selectionEnd;
|
||||||
|
node.value = node.value.substring(0, startPos) + open + node.value.substring(startPos, endPos) + close + node.value.substring(endPos);
|
||||||
|
node.selectionEnd = endPos + open.length;
|
||||||
|
node.selectionStart = node.selectionEnd;
|
||||||
|
} else {
|
||||||
|
node.value += open + close;
|
||||||
|
node.selectionEnd = node.value.length - close.length;
|
||||||
|
node.selectionStart = node.selectionEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.focus().trigger('change');
|
||||||
|
};
|
||||||
|
|
||||||
|
container_reason.find(".button-bold").on('click', () => insert_tag('[b]', '[/b]'));
|
||||||
|
container_reason.find(".button-italic").on('click', () => insert_tag('[i]', '[/i]'));
|
||||||
|
container_reason.find(".button-underline").on('click', () => insert_tag('[u]', '[/u]'));
|
||||||
|
container_reason.find(".button-color input").on('change', event => {
|
||||||
|
insert_tag('[color=' + (event.target as HTMLInputElement).value + ']', '[/color]')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* buttons */
|
||||||
|
{
|
||||||
|
button_cancel.on('click', event => modal.close());
|
||||||
|
button_ok.on('click', event => {
|
||||||
|
const duration = input_duration_type.val() === "perm" ? 0 : (1000 * parseInt(input_duration_type.find("option[value='" + input_duration_type.val() + "']").attr("duration-scale")) * parseInt(input_duration_value.val() as string));
|
||||||
|
|
||||||
|
modal.close();
|
||||||
|
callback({
|
||||||
|
length: Math.floor(duration / 1000),
|
||||||
|
reason: container_reason.find("textarea").val() as string,
|
||||||
|
|
||||||
|
no_hwid: !criteria_hardware_id.find("input").prop("checked"),
|
||||||
|
no_ip: !criteria_ip_address.find("input").prop("checked"),
|
||||||
|
no_name: !criteria_nickname.find("input").prop("checked")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputs = template.find(".input-boxed");
|
||||||
|
update_button_ok = () => {
|
||||||
|
const invalid = [...inputs].find(e => $(e).hasClass("is-invalid"));
|
||||||
|
button_ok.prop('disabled', !!invalid);
|
||||||
|
};
|
||||||
|
update_button_ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip(template);
|
||||||
|
return template.children();
|
||||||
},
|
},
|
||||||
footer: function () {
|
footer: null,
|
||||||
let tag = $.spawn("div");
|
|
||||||
tag.css("text-align", "right");
|
|
||||||
tag.css("margin-top", "3px");
|
|
||||||
tag.css("margin-bottom", "6px");
|
|
||||||
tag.addClass("modal-button-group");
|
|
||||||
|
|
||||||
let buttonCancel = $.spawn("button");
|
min_width: "10em",
|
||||||
buttonCancel.text("Cancel");
|
width: "30em"
|
||||||
buttonCancel.on("click", () => connectModal.close());
|
|
||||||
tag.append(buttonCancel);
|
|
||||||
|
|
||||||
|
|
||||||
let buttonOk = $.spawn("button");
|
|
||||||
buttonOk.text("OK").addClass("btn_success");
|
|
||||||
tag.append(buttonOk);
|
|
||||||
return tag;
|
|
||||||
},
|
|
||||||
|
|
||||||
width: 450
|
|
||||||
});
|
});
|
||||||
connectModal.open();
|
modal.open();
|
||||||
|
|
||||||
connectModal.htmlTag.find(".btn_success").on('click', () => {
|
modal.htmlTag.find(".modal-body").addClass("modal-ban-client");
|
||||||
connectModal.close();
|
|
||||||
|
|
||||||
let length = connectModal.htmlTag.find(".ban_duration").val() as number;
|
|
||||||
let duration = connectModal.htmlTag.find(".ban_duration_type option:selected");
|
|
||||||
console.log(duration);
|
|
||||||
console.log(length + "*" + duration.attr("duration-scale"));
|
|
||||||
|
|
||||||
callback({
|
|
||||||
length: length * parseInt(duration.attr("duration-scale")),
|
|
||||||
reason: connectModal.htmlTag.find(".ban_reason").val() as string,
|
|
||||||
no_hwid: !connectModal.htmlTag.find(".ban-type-hardware-id").prop("checked"),
|
|
||||||
no_ip: !connectModal.htmlTag.find(".ban-type-ip").prop("checked"),
|
|
||||||
no_name: !connectModal.htmlTag.find(".ban-type-nickname").prop("checked")
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,147 +0,0 @@
|
||||||
namespace Modals {
|
|
||||||
export function spawnBanCreate(connection: ConnectionHandler, base?: BanEntry, callback?: (entry?: BanEntry) => any) {
|
|
||||||
let result: BanEntry = {} as any;
|
|
||||||
result.banid = base ? base.banid : 0;
|
|
||||||
|
|
||||||
let modal: Modal;
|
|
||||||
modal = createModal({
|
|
||||||
header: base && base.banid > 0 ? tr("Edit ban") : tr("Add ban"),
|
|
||||||
body: () => {
|
|
||||||
let template = $("#tmpl_ban_create").renderTag();
|
|
||||||
|
|
||||||
const input_name = template.find(".input-name");
|
|
||||||
const input_name_type = template.find(".input-name-type");
|
|
||||||
const input_ip = template.find(".input-ip");
|
|
||||||
const input_uid = template.find(".input-uid");
|
|
||||||
const input_reason = template.find(".input-reason");
|
|
||||||
const input_time = template.find(".input-time");
|
|
||||||
const input_time_type = template.find(".input-time-unit");
|
|
||||||
const input_hwid = template.find(".input-hwid");
|
|
||||||
const input_global = template.find(".input-global");
|
|
||||||
|
|
||||||
{
|
|
||||||
let maxTime = 0; //globalClient.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).value;
|
|
||||||
let unlimited = maxTime == 0 || maxTime == -1;
|
|
||||||
if(unlimited) maxTime = 0;
|
|
||||||
|
|
||||||
input_time_type.find("option[value=\"sec\"]").prop("disabled", !unlimited && 1 > maxTime)
|
|
||||||
.attr("duration-scale", 1)
|
|
||||||
.attr("duration-max", maxTime);
|
|
||||||
input_time_type.find("option[value=\"min\"]").prop("disabled", !unlimited && 60 > maxTime)
|
|
||||||
.attr("duration-scale", 60)
|
|
||||||
.attr("duration-max", maxTime / 60);
|
|
||||||
input_time_type.find("option[value=\"hours\"]").prop("disabled", !unlimited && 60 * 60 > maxTime)
|
|
||||||
.attr("duration-scale", 60 * 60)
|
|
||||||
.attr("duration-max", maxTime / (60 * 60));
|
|
||||||
input_time_type.find("option[value=\"days\"]").prop("disabled", !unlimited && 60 * 60 * 24 > maxTime)
|
|
||||||
.attr("duration-scale", 60 * 60 * 24)
|
|
||||||
.attr("duration-max", maxTime / (60 * 60 * 24));
|
|
||||||
input_time_type.find("option[value=\"perm\"]").prop("disabled", !unlimited)
|
|
||||||
.attr("duration-scale", 0);
|
|
||||||
|
|
||||||
input_time_type.change(event => {
|
|
||||||
let element = $((event.target as HTMLSelectElement).selectedOptions.item(0));
|
|
||||||
if(element.val() !== "perm") {
|
|
||||||
input_time.prop("disabled", false);
|
|
||||||
|
|
||||||
let current = input_time.val() as number;
|
|
||||||
let max = parseInt(element.attr("duration-max"));
|
|
||||||
if (max > 0 && current > max)
|
|
||||||
input_time.val(max);
|
|
||||||
else if(current <= 0)
|
|
||||||
input_time.val(1);
|
|
||||||
input_time.attr("max", max);
|
|
||||||
} else {
|
|
||||||
input_time.prop("disabled", true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template.find('input, textarea').on('keyup change', event => {
|
|
||||||
let valid = false;
|
|
||||||
|
|
||||||
if(input_name.val() || input_ip.val() || input_uid.val())
|
|
||||||
valid = true;
|
|
||||||
|
|
||||||
modal.htmlTag.find(".button-success").prop("disabled", !valid);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(base) {
|
|
||||||
input_ip.val(base.ip);
|
|
||||||
input_uid.val(base.unique_id);
|
|
||||||
input_name.val(base.name);
|
|
||||||
input_hwid.val(base.hardware_id);
|
|
||||||
|
|
||||||
(input_name_type[0] as HTMLSelectElement).selectedIndex = base.name_type || 0;
|
|
||||||
input_reason.val(base.reason);
|
|
||||||
|
|
||||||
if(base.timestamp_expire.getTime() == 0) {
|
|
||||||
input_time_type.find("option[value=\"perm\"]").prop("selected", true);
|
|
||||||
} else {
|
|
||||||
const time = (base.timestamp_expire.getTime() - base.timestamp_created.getTime()) / 1000;
|
|
||||||
if(time % (60 * 60 * 24) === 0) {
|
|
||||||
input_time_type.find("option[value=\"days\"]").prop("selected", true);
|
|
||||||
input_time.val(time / (60 * 60 * 24));
|
|
||||||
} else if(time % (60 * 60) === 0) {
|
|
||||||
input_time_type.find("option[value=\"hours\"]").prop("selected", true);
|
|
||||||
input_time.val(time / (60 * 60));
|
|
||||||
} else if(time % (60) === 0) {
|
|
||||||
input_time_type.find("option[value=\"min\"]").prop("selected", true);
|
|
||||||
input_time.val(time / (60));
|
|
||||||
} else {
|
|
||||||
input_time_type.find("option[value=\"sec\"]").prop("selected", true);
|
|
||||||
input_time.val(time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template.find(".container-global").detach(); //We cant edit this
|
|
||||||
input_global.prop("checked", base.server_id == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(connection && connection.permissions)
|
|
||||||
input_global.prop("disabled", !connection.permissions.neededPermission(base ? PermissionType.B_CLIENT_BAN_EDIT_GLOBAL : PermissionType.B_CLIENT_BAN_CREATE_GLOBAL));
|
|
||||||
|
|
||||||
return template;
|
|
||||||
},
|
|
||||||
footer: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.htmlTag.find(".button-close").on('click', () => modal.close());
|
|
||||||
modal.htmlTag.find(".button-success").on('click', () => {
|
|
||||||
{
|
|
||||||
let length = modal.htmlTag.find(".input-time").val() as number;
|
|
||||||
let duration = modal.htmlTag.find(".input-time-unit option:selected");
|
|
||||||
|
|
||||||
console.log(duration);
|
|
||||||
console.log(length + "*" + duration.attr("duration-scale"));
|
|
||||||
const time = length * parseInt(duration.attr("duration-scale"));
|
|
||||||
|
|
||||||
if(!result.timestamp_created)
|
|
||||||
result.timestamp_created = new Date();
|
|
||||||
if(time > 0)
|
|
||||||
result.timestamp_expire = new Date(result.timestamp_created.getTime() + time * 1000);
|
|
||||||
else
|
|
||||||
result.timestamp_expire = new Date(0);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
result.name = modal.htmlTag.find(".input-name").val() as string;
|
|
||||||
{
|
|
||||||
const name_type = modal.htmlTag.find(".input-name-type") as JQuery<HTMLSelectElement>;
|
|
||||||
result.name_type = name_type[0].selectedIndex;
|
|
||||||
}
|
|
||||||
result.ip = modal.htmlTag.find(".input-ip").val() as string;
|
|
||||||
result.unique_id = modal.htmlTag.find(".input-uid").val() as string;
|
|
||||||
result.reason = modal.htmlTag.find(".input-reason").val() as string;
|
|
||||||
result.hardware_id = modal.htmlTag.find(".input-hwid").val() as string;
|
|
||||||
result.server_id = modal.htmlTag.find(".input-global").prop("checked") ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
modal.close();
|
|
||||||
if(callback) callback(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
modal.htmlTag.find("input").trigger("change");
|
|
||||||
|
|
||||||
modal.open();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -233,7 +233,7 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note: This object must be sorted (from shortest to longest)!
|
//Note: This object must be sorted (from shortest to longest)!
|
||||||
const duration_data = {
|
export const duration_data = {
|
||||||
"sec": {
|
"sec": {
|
||||||
"text": tr("Seconds"),
|
"text": tr("Seconds"),
|
||||||
"1-text": tr("Second"),
|
"1-text": tr("Second"),
|
||||||
|
|
|
@ -107,7 +107,7 @@ namespace Modals {
|
||||||
};
|
};
|
||||||
|
|
||||||
const container_bookmarks = template.find(".container-bookmarks");
|
const container_bookmarks = template.find(".container-bookmarks");
|
||||||
const update_bookmark_list = () => {
|
const update_bookmark_list = (_current_selected: string) => {
|
||||||
container_bookmarks.empty();
|
container_bookmarks.empty();
|
||||||
selected_bookmark = undefined;
|
selected_bookmark = undefined;
|
||||||
update_selected();
|
update_selected();
|
||||||
|
@ -155,6 +155,8 @@ namespace Modals {
|
||||||
update_buttons();
|
update_buttons();
|
||||||
update_selected();
|
update_selected();
|
||||||
});
|
});
|
||||||
|
if(entry.unique_id === _current_selected)
|
||||||
|
container.trigger('click');
|
||||||
|
|
||||||
hide_links.push(sibling_data.last);
|
hide_links.push(sibling_data.last);
|
||||||
let cindex = 0;
|
let cindex = 0;
|
||||||
|
@ -197,13 +199,13 @@ namespace Modals {
|
||||||
if(answer) {
|
if(answer) {
|
||||||
bookmarks.delete_bookmark(selected_bookmark);
|
bookmarks.delete_bookmark(selected_bookmark);
|
||||||
bookmarks.save_bookmark(selected_bookmark);
|
bookmarks.save_bookmark(selected_bookmark);
|
||||||
update_bookmark_list();
|
update_bookmark_list(undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
bookmarks.delete_bookmark(selected_bookmark);
|
bookmarks.delete_bookmark(selected_bookmark);
|
||||||
bookmarks.save_bookmark(selected_bookmark);
|
bookmarks.save_bookmark(selected_bookmark);
|
||||||
update_bookmark_list();
|
update_bookmark_list(undefined);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -221,7 +223,7 @@ namespace Modals {
|
||||||
result as string
|
result as string
|
||||||
);
|
);
|
||||||
bookmarks.save_bookmark(mark);
|
bookmarks.save_bookmark(mark);
|
||||||
update_bookmark_list();
|
update_bookmark_list(mark.unique_id);
|
||||||
}
|
}
|
||||||
}).open();
|
}).open();
|
||||||
});
|
});
|
||||||
|
@ -243,7 +245,7 @@ namespace Modals {
|
||||||
server_password_hash: ""
|
server_password_hash: ""
|
||||||
}, "");
|
}, "");
|
||||||
bookmarks.save_bookmark(mark);
|
bookmarks.save_bookmark(mark);
|
||||||
update_bookmark_list();
|
update_bookmark_list(mark.unique_id);
|
||||||
}
|
}
|
||||||
}).open();
|
}).open();
|
||||||
});
|
});
|
||||||
|
@ -305,10 +307,49 @@ namespace Modals {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Arrow key navigation for the bookmark list */
|
||||||
|
{
|
||||||
|
let _focused = false;
|
||||||
|
let _focus_listener;
|
||||||
|
let _key_listener;
|
||||||
|
|
||||||
update_bookmark_list();
|
_focus_listener = event => {
|
||||||
|
_focused = false;
|
||||||
|
let element = event.target as HTMLElement;
|
||||||
|
while(element) {
|
||||||
|
if(element === container_bookmarks[0]) {
|
||||||
|
_focused = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
element = element.parentNode as HTMLElement;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_key_listener = event => {
|
||||||
|
if(!_focused) return;
|
||||||
|
|
||||||
|
if(event.key.toLowerCase() === "arrowdown") {
|
||||||
|
container_bookmarks.find(".selected").next().trigger('click');
|
||||||
|
} else if(event.key.toLowerCase() === "arrowup") {
|
||||||
|
container_bookmarks.find(".selected").prev().trigger('click');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', _focus_listener);
|
||||||
|
document.addEventListener('keydown', _key_listener);
|
||||||
|
modal.close_listener.push(() => {
|
||||||
|
document.removeEventListener('click', _focus_listener);
|
||||||
|
document.removeEventListener('keydown', _key_listener);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update_bookmark_list(undefined);
|
||||||
update_buttons();
|
update_buttons();
|
||||||
|
|
||||||
|
template.find(".container-bookmarks").on('keydown', event => {
|
||||||
|
console.error(event.key);
|
||||||
|
});
|
||||||
template.find(".button-close").on('click', event => modal.close());
|
template.find(".button-close").on('click', event => modal.close());
|
||||||
return template.children();
|
return template.children();
|
||||||
},
|
},
|
||||||
|
@ -321,6 +362,7 @@ namespace Modals {
|
||||||
control_bar.update_bookmarks();
|
control_bar.update_bookmarks();
|
||||||
top_menu.rebuild_bookmarks();
|
top_menu.rebuild_bookmarks();
|
||||||
});
|
});
|
||||||
|
|
||||||
modal.open();
|
modal.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
namespace Modals {
|
||||||
|
export function openChannelInfo(channel: ChannelEntry) {
|
||||||
|
let modal: Modal;
|
||||||
|
|
||||||
|
modal = createModal({
|
||||||
|
header: tr("Channel information: ") + channel.channelName(),
|
||||||
|
body: () => {
|
||||||
|
const template = $("#tmpl_channel_info").renderTag();
|
||||||
|
|
||||||
|
const update_values = (container) => {
|
||||||
|
|
||||||
|
apply_channel_description(container.find(".container-description"), channel);
|
||||||
|
apply_general(container, channel);
|
||||||
|
};
|
||||||
|
|
||||||
|
template.find(".button-copy").on('click', event => {
|
||||||
|
copy_to_clipboard(channel.properties.channel_description);
|
||||||
|
createInfoModal(tr("Description copied"), tr("The channel description has been copied to your clipboard!")).open();
|
||||||
|
});
|
||||||
|
|
||||||
|
const button_update = template.find(".button-update");
|
||||||
|
button_update.on('click', event => update_values(modal.htmlTag));
|
||||||
|
|
||||||
|
update_values(template);
|
||||||
|
tooltip(template);
|
||||||
|
return template.children();
|
||||||
|
},
|
||||||
|
footer: null,
|
||||||
|
width: "65em"
|
||||||
|
});
|
||||||
|
modal.htmlTag.find(".button-close").on('click', event => modal.close());
|
||||||
|
modal.htmlTag.find(".modal-body").addClass("modal-channel-info");
|
||||||
|
modal.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply_channel_description(container: JQuery, channel: ChannelEntry) {
|
||||||
|
const container_value = container.find(".value");
|
||||||
|
const container_no_value = container.find(".no-value");
|
||||||
|
|
||||||
|
channel.getChannelDescription().then(description => {
|
||||||
|
if(description) {
|
||||||
|
const result = xbbcode.parse(description, {});
|
||||||
|
container_value[0].innerHTML = result.build_html();
|
||||||
|
container_no_value.hide();
|
||||||
|
container_value.show();
|
||||||
|
} else {
|
||||||
|
container_no_value.text(tr("Channel has no description"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
container_value.hide();
|
||||||
|
container_no_value.text(tr("loading...")).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
const codec_names = [
|
||||||
|
tr("Speex Narrowband"),
|
||||||
|
tr("Speex Wideband"),
|
||||||
|
tr("Speex Ultra-Wideband"),
|
||||||
|
tr("CELT Mono"),
|
||||||
|
tr("Opus Voice"),
|
||||||
|
tr("Opus Music")
|
||||||
|
];
|
||||||
|
|
||||||
|
function apply_general(container: JQuery, channel: ChannelEntry) {
|
||||||
|
/* channel type */
|
||||||
|
{
|
||||||
|
const tag = container.find(".channel-type .value").empty();
|
||||||
|
if(channel.properties.channel_flag_permanent)
|
||||||
|
tag.text(tr("Permanent"));
|
||||||
|
else if(channel.properties.channel_flag_semi_permanent)
|
||||||
|
tag.text(tr("Semi permanent"));
|
||||||
|
else
|
||||||
|
//TODO: Channel delete delay!
|
||||||
|
tag.text(tr("Temporary"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chat mode */
|
||||||
|
{
|
||||||
|
const tag = container.find(".chat-mode .value").empty();
|
||||||
|
if(channel.properties.channel_flag_conversation_private || channel.properties.channel_flag_password) {
|
||||||
|
tag.text(tr("Private"));
|
||||||
|
} else {
|
||||||
|
if(channel.properties.channel_conversation_history_length == -1)
|
||||||
|
tag.text(tr("Public; Semi permanent message saving"));
|
||||||
|
else if(channel.properties.channel_conversation_history_length == 0)
|
||||||
|
tag.text(tr("Public; Permanent message saving"));
|
||||||
|
else
|
||||||
|
tag.append(MessageHelper.formatMessage(tr("Public; Saving last {} messages"), channel.properties.channel_conversation_history_length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* current clients */
|
||||||
|
{
|
||||||
|
const tag = container.find(".current-clients .value").empty();
|
||||||
|
|
||||||
|
if(channel.flag_subscribed) {
|
||||||
|
const current = channel.clients().length;
|
||||||
|
let channel_limit = tr("Unlimited");
|
||||||
|
if(!channel.properties.channel_flag_maxclients_unlimited)
|
||||||
|
channel_limit = "" + channel.properties.channel_maxclients;
|
||||||
|
else if(!channel.properties.channel_flag_maxfamilyclients_unlimited) {
|
||||||
|
if(channel.properties.channel_maxfamilyclients >= 0)
|
||||||
|
channel_limit = "" + channel.properties.channel_maxfamilyclients;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.text(current + " / " + channel_limit);
|
||||||
|
} else {
|
||||||
|
tag.text(tr("Channel not subscribed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* audio codec */
|
||||||
|
{
|
||||||
|
const tag = container.find(".audio-codec .value").empty();
|
||||||
|
tag.text((codec_names[channel.properties.channel_codec] || tr("Unknown")) + " (" + channel.properties.channel_codec_quality + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* audio encrypted */
|
||||||
|
{
|
||||||
|
const tag = container.find(".audio-encrypted .value").empty();
|
||||||
|
const mode = channel.channelTree.server.properties.virtualserver_codec_encryption_mode;
|
||||||
|
let appendix;
|
||||||
|
if(mode == 1)
|
||||||
|
appendix = tr("Overridden by the server with Unencrypted!");
|
||||||
|
else if(mode == 2)
|
||||||
|
appendix = tr("Overridden by the server with Encrypted!");
|
||||||
|
|
||||||
|
tag.html((channel.properties.channel_codec_is_unencrypted ? tr("Unencrypted") : tr("Encrypted")) + (appendix ? "<br>" + appendix : ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* flag password */
|
||||||
|
{
|
||||||
|
const tag = container.find(".flag-password .value").empty();
|
||||||
|
if(channel.properties.channel_flag_password)
|
||||||
|
tag.text(tr("Yes"));
|
||||||
|
else
|
||||||
|
tag.text(tr("No"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* topic */
|
||||||
|
{
|
||||||
|
const container_tag = container.find(".topic");
|
||||||
|
const tag = container_tag.find(".value").empty();
|
||||||
|
if(channel.properties.channel_topic) {
|
||||||
|
container_tag.show();
|
||||||
|
tag.text(channel.properties.channel_topic);
|
||||||
|
} else {
|
||||||
|
container_tag.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -317,20 +317,7 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tag.find(".button-group-add").on('click', event => {
|
tag.find(".button-group-add").on('click', () => client.open_assignment_modal());
|
||||||
Modals.createServerGroupAssignmentModal(client, (group, flag) => {
|
|
||||||
if(flag) {
|
|
||||||
return client.channelTree.client.serverConnection.send_command("servergroupaddclient", {
|
|
||||||
sgid: group.id,
|
|
||||||
cldbid: client.properties.client_database_id
|
|
||||||
}).then(result => { update_groups(); return true; });
|
|
||||||
} else
|
|
||||||
return client.channelTree.client.serverConnection.send_command("servergroupdelclient", {
|
|
||||||
sgid: group.id,
|
|
||||||
cldbid: client.properties.client_database_id
|
|
||||||
}).then(result => { update_groups(); return true; });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
update_groups();
|
update_groups();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
let current_modal: Modal;
|
let current_modal: Modal;
|
||||||
export function createServerGroupAssignmentModal(client: ClientEntry, callback: (group: Group, flag: boolean) => Promise<boolean>) {
|
export function createServerGroupAssignmentModal(client: ClientEntry, callback: (groups: number[], flag: boolean) => Promise<boolean>) {
|
||||||
if(current_modal)
|
if(current_modal)
|
||||||
current_modal.close();
|
current_modal.close();
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ namespace Modals {
|
||||||
let entry = {} as any;
|
let entry = {} as any;
|
||||||
entry["id"] = group.id;
|
entry["id"] = group.id;
|
||||||
entry["name"] = group.name;
|
entry["name"] = group.name;
|
||||||
entry["assigned"] = client.groupAssigned(group);
|
|
||||||
entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower);
|
entry["disabled"] = !client.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower);
|
||||||
entry["default"] = client.channelTree.server.properties.virtualserver_default_server_group == group.id;
|
entry["default"] = client.channelTree.server.properties.virtualserver_default_server_group == group.id;
|
||||||
tag["icon_" + group.id] = client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid);
|
tag["icon_" + group.id] = client.channelTree.client.fileManager.icons.generateTag(group.properties.iconid);
|
||||||
|
@ -28,6 +27,12 @@ namespace Modals {
|
||||||
|
|
||||||
let template = $("#tmpl_server_group_assignment").renderTag(tag);
|
let template = $("#tmpl_server_group_assignment").renderTag(tag);
|
||||||
|
|
||||||
|
const update_groups = () => {
|
||||||
|
for(let group of _groups) {
|
||||||
|
template.find("input[group-id='" + group.id + "']").prop("checked", client.groupAssigned(group));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template.find(".group-entry input").each((_idx, _entry) => {
|
template.find(".group-entry input").each((_idx, _entry) => {
|
||||||
let entry = $(_entry);
|
let entry = $(_entry);
|
||||||
|
|
||||||
|
@ -40,27 +45,32 @@ namespace Modals {
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = entry.prop("checked");
|
let target = entry.prop("checked");
|
||||||
callback(group, target).then(flag => flag ? Promise.resolve() : Promise.reject()).then(() => {
|
callback([group.id], target).catch(e => { log.warn(LogCategory.GENERAL, tr("Failed to change group assignment: %o"), e)}).then(update_groups);
|
||||||
template.find(".group-entry input[default]").prop("checked", template.find(".group-entry input:checked").length == 0);
|
|
||||||
}).catch(error => entry.prop("checked", !target));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
template.find(".button-close").on('click', () => current_modal.close());
|
template.find(".button-close").on('click', () => current_modal.close());
|
||||||
template.find(".button-remove-all").on('click', () => {
|
template.find(".button-remove-all").on('click', () => {
|
||||||
|
const group_ids = [];
|
||||||
|
|
||||||
template.find(".group-entry input").each((_idx, _entry) => {
|
template.find(".group-entry input").each((_idx, _entry) => {
|
||||||
let entry = $(_entry);
|
let entry = $(_entry);
|
||||||
if(entry.attr("default") !== undefined || !entry.prop("checked"))
|
if(entry.attr("default") !== undefined || !entry.prop("checked"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entry.prop("checked", false).trigger('change');
|
group_ids.push(parseInt(entry.attr("group-id")));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback(group_ids, false).catch(e => { log.warn(LogCategory.GENERAL, tr("Failed to remove all group assignments: %o"), e)}).then(update_groups);
|
||||||
|
|
||||||
});
|
});
|
||||||
template.find(".group-entry input[default]").prop("checked", template.find(".group-entry input:checked").length == 0);
|
|
||||||
|
update_groups();
|
||||||
return template;
|
return template;
|
||||||
},
|
},
|
||||||
footer: null,
|
footer: null,
|
||||||
width: "max-content"
|
min_width: "10em"
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
current_modal.htmlTag.find(".modal-body").addClass("modal-server-group-assignments");
|
current_modal.htmlTag.find(".modal-body").addClass("modal-server-group-assignments");
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
namespace Modals {
|
namespace Modals {
|
||||||
type InfoUpdateCallback = (info: ServerConnectionInfo | boolean) => any;
|
export type InfoUpdateCallback = (info: ServerConnectionInfo | boolean) => any;
|
||||||
export function openServerInfoBandwidth(server: ServerEntry, update_callbacks?: InfoUpdateCallback[]) : Modal {
|
export function openServerInfoBandwidth(server: ServerEntry, update_callbacks?: InfoUpdateCallback[]) : Modal {
|
||||||
let modal: Modal;
|
let modal: Modal;
|
||||||
let own_callbacks = !update_callbacks;
|
let own_callbacks = !update_callbacks;
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace Modals {
|
||||||
|
|
||||||
select.on('change', event => {
|
select.on('change', event => {
|
||||||
const value = parseInt(select.val() as string);
|
const value = parseInt(select.val() as string);
|
||||||
settings.static_global(Settings.KEY_FONT_SIZE, value);
|
settings.changeGlobal(Settings.KEY_FONT_SIZE, value);
|
||||||
console.log("Changed font size of %dpx", value);
|
console.log("Changed font size of %dpx", value);
|
||||||
|
|
||||||
$(document.body).css("font-size", value + "px");
|
$(document.body).css("font-size", value + "px");
|
||||||
|
|
|
@ -140,6 +140,15 @@ class ServerEntry {
|
||||||
|
|
||||||
let tag = $.spawn("div").addClass("tree-entry server");
|
let tag = $.spawn("div").addClass("tree-entry server");
|
||||||
|
|
||||||
|
/* unread marker */
|
||||||
|
{
|
||||||
|
tag.append(
|
||||||
|
$.spawn("div")
|
||||||
|
.addClass("marker-text-unread hidden")
|
||||||
|
.attr("conversation", 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
tag.append(
|
tag.append(
|
||||||
$.spawn("div")
|
$.spawn("div")
|
||||||
.addClass("server_type icon client-server_green")
|
.addClass("server_type icon client-server_green")
|
||||||
|
@ -388,4 +397,8 @@ class ServerEntry {
|
||||||
if(this.properties.virtualserver_uptime == 0 || this.lastInfoRequest == 0) return this.properties.virtualserver_uptime;
|
if(this.properties.virtualserver_uptime == 0 || this.lastInfoRequest == 0) return this.properties.virtualserver_uptime;
|
||||||
return this.properties.virtualserver_uptime + (new Date().getTime() - this.lastInfoRequest) / 1000;
|
return this.properties.virtualserver_uptime + (new Date().getTime() - this.lastInfoRequest) / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set flag_text_unread(flag: boolean) {
|
||||||
|
this._htmlTag.find(".marker-text-unread").toggleClass("hidden", !flag);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -608,7 +608,12 @@ class ChannelTree {
|
||||||
name: tr("Ban clients"),
|
name: tr("Ban clients"),
|
||||||
invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
invalidPermission: !this.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
Modals.spawnBanClient((clients).map(entry => entry.clientNickName()), (data) => {
|
Modals.spawnBanClient(this.client, (clients).map(entry => {
|
||||||
|
return {
|
||||||
|
name: entry.clientNickName(),
|
||||||
|
unique_id: entry.properties.client_unique_identifier
|
||||||
|
}
|
||||||
|
}), (data) => {
|
||||||
for (const client of clients)
|
for (const client of clients)
|
||||||
this.client.serverConnection.send_command("banclient", {
|
this.client.serverConnection.send_command("banclient", {
|
||||||
uid: client.properties.client_unique_identifier,
|
uid: client.properties.client_unique_identifier,
|
||||||
|
|
|
@ -198,6 +198,9 @@ const loader_javascript = {
|
||||||
"js/ui/modal/ModalAvatar.js",
|
"js/ui/modal/ModalAvatar.js",
|
||||||
"js/ui/modal/ModalAvatarList.js",
|
"js/ui/modal/ModalAvatarList.js",
|
||||||
"js/ui/modal/ModalClientInfo.js",
|
"js/ui/modal/ModalClientInfo.js",
|
||||||
|
"js/ui/modal/ModalChannelInfo.js",
|
||||||
|
"js/ui/modal/ModalServerInfo.js",
|
||||||
|
"js/ui/modal/ModalServerInfoBandwidth.js",
|
||||||
"js/ui/modal/ModalQuery.js",
|
"js/ui/modal/ModalQuery.js",
|
||||||
"js/ui/modal/ModalQueryManage.js",
|
"js/ui/modal/ModalQueryManage.js",
|
||||||
"js/ui/modal/ModalPlaylistList.js",
|
"js/ui/modal/ModalPlaylistList.js",
|
||||||
|
@ -207,14 +210,11 @@ const loader_javascript = {
|
||||||
"js/ui/modal/ModalSettings.js",
|
"js/ui/modal/ModalSettings.js",
|
||||||
"js/ui/modal/ModalCreateChannel.js",
|
"js/ui/modal/ModalCreateChannel.js",
|
||||||
"js/ui/modal/ModalServerEdit.js",
|
"js/ui/modal/ModalServerEdit.js",
|
||||||
"js/ui/modal/ModalServerInfo.js",
|
|
||||||
"js/ui/modal/ModalServerInfoBandwidth.js",
|
|
||||||
"js/ui/modal/ModalChangeVolume.js",
|
"js/ui/modal/ModalChangeVolume.js",
|
||||||
"js/ui/modal/ModalBanClient.js",
|
"js/ui/modal/ModalBanClient.js",
|
||||||
"js/ui/modal/ModalIconSelect.js",
|
"js/ui/modal/ModalIconSelect.js",
|
||||||
"js/ui/modal/ModalInvite.js",
|
"js/ui/modal/ModalInvite.js",
|
||||||
"js/ui/modal/ModalIdentity.js",
|
"js/ui/modal/ModalIdentity.js",
|
||||||
"js/ui/modal/ModalBanCreate.js",
|
|
||||||
"js/ui/modal/ModalBanList.js",
|
"js/ui/modal/ModalBanList.js",
|
||||||
"js/ui/modal/ModalYesNo.js",
|
"js/ui/modal/ModalYesNo.js",
|
||||||
"js/ui/modal/ModalPoke.js",
|
"js/ui/modal/ModalPoke.js",
|
||||||
|
@ -371,7 +371,8 @@ const loader_style = {
|
||||||
"css/static/modal-invite.css",
|
"css/static/modal-invite.css",
|
||||||
"css/static/modal-playlist.css",
|
"css/static/modal-playlist.css",
|
||||||
"css/static/modal-banlist.css",
|
"css/static/modal-banlist.css",
|
||||||
"css/static/modal-bancreate.css",
|
"css/static/modal-banclient.css",
|
||||||
|
"css/static/modal-channelinfo.css",
|
||||||
"css/static/modal-clientinfo.css",
|
"css/static/modal-clientinfo.css",
|
||||||
"css/static/modal-serverinfo.css",
|
"css/static/modal-serverinfo.css",
|
||||||
"css/static/modal-serverinfobandwidth.css",
|
"css/static/modal-serverinfobandwidth.css",
|
||||||
|
@ -523,7 +524,7 @@ loader.register_task(loader.Stage.LOADED, {
|
||||||
function: async () => {
|
function: async () => {
|
||||||
fadeoutLoader();
|
fadeoutLoader();
|
||||||
},
|
},
|
||||||
priority: 10
|
priority: 5
|
||||||
});
|
});
|
||||||
|
|
||||||
loader.register_task(loader.Stage.LOADED, {
|
loader.register_task(loader.Stage.LOADED, {
|
||||||
|
@ -589,6 +590,19 @@ loader.register_task(loader.Stage.SETUP, {
|
||||||
priority: 10
|
priority: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* test if we're getting loaded within a TeaClient preview window */
|
||||||
|
loader.register_task(loader.Stage.SETUP, {
|
||||||
|
name: "TeaClient tester",
|
||||||
|
function: async () => {
|
||||||
|
//@ts-ignore
|
||||||
|
if(typeof __teaclient_preview_notice !== "undefined" && typeof __teaclient_preview_error !== "undefined") {
|
||||||
|
loader.critical_error("Why you're opening TeaWeb within the TeaSpeak client?!");
|
||||||
|
throw "we're already a TeaClient!";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
priority: 100
|
||||||
|
});
|
||||||
|
|
||||||
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
|
||||||
name: "log enabled initialisation",
|
name: "log enabled initialisation",
|
||||||
function: async () => log.initialize(app.type === app.Type.CLIENT_DEBUG || app.type === app.Type.WEB_DEBUG ? log.LogType.TRACE : log.LogType.INFO),
|
function: async () => log.initialize(app.type === app.Type.CLIENT_DEBUG || app.type === app.Type.WEB_DEBUG ? log.LogType.TRACE : log.LogType.INFO),
|
||||||
|
|
4
todo.txt
4
todo.txt
|
@ -19,9 +19,8 @@
|
||||||
- Icon Select
|
- Icon Select
|
||||||
- Icon upload
|
- Icon upload
|
||||||
- Avatar list
|
- Avatar list
|
||||||
- Server group assignments checkbox
|
|
||||||
- Invite buddy
|
- Invite buddy
|
||||||
|
- Change volume
|
||||||
|
|
||||||
- Ban Liste
|
- Ban Liste
|
||||||
- Übersicht
|
- Übersicht
|
||||||
|
@ -132,6 +131,7 @@
|
||||||
- File Transfer bytes transferred, month & global (Up + Download)
|
- File Transfer bytes transferred, month & global (Up + Download)
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
Context menu for clients => Group assignment => Not assignable groups sind assignable
|
||||||
Fix these icons: https://img.did.science/Screenshot_20-11-06.png
|
Fix these icons: https://img.did.science/Screenshot_20-11-06.png
|
||||||
- Application Options
|
- Application Options
|
||||||
- Crash
|
- Crash
|
||||||
|
|
Loading…
Reference in New Issue