Add context menu for users and channels

This commit is contained in:
Jonas Herzig 2018-09-24 19:00:18 +02:00
parent 54b62b9964
commit af5a134081
3 changed files with 251 additions and 0 deletions

View file

@ -179,6 +179,129 @@
</form> </form>
</div> </div>
<!-- /ko --> <!-- /ko -->
<!-- ko with: userContextMenu -->
<ul class="context-menu" data-bind="if: target,
style: { left: posX() + 'px',
top: posY() + 'px' }">
<!-- ko with: target -->
<li data-bind="css: { disabled: !canChangeMute() }">
Mute
</li>
<li data-bind="css: { disabled: !canChangeDeafen() }">
Deafen
</li>
<li data-bind="css: { disabled: !canChangePrioritySpeaker() }">
Priority Speaker
</li>
<li data-bind="css: { disabled: !canLocalMute() }">
Local Mute
</li>
<li data-bind="css: { disabled: !canIgnoreMessages() }">
Ignore Messages
</li>
<li data-bind="css: { disabled: !canChangeComment() }, visible: comment">
View Comment
</li>
<!-- ko if: $data === $root.thisUser() -->
<li data-bind="css: { disabled: !canChangeComment() }, visible: true">
Change Comment
</li>
<!-- /ko -->
<!-- ko if: $data !== $root.thisUser() -->
<li data-bind="css: { disabled: !canChangeComment() }, visible: comment">
Reset Comment
</li>
<!-- /ko -->
<li data-bind="css: { disabled: !canChangeAvatar() }, visible: texture">
View Avatar
</li>
<!-- ko if: $data === $root.thisUser() -->
<li data-bind="css: { disabled: !canChangeAvatar() }, visible: true">
Change Avatar
</li>
<!-- /ko -->
<li data-bind="css: { disabled: !canChangeAvatar() }, visible: texture">
Reset Avatar
</li>
<li data-bind="css: { disabled: true }, visible: true">
Send Message
</li>
<li data-bind="css: { disabled: true }, visible: true">
Information
</li>
<li data-bind="visible: $data === $root.thisUser(),
css: { checked: selfMute },
click: toggleMute">
Self Mute
</li>
<li data-bind="visible: $data === $root.thisUser(),
css: { checked: selfDeaf },
click: toggleDeaf">
Self Deafen
</li>
<!-- ko if: $data !== $root.thisUser() -->
<li data-bind="css: { disabled: true }, visible: true">
Add Friend
</li>
<li data-bind="css: { disabled: true }, visible: false">
Remove Friend
</li>
<!-- /ko -->
<!-- /ko -->
</ul>
<!-- /ko -->
<!-- ko with: channelContextMenu -->
<ul class="context-menu" data-bind="if: target,
style: { left: posX() + 'px',
top: posY() + 'px' }">
<!-- ko with: target -->
<li data-bind="visible: users.indexOf($root.thisUser()) === -1,
css: { disabled: !canJoin() },
click: $root.requestMove.bind($root, $root.thisUser())">
Join Channel
</li>
<li data-bind="css: { disabled: !canAdd() }">
Add
</li>
<li data-bind="css: { disabled: !canEdit() }">
Edit
</li>
<li data-bind="css: { disabled: !canRemove() }">
Remove
</li>
<li data-bind="css: { disabled: !canLink() }">
Link
</li>
<li data-bind="css: { disabled: !canUnlink() }">
Unlink
</li>
<li data-bind="css: { disabled: !canUnlink() }">
Unlink All
</li>
<li data-bind="css: { disabled: true }">
Copy Mumble URL
</li>
<li data-bind="css: { disabled: true }">
Copy Mumble-Web URL
</li>
<li data-bind="css: { disabled: !canSendMessage() }">
Send Message
</li>
<!-- /ko -->
</ul>
<!-- /ko -->
<script type="text/html" id="user-tag"> <script type="text/html" id="user-tag">
<span class="user-tag" data-bind="text: name"></span> <span class="user-tag" data-bind="text: name"></span>
</script> </script>
@ -265,6 +388,7 @@
<div class="channel" data-bind=" <div class="channel" data-bind="
click: $root.select, click: $root.select,
event: { event: {
contextmenu: openContextMenu,
dblclick: $root.requestMove.bind($root, $root.thisUser()) dblclick: $root.requestMove.bind($root, $root.thisUser())
}, },
css: { css: {
@ -292,6 +416,9 @@
<div class="user-tree"></div> <div class="user-tree"></div>
<div class="user" data-bind=" <div class="user" data-bind="
click: $root.select, click: $root.select,
event: {
contextmenu: openContextMenu
},
css: { css: {
thisClient: $root.thisUser() === $data, thisClient: $root.thisUser() === $data,
selected: $root.selected() === $data selected: $root.selected() === $data

View file

@ -19,8 +19,34 @@ function sanitize (html) {
}) })
} }
function openContextMenu (event, contextMenu, target) {
contextMenu.posX(event.clientX)
contextMenu.posY(event.clientY)
contextMenu.target(target)
const closeListener = (event) => {
// Always close, no matter where they clicked
setTimeout(() => { // delay to allow click to be actually processed
contextMenu.target(null)
unregister()
})
}
const unregister = () => document.removeEventListener('click', closeListener)
document.addEventListener('click', closeListener)
event.stopPropagation()
event.preventDefault()
}
// GUI // GUI
function ContextMenu () {
var self = this
self.posX = ko.observable()
self.posY = ko.observable()
self.target = ko.observable()
}
function ConnectDialog () { function ConnectDialog () {
var self = this var self = this
self.address = ko.observable('') self.address = ko.observable('')
@ -152,6 +178,8 @@ class GlobalBindings {
constructor () { constructor () {
this.settings = new Settings() this.settings = new Settings()
this.client = null this.client = null
this.userContextMenu = new ContextMenu()
this.channelContextMenu = new ContextMenu()
this.connectDialog = new ConnectDialog() this.connectDialog = new ConnectDialog()
this.connectErrorDialog = new ConnectErrorDialog(this.connectDialog) this.connectErrorDialog = new ConnectErrorDialog(this.connectDialog)
this.connectionInfo = new ConnectionInfo() this.connectionInfo = new ConnectionInfo()
@ -342,6 +370,46 @@ class GlobalBindings {
} }
return true return true
} }
ui.openContextMenu = (_, event) => openContextMenu(event, this.userContextMenu, ui)
ui.canChangeMute = () => {
return false // TODO check for perms and implement
}
ui.canChangeDeafen = () => {
return false // TODO check for perms and implement
}
ui.canChangePrioritySpeaker = () => {
return false // TODO check for perms and implement
}
ui.canLocalMute = () => {
return false // TODO implement local mute
// return this.thisUser() !== ui
}
ui.canIgnoreMessages = () => {
return false // TODO implement ignore messages
// return this.thisUser() !== ui
}
ui.canChangeComment = () => {
return false // TODO implement changing of comments
// return this.thisUser() === ui // TODO check for perms
}
ui.canChangeAvatar = () => {
return false // TODO implement changing of avatar
// return this.thisUser() === ui // TODO check for perms
}
ui.toggleMute = () => {
if (ui.selfMute()) {
this.requestUnmute(ui)
} else {
this.requestMute(ui)
}
}
ui.toggleDeaf = () => {
if (ui.selfDeaf()) {
this.requestUndeaf(ui)
} else {
this.requestDeaf(ui)
}
}
Object.entries(simpleProperties).forEach(key => { Object.entries(simpleProperties).forEach(key => {
ui[key[1]] = ko.observable(user[key[0]]) ui[key[1]] = ko.observable(user[key[0]])
}) })
@ -421,6 +489,28 @@ class GlobalBindings {
users: ko.observableArray(), users: ko.observableArray(),
linked: ko.observable(false) linked: ko.observable(false)
} }
ui.openContextMenu = (_, event) => openContextMenu(event, this.channelContextMenu, ui)
ui.canJoin = () => {
return true // TODO check for perms
}
ui.canAdd = () => {
return false // TODO check for perms and implement
}
ui.canEdit = () => {
return false // TODO check for perms and implement
}
ui.canRemove = () => {
return false // TODO check for perms and implement
}
ui.canLink = () => {
return false // TODO check for perms and implement
}
ui.canUnlink = () => {
return false // TODO check for perms and implement
}
ui.canSendMessage = () => {
return false // TODO check for perms and implement
}
Object.entries(simpleProperties).forEach(key => { Object.entries(simpleProperties).forEach(key => {
ui[key[1]] = ko.observable(channel[key[0]]) ui[key[1]] = ko.observable(channel[key[0]])
}) })

View file

@ -6,6 +6,7 @@ $bg-color: #eee !default
$white: #fff !default $white: #fff !default
$font-color: $black !default $font-color: $black !default
$font-disabled-color: $gray !default
$panel-bg-color: $white !default $panel-bg-color: $white !default
$panel-border-color: $lightgray !default $panel-border-color: $lightgray !default
$channel-tree-color: $lightgray !default $channel-tree-color: $lightgray !default
@ -38,6 +39,9 @@ $dialog-button-color: $dialog-color !default
$dialog-input-border-color: $darkgray !default $dialog-input-border-color: $darkgray !default
$dialog-input-bg-color: $white !default $dialog-input-bg-color: $white !default
$dialog-input-color: $dialog-color !default $dialog-input-color: $dialog-color !default
$context-menu-bg-color: $bg-color !default
$context-menu-border-color: $panel-border-color !default
$context-menu-hover-bg-color: $toolbar-hover-bg-color !default
$tooltip-bg-color: $panel-bg-color !default $tooltip-bg-color: $panel-bg-color !default
$channels-bg-color: $panel-bg-color !default $channels-bg-color: $panel-bg-color !default
@ -219,6 +223,36 @@ html, body {
position: absolute; position: absolute;
z-index: 100; z-index: 100;
} }
.context-menu {
position: absolute;
z-index: 50;
background: $context-menu-bg-color;
border: 1px solid $context-menu-border-color;
margin: 0;
padding: 0;
list-style: none;
& > li {
padding: 5px 20px;
padding-left: 10px;
&::before {
display: inline-block;
width: 10px;
padding-right: 5px;
content: '';
}
&.checked::before {
content: '';
}
&:hover {
background: $context-menu-hover-bg-color;
}
&.disabled {
background: $context-menu-bg-color;
color: $font-disabled-color;
}
}
}
.toolbar { .toolbar {
display: flex; display: flex;
align-items: center; align-items: center;