* VRFS-3494 - show popup when video window launches for the 1st time to offer guidance
This commit is contained in:
parent
c3f81a4d23
commit
9420cebad4
|
|
@ -113,6 +113,21 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
@user.delete_mod(User::MOD_GEAR, User::MOD_GEAR_FRAME_OPTIONS)
|
||||
end
|
||||
|
||||
|
||||
if params[:jam_ruby_user][:how_to_use_video_no_show].to_i == 1
|
||||
@user.mod_merge({User::MOD_NO_SHOW => {User::HOWTO_USE_VIDEO_NOSHOW => true}})
|
||||
else
|
||||
@user.delete_mod(User::MOD_NO_SHOW, User::HOWTO_USE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
||||
|
||||
if params[:jam_ruby_user][:configure_video_no_show].to_i == 1
|
||||
@user.mod_merge({User::MOD_NO_SHOW => {User::CONFIGURE_VIDEO_NOSHOW=> true}})
|
||||
else
|
||||
@user.delete_mod(User::MOD_NO_SHOW, User::CONFIGURE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
||||
|
||||
@user.save!
|
||||
|
||||
redirect_to edit_admin_user_path(@user)
|
||||
|
|
|
|||
|
|
@ -9,4 +9,7 @@
|
|||
= f.input :musician
|
||||
= f.inputs "Gear Mods" do
|
||||
= f.input :show_frame_options, as: :boolean
|
||||
= f.inputs "Do Not Shows" do
|
||||
= f.input :how_to_use_video_no_show, as: :boolean
|
||||
= f.input :configure_video_no_show, as: :boolean
|
||||
= f.actions
|
||||
|
|
|
|||
|
|
@ -27,4 +27,13 @@
|
|||
def show_frame_options
|
||||
self.get_gear_mod(MOD_GEAR_FRAME_OPTIONS)
|
||||
end
|
||||
|
||||
|
||||
def how_to_use_video_no_show
|
||||
self.get_no_show_mod(HOWTO_USE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
||||
def configure_video_no_show
|
||||
self.get_no_show_mod(CONFIGURE_VIDEO_NOSHOW)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1774,7 +1774,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
if count > 500
|
||||
break
|
||||
#break
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ module JamRuby
|
|||
MOD_GEAR_FRAME_OPTIONS = "show_frame_options"
|
||||
|
||||
MOD_NO_SHOW = "no_show"
|
||||
HOWTO_USE_VIDEO_NOSHOW = 'how-to-use-video'
|
||||
CONFIGURE_VIDEO_NOSHOW = 'configure-video'
|
||||
|
||||
# MIN/MAX AUDIO LATENCY
|
||||
MINIMUM_AUDIO_LATENCY = 2
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 802 B |
Binary file not shown.
|
After Width: | Height: | Size: 876 B |
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.AccountVideoProfile = function (app) {
|
||||
var $webcamViewer = new context.JK.WebcamViewer()
|
||||
var webcamViewerReact = null;
|
||||
function initialize() {
|
||||
var screenBindings = {
|
||||
'beforeShow': beforeShow,
|
||||
|
|
@ -12,16 +12,19 @@
|
|||
};
|
||||
app.bindScreen('account/video', screenBindings);
|
||||
|
||||
var $root = $("#account-video-profile .webcam-container")
|
||||
$webcamViewer.init($root, true)
|
||||
var reactElement = React.createElement(window.WebcamViewer, {isVisible: false});
|
||||
var reactDomNode = $("#account-video-profile .webcam-container").get(0)
|
||||
webcamViewerReact = React.render(reactElement, reactDomNode)
|
||||
}
|
||||
|
||||
|
||||
function beforeShow() {
|
||||
$webcamViewer.beforeShow()
|
||||
console.log("webcamViewerReact", webcamViewerReact)
|
||||
webcamViewerReact.beforeShow()
|
||||
}
|
||||
|
||||
function beforeHide() {
|
||||
$webcamViewer.beforeHide()
|
||||
webcamViewerReact.beforeHide()
|
||||
}
|
||||
|
||||
this.beforeShow = beforeShow
|
||||
|
|
|
|||
|
|
@ -131,6 +131,12 @@
|
|||
// context.JK.CurrentSessionModel.onPlaybackStateChange(type, text);
|
||||
context.MediaPlaybackActions.playbackStateChange(text);
|
||||
}
|
||||
else if(type === ALERT_NAMES.VIDEO_WINDOW_OPENED) {
|
||||
context.VideoActions.videoWindowOpened()
|
||||
}
|
||||
else if(type === ALERT_NAMES.VIDEO_WINDOW_CLOSED) {
|
||||
context.VideoActions.videoWindowClosed()
|
||||
}
|
||||
else if((!context.JK.CurrentSessionModel || !context.JK.CurrentSessionModel.inSession()) &&
|
||||
(ALERT_NAMES.INPUT_IO_RATE == type || ALERT_NAMES.INPUT_IO_JTR == type || ALERT_NAMES.OUTPUT_IO_RATE == type || ALERT_NAMES.OUTPUT_IO_JTR== type)) {
|
||||
// squelch these events if not in session
|
||||
|
|
|
|||
|
|
@ -39,4 +39,6 @@ context.JK.ClientInit = class ClientInit
|
|||
nativeClientInit: () =>
|
||||
@gearUtils.bootstrapDefaultPlaybackProfile();
|
||||
|
||||
window.VideoActions.checkPromptConfigureVideo()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -80,20 +80,20 @@
|
|||
|
||||
}
|
||||
function FTUEGetVideoCaptureDeviceNames() {
|
||||
return ["Built-in Webcam HD"]
|
||||
return {"xy323ss": "Built-in Webcam HD"}
|
||||
}
|
||||
function FTUECurrentSelectedVideoDevice() {
|
||||
return {"xy323ss": "Built-in Webcam HD"}
|
||||
//return {"xy323ss": "Built-in Webcam HD"}
|
||||
return {}
|
||||
}
|
||||
function FTUEGetAvailableEncodeVideoResolutions() {
|
||||
return {
|
||||
1 : "QCIF (176X144)",
|
||||
2 : "CIF (352X288)",
|
||||
3 : "VGA (640X480)",
|
||||
4 : "4CIF (704X576)",
|
||||
5 : "1/2WHD (640X360)",
|
||||
6: "WHD (1280X720)",
|
||||
7 : "FHD (1920x1080)"
|
||||
1 : "CIF (352X288)",
|
||||
2 : "VGA (640X480)",
|
||||
3 : "4CIF (704X576)",
|
||||
4 : "1/2WHD (640X360)",
|
||||
5 : "WHD (1280X720)",
|
||||
6 : "FHD (1920x1080)"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,16 @@
|
|||
SHOW_PREFERENCES : 39, // tell frontend to show preferences dialog
|
||||
USB_CONNECTED : 40, // tell frontend that a USB device was connected
|
||||
USB_DISCONNECTED : 41, // tell frontend that a USB device was disconnected
|
||||
LAST_ALERT : 42
|
||||
JAM_TRACK_SERVER_ERROR : 42, //error talking with server
|
||||
BAD_INTERVAL_RATE : 43, //the audio gear is calling back at rate that does not match the expected interval
|
||||
FIRST_AUDIO_PACKET : 44,// we are receiving audio from peer
|
||||
NETWORK_PORT_MANGLED : 45, // packet from peer indicates network port is being mangled
|
||||
NO_GLOBAL_CLOCK_SERVER : 46, //can't reach global clock NTP server
|
||||
GLOBAL_CLOCK_SYNCED : 47, //clock synced
|
||||
RECORDING_DONE :48, //the recording writer thread is done
|
||||
VIDEO_WINDOW_OPENED :49, //video window opened
|
||||
VIDEO_WINDOW_CLOSED :50,
|
||||
LAST_ALERT : 51
|
||||
}
|
||||
// recreate eThresholdType enum from MixerDialog.h
|
||||
context.JK.ALERT_TYPES = {
|
||||
|
|
@ -171,7 +180,16 @@
|
|||
39: {"title": "", "message": ""}, // SHOW_PREFERENCES, //show preferences dialog
|
||||
40: {"title": "", "message": ""}, // USB_CONNECTED
|
||||
41: {"title": "", "message": ""}, // USB_DISCONNECTED, // tell frontend that a USB device was disconnected
|
||||
42: {"title": "", "message": ""} // LAST_ALERT
|
||||
42: {"title": "", "message": ""}, // JAM_TRACK_SERVER_ERROR
|
||||
43: {"title": "", "message": ""}, // BAD_INTERVAL_RATE
|
||||
44: {"title": "", "message": ""}, // FIRST_AUDIO_PACKET
|
||||
45: {"title": "", "message": ""}, // NETWORK_PORT_MANGLED
|
||||
46: {"title": "", "message": ""}, // NO_GLOBAL_CLOCK_SERVER
|
||||
47: {"title": "", "message": ""}, // GLOBAL_CLOCK_SYNCED
|
||||
48: {"title": "", "message": ""}, // RECORDING_DONE
|
||||
49: {"title": "", "message": ""}, // VIDEO_WINDOW_OPENED
|
||||
50: {"title": "", "message": ""}, // VIDEO_WINDOW_CLOSED
|
||||
51: {"title": "", "message": ""} // LAST_ALERT
|
||||
};
|
||||
|
||||
// add the alert's name to the ALERT_TYPES structure
|
||||
|
|
@ -311,7 +329,9 @@
|
|||
|
||||
/** NAMED_MESSAGES means messages that we show to the user (dialogs/banners/whatever), that we have formally named */
|
||||
context.JK.NAMED_MESSAGES = {
|
||||
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix'
|
||||
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix',
|
||||
HOWTO_USE_VIDEO_NOSHOW : 'how-to-use-video',
|
||||
CONFIGURE_VIDEO_NOSHOW : 'configure-video'
|
||||
}
|
||||
|
||||
context.JK.ChannelGroupIds = {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
//= require ./react-components/stores/SessionMyTracksStore
|
||||
//= require ./react-components/stores/SessionOtherTracksStore
|
||||
//= require ./react-components/stores/SessionMediaTracksStore
|
||||
//= require ./react-components/stores/VideoStore
|
||||
//= require_directory ./react-components/stores
|
||||
//= require_directory ./react-components/mixins
|
||||
//= require_directory ./react-components
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
mixins = []
|
||||
|
||||
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
|
||||
accessOpener = false
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
accessOpener = true
|
||||
catch e
|
||||
|
||||
|
||||
if accessOpener
|
||||
VideoActions = window.opener.VideoActions
|
||||
VideoStore = window.opener.VideoStore
|
||||
|
||||
#mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
|
||||
|
||||
|
||||
@PopupConfigureVideoGear = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
logger: context.JK.logger
|
||||
|
||||
render: () ->
|
||||
`<div className="configure-video-geaor">
|
||||
<div className="popup-contents">
|
||||
<div classNmae="video-header">
|
||||
<h2 className="subcaption">video is not configured</h2>
|
||||
<div className="subcaption">
|
||||
If you might like to use video in sessions, please select a webcam to use, and a video resolution and frame rate to capture. Then click the TEST WEBCAM button to verify that you see video from your webcam properly. In sessions, you can choose to turn video on or off any time.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="webcam-container">
|
||||
<WebcamViewer isVisible={true} />
|
||||
</div>
|
||||
|
||||
<div className="important-note">
|
||||
<h5>
|
||||
Important Note
|
||||
</h5>
|
||||
<div className="contents">
|
||||
You can update your video configuration any time in your Account settings, or in the menus of the video window while in a session.
|
||||
</div>
|
||||
</div>
|
||||
<div className="clearall" />
|
||||
</div>
|
||||
|
||||
<div className="close-behavior">
|
||||
<span className="field">
|
||||
<input type="checkbox" name="dont_show" /><label htmlFor="dont_show">Don't show this again</label>
|
||||
</span>
|
||||
<a className="button-orange close-link" onClick={this.close}>CLOSE</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
close: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
VideoActions.configureVideoPopupClosed($dontShow.is(':checked'))
|
||||
window.close()
|
||||
|
||||
windowUnloaded: () ->
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
|
||||
VideoActions.howToUseVideoPopupClosed($dontShow.is(':checked'))
|
||||
|
||||
componentDidMount: () ->
|
||||
$(window).unload(@windowUnloaded)
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
context.JK.checkbox($dontShow)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
mysteryTopMargin = 20
|
||||
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
mixins = []
|
||||
|
||||
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
|
||||
accessOpener = false
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
accessOpener = true
|
||||
catch e
|
||||
|
||||
|
||||
if accessOpener
|
||||
VideoActions = window.opener.VideoActions
|
||||
VideoStore = window.opener.VideoStore
|
||||
|
||||
#mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
|
||||
|
||||
@PopupHowToUseVideo = React.createClass({
|
||||
|
||||
render: () ->
|
||||
`<div className="how-to-use-video">
|
||||
<div className="popup-contents">
|
||||
<div className="control-holder">
|
||||
<a className="control start-video" onClick={this.startVideo}>
|
||||
<span className="helper" />
|
||||
<img src="/assets/content/webcam-icon-gray.png" width="20" height="20" />
|
||||
<span id="recording-status">Start Webcam</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="important-note">
|
||||
<h5>
|
||||
Important Note
|
||||
</h5>
|
||||
<div className="contents">
|
||||
You can start and stop your webcam at any time by navigating to the Webcam menu of the video window and selecting Start/Stop Webcam.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<input type="checkbox" name="dont_show" /><label htmlFor="dont_show">Don't show this again</label>
|
||||
</div>
|
||||
|
||||
<div className="close-behavior">
|
||||
<a className="button-orange close-link" onClick={this.close}>CLOSE</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
close: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
VideoActions.howToUseVideoPopupClosed($dontShow.is(':checked'))
|
||||
window.close()
|
||||
|
||||
startVideo: (e) ->
|
||||
e.preventDefault
|
||||
VideoActions.startVideo()
|
||||
|
||||
windowUnloaded: () ->
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
|
||||
VideoActions.howToUseVideoPopupClosed($dontShow.is(':checked'))
|
||||
|
||||
componentDidMount: () ->
|
||||
$(window).unload(@windowUnloaded)
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
context.JK.checkbox($dontShow)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
mysteryTopMargin = 20
|
||||
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
context = window
|
||||
|
||||
@VideoActions = Reflux.createActions({
|
||||
refresh: {}
|
||||
stopVideo: {}
|
||||
startVideo: {}
|
||||
setVideoEncodeResolution: {}
|
||||
setSendFrameRate: {}
|
||||
selectDevice: {}
|
||||
videoWindowOpened : {}
|
||||
videoWindowClosed : {}
|
||||
howToUseVideoPopupClosed: {}
|
||||
toggleVideo: {}
|
||||
configureVideoPopupClosed: {}
|
||||
checkPromptConfigureVideo: {}
|
||||
})
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
EVENTS = context.JK.EVENTS
|
||||
NAMED_MESSAGES = context.JK.NAMED_MESSAGES
|
||||
|
||||
VideoActions = @VideoActions
|
||||
|
||||
@VideoStore = Reflux.createStore(
|
||||
{
|
||||
listenables: VideoActions
|
||||
logger: context.JK.logger
|
||||
videoShared: false
|
||||
videoOpen : false
|
||||
state : null
|
||||
|
||||
init: ->
|
||||
this.listenTo(context.SessionStore, this.onSessionChange)
|
||||
|
||||
|
||||
|
||||
# someone has requested us to refresh our config
|
||||
onRefresh: ->
|
||||
|
||||
currentDevice = context.jamClient.FTUECurrentSelectedVideoDevice()
|
||||
deviceNames = context.jamClient.FTUEGetVideoCaptureDeviceNames()
|
||||
#deviceCaps = context.jamClient.FTUEGetVideoCaptureDeviceCapabilities()
|
||||
currentResolution = context.jamClient.GetCurrentVideoResolution()
|
||||
currentFrameRate = context.jamClient.GetCurrentVideoFrameRate()
|
||||
encodeResolutions = context.jamClient.FTUEGetAvailableEncodeVideoResolutions()
|
||||
frameRates = context.jamClient.FTUEGetSendFrameRates()
|
||||
|
||||
#deviceCaps: deviceCaps,
|
||||
|
||||
@state = {
|
||||
currentDevice: currentDevice,
|
||||
deviceNames: deviceNames,
|
||||
currentResolution: currentResolution,
|
||||
currentFrameRate: currentFrameRate,
|
||||
encodeResolutions: encodeResolutions,
|
||||
frameRates: frameRates,
|
||||
videoShared: @videoShared
|
||||
videoOpen: @videoOpen
|
||||
}
|
||||
this.trigger(@state)
|
||||
|
||||
onSessionChange: (@session) ->
|
||||
|
||||
onStartVideo: ->
|
||||
if @howtoWindow?
|
||||
@howtoWindow.close()
|
||||
@howtoWindow = null
|
||||
#else # TESTING
|
||||
# @howtoWindow = window.open("/popups/how-to-use-video", 'How to Use Video', 'scrollbars=yes,toolbar=no,status=no,height=315,width=320')
|
||||
|
||||
@logger.debug("SessStartVideoSharing()")
|
||||
context.jamClient.SessStartVideoSharing(0)
|
||||
@videoShared = true
|
||||
|
||||
@state.videoShared = @videoShared
|
||||
this.trigger(@state)
|
||||
|
||||
onStopVideo: ->
|
||||
if @videoShared
|
||||
@logger.debug("SessStopVideoSharing()")
|
||||
context.jamClient.SessStopVideoSharing()
|
||||
@videoShared = false
|
||||
@state.videoShared = @videoShared
|
||||
this.trigger(@state)
|
||||
|
||||
onToggleVideo: () ->
|
||||
if @videoShared
|
||||
@onStopVideo()
|
||||
else
|
||||
@onStartVideo()
|
||||
|
||||
onSetVideoEncodeResolution: (resolution) ->
|
||||
context.jamClient.FTUESetVideoEncodeResolution(resolution)
|
||||
|
||||
onSetSendFrameRate: (frameRates) ->
|
||||
context.jamClient.FTUESetSendFrameRates(frameRates)
|
||||
|
||||
onSelectDevice: (device, caps) ->
|
||||
result = context.jamClient.FTUESelectVideoCaptureDevice(device, caps)
|
||||
if(!result)
|
||||
@logger.error("onSelectDevice failed with device #{device}")
|
||||
|
||||
onVideoWindowOpened: () ->
|
||||
@onRefresh() unless @state?
|
||||
|
||||
@logger.debug("in session? #{@session.inSession()}, currentDevice? #{@state?.currentDevice?}, videoShared? #{@videoShared}")
|
||||
|
||||
if @session.inSession() && @state.currentDevice? && Object.keys(@state.currentDevice).length > 0 && !@videoShared
|
||||
context.JK.ModUtils.shouldShow(NAMED_MESSAGES.HOWTO_USE_VIDEO_NOSHOW).done((shouldShow) =>
|
||||
@logger.debug("checking if user has 'should show' on video howto: #{shouldShow}")
|
||||
if shouldShow
|
||||
@howtoWindow = window.open("/popups/how-to-use-video", 'How to Use Video', 'scrollbars=yes,toolbar=no,status=no,height=315,width=320')
|
||||
)
|
||||
|
||||
#@howtoWindo.ParentRecordingStore = context.RecordingStore
|
||||
#@howtoWindo.ParentIsRecording = @recordingModel.isRecording()
|
||||
|
||||
@videoOpen = true
|
||||
@state.videoOpen = @videoOpen
|
||||
this.trigger(@state)
|
||||
|
||||
onVideoWindowClosed: () ->
|
||||
@onRefresh() unless @state?
|
||||
|
||||
if @howtoWindow?
|
||||
@howtoWindow.close()
|
||||
@howtoWindow = null
|
||||
|
||||
@videoOpen = false
|
||||
@state.videoOpen = @videoOpen
|
||||
@videoShared = false
|
||||
@state.videoShared = @videoShared
|
||||
this.trigger(@state)
|
||||
|
||||
onHowToUseVideoPopupClosed: (dontShow) ->
|
||||
if (dontShow)
|
||||
@logger.debug("requesting that user no longer see how-to-use-video")
|
||||
context.JK.ModUtils.updateNoShow(NAMED_MESSAGES.HOWTO_USE_VIDEO_NOSHOW);
|
||||
|
||||
logger.debug("how-to-use-video popup closed")
|
||||
@howtoWindow = null
|
||||
|
||||
onConfigureVideoPopupClosed: (dontShow) ->
|
||||
if (dontShow)
|
||||
@logger.debug("requesting that user no longer see configure-video")
|
||||
context.JK.ModUtils.updateNoShow(NAMED_MESSAGES.CONFIGURE_VIDEO_NOSHOW);
|
||||
|
||||
logger.debug("configure-video popup closed")
|
||||
@configureWindow = null
|
||||
|
||||
# if the user passes all the safeguards, let's see if we should get them to configure video
|
||||
onCheckPromptConfigureVideo: () ->
|
||||
@onRefresh() unless @state?
|
||||
|
||||
@logger.debug("checkPromptConfigureVideo", @state.currentDevice, @state.deviceNames)
|
||||
|
||||
# if no device configured and this is the native client and if you have at least 1 video
|
||||
if (!@state.currentDevice? || Object.keys(@state.currentDevice).length == 0) && gon?.isNativeClient && Object.keys(@state.deviceNames).length > 0
|
||||
# and if they haven't said stop bothering me about this
|
||||
context.JK.ModUtils.shouldShow(NAMED_MESSAGES.CONFIGURE_VIDEO_NOSHOW).done((shouldShow) =>
|
||||
@logger.debug("checking if user has 'should show' on video config: #{shouldShow}")
|
||||
if shouldShow
|
||||
@configureWindow = window.open("/popups/configure-video", 'Configure Video', 'scrollbars=yes,toolbar=no,status=no,height=395,width=444')
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,319 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
reactContext = if window.opener? then window.opener else window
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
catch e
|
||||
reactContext = window
|
||||
|
||||
VideoStore = reactContext.VideoStore
|
||||
VideoActions = reactContext.VideoActions
|
||||
|
||||
ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||
|
||||
BackendToFrontend = {
|
||||
1 : "CIF (352x288)",
|
||||
2 : "VGA (640x480)",
|
||||
3 : "4CIF (704x576)",
|
||||
4 : "1/2 720p HD (640x360)",
|
||||
5 : "720p HD (1280x720)",
|
||||
6 : "1080p HD (1920x1080)"
|
||||
}
|
||||
|
||||
BackendNumericToBackendString = {
|
||||
1 : "CIF (352X288)",
|
||||
2 : "VGA (640X480)",
|
||||
3 : "4CIF (704X576)",
|
||||
4 : "1/2WHD (640X360)",
|
||||
5 : "WHD (1280X720)",
|
||||
6 : "FHD (1920x1080)"
|
||||
}
|
||||
|
||||
|
||||
BackendToFrontendFPS = {
|
||||
|
||||
1: 30,
|
||||
2: 24,
|
||||
3: 20,
|
||||
4: 15,
|
||||
5: 10
|
||||
}
|
||||
FrontendToBackend = {}
|
||||
for key, value of BackendToFrontend
|
||||
FrontendToBackend[value] = key
|
||||
|
||||
mixins = []
|
||||
mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
|
||||
|
||||
|
||||
@WebcamViewer = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
logger: context.JK.logger
|
||||
|
||||
getInitialState: () ->
|
||||
{
|
||||
currentDevice: null
|
||||
deviceNames: {}
|
||||
deviceCaps: null
|
||||
currentResolution: 0
|
||||
currentFrameRate: 0
|
||||
encodeResolutions: {}
|
||||
frameRates: {}
|
||||
rescanning: false
|
||||
}
|
||||
|
||||
onVideoStateChanged: (changes) ->
|
||||
@setState(changes)
|
||||
|
||||
render: () ->
|
||||
|
||||
if @props.showBackBtn
|
||||
backBtn = `<a className="hidden button-grey back-btn" onClick={this.back}>BACK</a>`
|
||||
|
||||
selectedDevice = this.selectedDeviceName(@state)
|
||||
|
||||
# build list of webcams
|
||||
|
||||
webcams = []
|
||||
context._.each @state.deviceNames, (deviceName, deviceGuid) ->
|
||||
selected = deviceName == selectedDevice
|
||||
webcams.push `<option key={deviceGuid} value={deviceGuid} selected={selected}>{deviceName}</option>`
|
||||
|
||||
# build list of capture resolutions
|
||||
|
||||
captureResolutions = []
|
||||
# load current settings from backend
|
||||
currentResolution = @state.currentResolution
|
||||
currentFrameRate = @state.currentFrameRate
|
||||
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
resolutions = @state.encodeResolutions
|
||||
frames = @state.frameRates
|
||||
@logger.debug 'FOUND THESE RESOLUTIONS', resolutions
|
||||
@logger.debug 'FOUND THESE FPS', frames
|
||||
context._.each resolutions, (resolution, resolutionKey, obj) =>
|
||||
|
||||
#{1: "CIF (352X288)", 2: "VGA (640X480)", 3: "4CIF (704X576)", 4: "1/2WHD (640X360)", 5: "WHD (1280X720)", 6: "FHD (1920x1080)"}
|
||||
context._.each frames, (frame, key, obj) =>
|
||||
|
||||
frontendResolution = BackendToFrontend[resolutionKey]
|
||||
|
||||
@logger.error("unknown resolution! #{resolution}", BackendToFrontend) unless frontendResolution
|
||||
|
||||
value = "#{resolutionKey}|#{frame}"
|
||||
text = "#{frontendResolution} at #{frame} fps"
|
||||
|
||||
selected = currentResolution + '|' + currentFrameRate == value
|
||||
|
||||
captureResolutions.push `<option key={value} value={value} selected={selected}>{text}</option>`
|
||||
|
||||
autoSelect = false
|
||||
if currentResolution == 0
|
||||
@logger.warn("current resolution not specified; defaulting to VGA")
|
||||
autoSelect = true
|
||||
currentResolution = 2
|
||||
if currentFrameRate == 0
|
||||
autoSelect = true
|
||||
@logger.warn("current frame rate not specified; defaulting to 30")
|
||||
currentFrameRate = 30
|
||||
else
|
||||
convertedFrameRate = BackendToFrontendFPS[currentFrameRate]
|
||||
@logger.debug("translating FPS: backend numeric #{currentFrameRate} to #{convertedFrameRate}")
|
||||
currentFrameRate = convertedFrameRate
|
||||
|
||||
# backend needs to be same as frontend
|
||||
if autoSelect
|
||||
@updateBackend(currentResolution, currentFrameRate)
|
||||
|
||||
if @state.videoShared
|
||||
toggleText = 'STOP WEBCAM'
|
||||
else
|
||||
toggleText = 'TEST WEBCAM'
|
||||
|
||||
if @state.rescanning
|
||||
rescanning =
|
||||
`<span className="rescanning-notice">
|
||||
<span className="spinner-small" />
|
||||
CHECKING GEAR
|
||||
</span>`
|
||||
|
||||
`<form className="video">
|
||||
<h2 className="sub-header select-webcam">select webcam:</h2>
|
||||
<div className="webcam-select-container wizard_control">
|
||||
<select onChange={this.selectWebcam}>
|
||||
{webcams}
|
||||
</select>
|
||||
</div>
|
||||
<h2 className="sub-header select-resolution">select video capture resolution & frame rate:</h2>
|
||||
<div className="webcam-resolution-select-container wizard_control">
|
||||
<select onChange={this.selectResolution}>
|
||||
{captureResolutions}
|
||||
</select>
|
||||
<a className="ftue-video-settings-help">[?]</a>
|
||||
</div>
|
||||
<div className="configure-webcam wizard_control">
|
||||
{backBtn}
|
||||
<a className="button-orange webcam-test-btn" onClick={this.toggleWebcam}>{toggleText}</a>
|
||||
</div>
|
||||
{rescanning}
|
||||
</form>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
if @props.isVisible
|
||||
@beforeShow()
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
$videoSettingsHelp = $root.find('.ftue-video-settings-help')
|
||||
context.JK.helpBubble($videoSettingsHelp, 'ftue-video-settings', {}, {width:300}) if $videoSettingsHelp.length > 0
|
||||
$videoSettingsHelp.click(false)
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
|
||||
@logger.debug("webcam devices", nextState.deviceNames, @state.deviceNames)
|
||||
|
||||
if !@initialScan?
|
||||
@initialScan = true
|
||||
else
|
||||
@findChangedWebcams(nextState.deviceNames, @state.deviceNames)
|
||||
|
||||
componentWillReceiveProps:(nextProps) ->
|
||||
if nextProps.isVisible
|
||||
@beforeShow()
|
||||
else
|
||||
@beforeHide()
|
||||
|
||||
beforeShow:() ->
|
||||
|
||||
VideoActions.refresh()
|
||||
VideoActions.stopVideo()
|
||||
|
||||
context.JK.onBackendEvent(ALERT_NAMES.USB_CONNECTED, 'webcam-viewer', @onUsbDeviceConnected);
|
||||
context.JK.onBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'webcam-viewer', @onUsbDeviceDisconnected);
|
||||
|
||||
beforeHide: () ->
|
||||
|
||||
context.JK.offBackendEvent(ALERT_NAMES.USB_CONNECTED, 'webcam-viewer', @onUsbDeviceConnected);
|
||||
context.JK.offBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'webcam-viewer', @onUsbDeviceDisconnected);
|
||||
|
||||
if @rescanTimeout?
|
||||
clearTimeout(@rescanTimeout)
|
||||
@rescanTimeout = null
|
||||
|
||||
@setVideoOff()
|
||||
|
||||
|
||||
onUsbDeviceConnected: () ->
|
||||
# don't handle USB events when minimized
|
||||
#return if !context.jamClient.IsFrontendVisible()
|
||||
|
||||
logger.debug("USB device connected")
|
||||
|
||||
@scheduleRescanSystem(3000)
|
||||
|
||||
onUsbDeviceDisconnected:() ->
|
||||
# don't handle USB events when minimized
|
||||
#return if !context.jamClient.IsFrontendVisible()
|
||||
|
||||
logger.debug("USB device disconnected")
|
||||
|
||||
@scheduleRescanSystem(3000)
|
||||
|
||||
scheduleRescanSystem: (time) ->
|
||||
if @rescanTimeout?
|
||||
clearTimeout(@rescanTimeout)
|
||||
@rescanTimeout = null
|
||||
|
||||
@setState({rescanning: true})
|
||||
@rescanTimeout = setTimeout(() =>
|
||||
@setState({rescanning: false})
|
||||
VideoActions.refresh()
|
||||
, time)
|
||||
|
||||
selectWebcam:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
device = $(e.target).val()
|
||||
|
||||
VideoActions.selectDevice(device, {})
|
||||
|
||||
updateBackend: (selectedResolution, selectedFps) ->
|
||||
@logger.debug 'Selecting webcam resolution: ', selectedResolution
|
||||
@logger.debug 'Selecting webcam fps: ', selectedFps
|
||||
|
||||
VideoActions.setVideoEncodeResolution(selectedResolution)
|
||||
VideoActions.setSendFrameRate(selectedFps)
|
||||
|
||||
selectResolution:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
resolution = $(e.target).val()
|
||||
@logger.debug 'new capture resolution selected: ' + resolution
|
||||
|
||||
if resolution?
|
||||
bits = resolution.split('|')
|
||||
selectedResolution = bits[0]
|
||||
selectedFps = bits[1]
|
||||
@updateBackend(selectedResolution, selectedFps)
|
||||
|
||||
setVideoOff:() ->
|
||||
VideoActions.stopVideo()
|
||||
|
||||
back: () =>
|
||||
window.location = '/client#/account'
|
||||
|
||||
toggleWebcam:(e) ->
|
||||
e.preventDefault()
|
||||
$toggleBtn = $(e.target)
|
||||
VideoActions.toggleVideo()
|
||||
|
||||
#if this.isVideoShared()
|
||||
# $toggleBtn.removeClass("selected")
|
||||
# VideoActions.stopVideo()
|
||||
# @setState({videoShared: false})
|
||||
#else
|
||||
# $toggleBtn.addClass("selected")
|
||||
# VideoActions.startVideo()
|
||||
# @setState({videoShared: true})
|
||||
|
||||
selectedDeviceName:(state) ->
|
||||
webcamName="None Configured"
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
webcam = state.currentDevice
|
||||
@logger.debug("currently selected video device", webcam)
|
||||
if (webcam? && Object.keys(webcam).length>0)
|
||||
webcamName = Object.keys(webcam)[0]
|
||||
|
||||
webcamName
|
||||
|
||||
findChangedWebcams: (newList, oldList) ->
|
||||
newKeys = Object.keys(newList)
|
||||
oldKeys = Object.keys(oldList)
|
||||
|
||||
webcamSelect = $(@getDOMNode()).find('.webcam-select-container select')
|
||||
|
||||
if newKeys.length > oldKeys.length
|
||||
for newKey in newKeys
|
||||
if oldKeys.indexOf(newKey) == -1
|
||||
newWebcam = newList[newKey]
|
||||
@logger.debug("new webcam found: " + newWebcam, newKey)
|
||||
context.JK.prodBubble(webcamSelect, 'new-webcam-found', {name: newWebcam}, {positions:['right']})
|
||||
break
|
||||
else if newKeys.length < oldKeys.length
|
||||
for oldKey in oldKeys
|
||||
if newKeys.indexOf(oldKey) == -1
|
||||
oldWebcam = oldList[oldKey]
|
||||
@logger.debug("webcam no longer found: " + oldWebcam)
|
||||
context.JK.prodBubble(webcamSelect, 'old-webcam-lost', {name: oldWebcam}, {positions:['right']})
|
||||
break
|
||||
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -2,6 +2,7 @@ $ = jQuery
|
|||
context = window
|
||||
context.JK ||= {};
|
||||
|
||||
|
||||
ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||
|
||||
BackendToFrontend = {
|
||||
|
|
|
|||
|
|
@ -4,21 +4,24 @@
|
|||
|
||||
context.JK = context.JK || {}
|
||||
context.JK.StepVideoGear = function (app, $dialog) {
|
||||
var $step = null
|
||||
var $webcamViewer = new context.JK.WebcamViewer()
|
||||
var $step = null
|
||||
var webcamViewerReact = null;
|
||||
|
||||
function initialize(_$step) {
|
||||
$step = _$step
|
||||
$webcamViewer.init($step, false)
|
||||
var reactElement = React.createElement(window.WebcamViewer, {isVisible: false});
|
||||
var reactDomNode = $step.find(".webcam-container").get(0)
|
||||
webcamViewerReact = React.render(reactElement, reactDomNode)
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
$dialog.getWizard().getDialog().find('h1.top-header').text('video gear setup')
|
||||
$webcamViewer.beforeShow()
|
||||
webcamViewerReact.beforeShow()
|
||||
}
|
||||
|
||||
function beforeHide() {
|
||||
$dialog.getWizard().getDialog().find('h1.top-header').text('audio gear setup')
|
||||
$webcamViewer.beforeHide()
|
||||
webcamViewerReact.beforeHide()
|
||||
}
|
||||
|
||||
this.beforeShow = beforeShow
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@
|
|||
.content-wrapper.account-video {
|
||||
|
||||
.rescanning-notice {
|
||||
display:none;
|
||||
|
||||
span.spinner-small {
|
||||
display:inline-block;
|
||||
|
|
|
|||
|
|
@ -319,7 +319,6 @@
|
|||
.wizard-step.video-gear {
|
||||
.wizard-step-content .wizard-step-column {
|
||||
form.video .rescanning-notice {
|
||||
display:none;
|
||||
margin-top: 20px;
|
||||
margin-left: -5px;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
@import "client/common";
|
||||
|
||||
body.configure-video-popup {
|
||||
|
||||
position:relative;
|
||||
color: $ColorTextTypical;
|
||||
|
||||
#minimal-container {
|
||||
padding-bottom:20px;
|
||||
}
|
||||
|
||||
.popup-contents {
|
||||
width:350px;
|
||||
margin-top:20px;
|
||||
padding-left:44px;
|
||||
padding-right:44px;
|
||||
}
|
||||
|
||||
.control-holder {
|
||||
width:100%;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.control {
|
||||
width:231px;
|
||||
height:34px;
|
||||
@include border_box_sizing;
|
||||
margin-top:15px;
|
||||
padding:3px;
|
||||
background-color:#242323;
|
||||
text-align:center;
|
||||
font-size:13px;
|
||||
border-radius:5px;
|
||||
vertical-align:middle;
|
||||
color:#ccc;
|
||||
}
|
||||
|
||||
|
||||
.control img {
|
||||
vertical-align:middle;
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
.control span {
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.iradio_minimal {
|
||||
float:left;
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
label {
|
||||
padding-top:2px;
|
||||
}
|
||||
|
||||
.field {
|
||||
height:18px;
|
||||
&:nth-child(1) {
|
||||
|
||||
}
|
||||
&:nth-child(2) {
|
||||
margin-top:9px;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
text-decoration:underline;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.important-note {
|
||||
margin-top:60px;
|
||||
line-height:150%;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
.close-behavior {
|
||||
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
font-size:11px;
|
||||
|
||||
position:relative;
|
||||
|
||||
.icheckbox_minimal {
|
||||
top: 4px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.field {
|
||||
position:absolute;
|
||||
left: 43px;
|
||||
top: 16px;
|
||||
}
|
||||
|
||||
label {
|
||||
display:inline;
|
||||
}
|
||||
}
|
||||
.close-link {
|
||||
margin-top:20px;
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
.rescanning-notice {
|
||||
|
||||
span.spinner-small {
|
||||
display:inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
h2.subcaption {
|
||||
color:white;
|
||||
font-size: 23px;
|
||||
font-weight: 400;
|
||||
margin-bottom:20px !important;
|
||||
}
|
||||
|
||||
div.subcaption {
|
||||
line-height:150%;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
.sub-header {
|
||||
color:white;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
margin-bottom:2px;
|
||||
}
|
||||
|
||||
.webcam-container {
|
||||
margin-top:40px;
|
||||
}
|
||||
|
||||
.webcam-test-btn {
|
||||
margin-right: 2px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
select {
|
||||
@include border_box_sizing;
|
||||
width:350px;
|
||||
|
||||
}
|
||||
|
||||
form {
|
||||
@include border_box_sizing;
|
||||
width:350px;
|
||||
}
|
||||
|
||||
a.ftue-video-settings-help {
|
||||
margin-left:15px;
|
||||
|
||||
position: absolute;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.configure-webcam {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.wizard_control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
@import "client/common";
|
||||
|
||||
body.how-to-use-video-popup {
|
||||
|
||||
position:relative;
|
||||
color: $ColorTextTypical;
|
||||
|
||||
#minimal-container {
|
||||
padding-bottom:20px;
|
||||
}
|
||||
|
||||
.popup-contents {
|
||||
padding-left:44px;
|
||||
padding-right:44px;
|
||||
}
|
||||
|
||||
.control-holder {
|
||||
width:100%;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.control {
|
||||
width:231px;
|
||||
height:34px;
|
||||
@include border_box_sizing;
|
||||
margin-top:15px;
|
||||
padding:3px;
|
||||
background-color:#242323;
|
||||
text-align:center;
|
||||
font-size:13px;
|
||||
border-radius:5px;
|
||||
vertical-align:middle;
|
||||
color:#ccc;
|
||||
}
|
||||
|
||||
|
||||
.control img {
|
||||
vertical-align:middle;
|
||||
margin-right:5px;
|
||||
margin-top:-2px;
|
||||
}
|
||||
|
||||
.control span {
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.iradio_minimal {
|
||||
float:left;
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
label {
|
||||
padding-top:2px;
|
||||
}
|
||||
|
||||
.field {
|
||||
height:18px;
|
||||
}
|
||||
|
||||
.note-show-hide {
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
h5 {
|
||||
text-decoration:underline;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.important-note {
|
||||
margin-top:30px;
|
||||
line-height:150%;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
|
||||
.field {
|
||||
margin-top:15px;
|
||||
font-size:11px;
|
||||
padding-left:44px;
|
||||
.icheckbox_minimal {
|
||||
top: 4px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
label {
|
||||
display:inline;
|
||||
}
|
||||
}
|
||||
|
||||
.close-behavior {
|
||||
|
||||
position:relative;
|
||||
margin-top:10px;
|
||||
margin-bottom:10px;
|
||||
font-size:11px;
|
||||
text-align:center;
|
||||
|
||||
|
||||
}
|
||||
.close-link {
|
||||
margin-top:20px;
|
||||
font-size:11px;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
*= require client/screen_common
|
||||
*= require client/content
|
||||
*= require client/ftue
|
||||
*= require client/help
|
||||
*= require icheck/minimal/minimal
|
||||
*= require_directory .
|
||||
*= require client/metronomePlaybackModeSelect
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@ class PopupsController < ApplicationController
|
|||
render :layout => "minimal"
|
||||
end
|
||||
|
||||
def how_to_use_video
|
||||
render :layout => "minimal"
|
||||
end
|
||||
|
||||
def configure_video
|
||||
render :layout => "minimal"
|
||||
end
|
||||
|
||||
def youtube_player
|
||||
@video_id = params[:id]
|
||||
render :layout => "minimal"
|
||||
|
|
|
|||
|
|
@ -21,12 +21,11 @@
|
|||
<div class="video-header">
|
||||
<h2 class="subcaption">video gear:</h2>
|
||||
<div class="subcaption">
|
||||
Select webcam to use for video in sessions. Verify that you see video from webcam in the external application window (it may be behind this window). Configure webcam settings if desired.
|
||||
Select webcam to use for video in sessions. Verify that you see video from webcam in the external application window (it may be behind this window).
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="webcam-container">
|
||||
<%= render 'webcam' %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
%li Select video capture settings.
|
||||
%li Click the Test webcam button to open a window and verify that your webcam is properly capturing video. Then use the Window / Close Video Window menu command to close the window, and click the Next button to move forward.
|
||||
.wizard-step-column
|
||||
=render(partial: '/clients/webcam')
|
||||
.webcam-container
|
||||
.clearall
|
||||
/ Webcam from client can't currently be embedded:
|
||||
/ .wizard-step-column
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render "clients/help" %>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
- provide(:page_name, 'configure-video-popup popup')
|
||||
= react_component 'PopupWrapper', {component: 'PopupConfigureVideoGear'}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
- provide(:page_name, 'how-to-use-video-popup popup')
|
||||
= react_component 'PopupWrapper', {component: 'PopupHowToUseVideo'}
|
||||
|
|
@ -145,6 +145,8 @@ SampleApp::Application.routes.draw do
|
|||
match '/recording-controls', to: 'popups#recording_controls'
|
||||
match '/media-controls', to: 'popups#media_controls'
|
||||
match '/youtube/player', to: 'popups#youtube_player'
|
||||
match '/how-to-use-video', to: 'popups#how_to_use_video'
|
||||
match '/configure-video', to: 'popups#configure_video'
|
||||
end
|
||||
|
||||
scope '/corp' do
|
||||
|
|
|
|||
Loading…
Reference in New Issue