Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Raise hand call action
Added hand and hand-off icons (need better fitting ones later).
Added "raise-hand" capability.
Broadcast "raisedHand" message through signaling and update the icons
accordingly.

Signed-off-by: Vincent Petry <[email protected]>
  • Loading branch information
PVince81 committed Dec 16, 2020
commit b8f09f076e008589b893e53cde9e6145c60749ff
2 changes: 2 additions & 0 deletions css/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
@include icon-black-white('bell-outline', 'spreed', 1);
@include icon-black-white('emoji-smile', 'spreed', 1);
@include icon-black-white('lobby', 'spreed', 1);
@include icon-black-white('hand', 'spreed', 1);
@include icon-black-white('hand-off', 'spreed', 1);
@include icon-black-white('text', 'filetypes', 1, true);

.app-talk,
Expand Down
7 changes: 4 additions & 3 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ title: Capabilities
* `sip-support` - Whether conversations API v3 exists and SIP can be configured and enabled by moderators. The conversations API will come with some new values `sipEnabled` which signals whether this conversation has SIP configured as well as `canEnableSIP` to see if a user can enable it. When it is enabled `attendeePin` will contain the unique dial-in code for this user.

## 11.0
* `config => previews => max-gif-size` - Maximum size in bytes below which a GIF can be embedded directly in the page at render time. Bigger files will be rendered statically using the preview endpoint instead. Can be set with `occ config:app:set spreed max-gif-size --value=X` where X is the new value in bytes. Defaults to 3 MB.
* `chat-read-status` - On conversation API v3 and the chat API the last common read message is exposed which can be used to update the "read status" flag of own chat messages. The info should be shown only when the user also shares their read status. The user's value can be found in `config => chat => read-privacy`.
* `config => chat => read-privacy` - See `chat-read-status`
* `phonebook-search` - Is present when the server has the endpoint to search for phone numbers to find matches in the accounts list
* `listable-rooms` - Conversations can searched for even when not joined. A "listable" attribute set on rooms defines the scope of who can find it.
* `phonebook-search` - Is present when the server has the endpoint to search for phone numbers to find matches in the accounts list
* `raise-hand` - Participants can raise or lower hand, the state change is sent through signaling messages.
* `config => chat => read-privacy` - See `chat-read-status`
* `config => previews => max-gif-size` - Maximum size in bytes below which a GIF can be embedded directly in the page at render time. Bigger files will be rendered statically using the preview endpoint instead. Can be set with `occ config:app:set spreed max-gif-size --value=X` where X is the new value in bytes. Defaults to 3 MB.
1 change: 1 addition & 0 deletions img/hand-off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions img/hand.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public function getCapabilities(): array {
'sip-support',
'chat-read-status',
'phonebook-search',
'raise-hand',
],
'config' => [
'attachments' => [
Expand Down
37 changes: 36 additions & 1 deletion src/components/CallView/shared/LocalMediaControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->

<template>
<div v-shortkey.push="['space']"
@shortkey="handleShortkey">
<div class="buttons-bar">
<button
id="raiseHand"
v-shortkey="['h']"
v-tooltip="raisedHandButtonTooltip"
:aria-label="raisedHandButtonAriaLabel"
:class="raisedHandButtonClass"
class="forced-white"
@shortkey="toggleHandRaised"
@click="toggleHandRaised" />
<div id="muteWrapper">
<button
id="mute"
Expand Down Expand Up @@ -176,6 +184,27 @@ export default {

computed: {

raisedHandButtonClass() {
return {
'icon-hand-white': this.model.attributes.raisedHand,
'icon-hand-off-white hand-disabled': !this.model.attributes.raisedHand,
}
},

raisedHandButtonAriaLabel() {
if (!this.model.attributes.raisedHand) {
return t('spreed', 'Raise hand')
}
return t('spreed', 'Lower hand')
},

raisedHandButtonTooltip() {
return {
content: this.raisedHandButtonAriaLabel,
show: false,
}
},

audioButtonClass() {
return {
'icon-audio': this.model.attributes.audioAvailable && this.model.attributes.audioEnabled,
Expand Down Expand Up @@ -519,6 +548,10 @@ export default {
}
},

toggleHandRaised() {
this.model.toggleHandRaised(!this.model.attributes.raisedHand)
},

shareScreen() {
if (!this.model.attributes.localScreen) {
this.startShareScreen('screen')
Expand Down Expand Up @@ -642,12 +675,14 @@ export default {
height: auto;
}

.buttons-bar button.hand-disabled,
.buttons-bar button.audio-disabled,
.buttons-bar button.video-disabled,
.buttons-bar button.screensharing-disabled {
opacity: .7;
}

.buttons-bar button.hand-disabled,
.buttons-bar button.audio-disabled:not(.no-audio-available),
.buttons-bar button.video-disabled:not(.no-video-available),
.buttons-bar button.screensharing-disabled {
Expand Down
18 changes: 18 additions & 0 deletions src/components/CallView/shared/VideoBottomBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@
<div v-if="!isSidebar"
class="bottom-bar"
:class="{'bottom-bar--video-on' : hasShadow, 'bottom-bar--big': isBig }">
<transition name="fade">
<div
v-show="showVideoOverlay"
class="bottom-bar__statusIndicator">
<div
v-show="!connectionStateFailedNoRestart && model.attributes.raisedHand"
class="raisedHandIndicator forced-white icon-hand-white" />
</div>
</transition>
<transition name="fade">
<div v-show="showNameIndicator"
class="bottom-bar__nameIndicator"
Expand Down Expand Up @@ -247,6 +256,7 @@ export default {
font-weight: bold;
}
}
&__statusIndicator,
&__mediaIndicator {
position: relative;
background-size: 22px;
Expand All @@ -265,6 +275,7 @@ export default {
}
}

.raisedHandIndicator,
.muteIndicator,
.hideRemoteVideo,
.screensharingIndicator,
Expand Down Expand Up @@ -304,4 +315,11 @@ export default {
opacity: .8 !important;
}

.raisedHandIndicator {
display: block;
/* like buttons */
padding: 6px 12px;
}


</style>
6 changes: 6 additions & 0 deletions src/components/SettingsDialog/SettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@
{{ t('spreed', 'Push to talk or push to mute') }}
</dd>
</div>
<div>
<dt><kbd>H</kbd></dt>
<dd class="shortcut-description">
{{ t('spreed', 'Raise/lower hand') }}
</dd>
</div>
</dl>
</AppSettingsSection>
</AppSettingsDialog>
Expand Down
12 changes: 11 additions & 1 deletion src/utils/webrtc/models/CallParticipantModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default function CallParticipantModel(options) {
speaking: undefined,
videoAvailable: undefined,
screen: null,
raisedHand: false,
}

this._handlers = []
Expand All @@ -68,14 +69,15 @@ export default function CallParticipantModel(options) {
this._handleUnmuteBound = this._handleUnmute.bind(this)
this._handleExtendedIceConnectionStateChangeBound = this._handleExtendedIceConnectionStateChange.bind(this)
this._handleChannelMessageBound = this._handleChannelMessage.bind(this)
this._handleRaisedHandBound = this._handleRaisedHand.bind(this)

this._webRtc.on('peerStreamAdded', this._handlePeerStreamAddedBound)
this._webRtc.on('peerStreamRemoved', this._handlePeerStreamRemovedBound)
this._webRtc.on('nick', this._handleNickBound)
this._webRtc.on('mute', this._handleMuteBound)
this._webRtc.on('unmute', this._handleUnmuteBound)
this._webRtc.on('channelMessage', this._handleChannelMessageBound)

this._webRtc.on('raisedHand', this._handleRaisedHandBound)
}

CallParticipantModel.prototype = {
Expand Down Expand Up @@ -241,6 +243,14 @@ CallParticipantModel.prototype = {
}
},

_handleRaisedHand: function(data) {
if (!this.get('peer') || this.get('peer').id !== data.id) {
return
}

this.set('raisedHand', data.raised)
},

setPeer: function(peer) {
if (peer && this.get('peerId') !== peer.id) {
console.warn('Mismatch between stored peer ID and ID of given peer: ', this.get('peerId'), peer.id)
Expand Down
18 changes: 18 additions & 0 deletions src/utils/webrtc/models/LocalMediaModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function LocalMediaModel() {
videoEnabled: false,
localScreen: null,
token: '',
raisedHand: false,
}

this._handlers = []
Expand Down Expand Up @@ -428,4 +429,21 @@ LocalMediaModel.prototype = {
this._webRtc.stopScreenShare()
},

/**
* Toggles hand raised mode for the local participant
*
* @param {bool} raised true for raised, false for lowered
*/
toggleHandRaised: function(raised) {
if (!this._webRtc) {
throw new Error('WebRtc not initialized yet')
}

this._webRtc.sendToAll('raiseHand', { raised: raised })

// Set state locally too, as even when sending to all the sender will not
// receive the message.
this.set('raisedHand', raised)
},

}
2 changes: 2 additions & 0 deletions src/utils/webrtc/simplewebrtc/peer.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ Peer.prototype.handleMessage = function(message) {
} else if (message.type === 'unshareScreen') {
this.parent.emit('unshareScreen', { id: message.from })
this.end()
} else if (message.type === 'raiseHand') {
this.parent.emit('raisedHand', { id: message.from, raised: message.payload.raised })
}
}

Expand Down