Refactor in preparation for publishing canvas-media on npm
closes MAT-108 flag=none Primary changes revolved around getting canvas-media to talk to RCS instead of directly to canvas. This necessitated passing more info from canvas -> rce -> canvas-media so it can connect. In the process, merged the 2 functions that uploaded closed captions into one. the change in root.js that lazy loads tinyRCE is to get the canvas-rce bundle size below size limits, which it blew after a rebase. test plan: This should work with you config/dynamic_settings.yml development.config.canvas.rich-content-service.app-host value = 'http://host:port' or simply 'host:port' - in the RCE, upload a video with closed caption using the Media > Upload/Record command. > expect the video to show up with CC - edit the captions in the video options tray > expect the captions to be updated > bonus result: no console warning about missing file.name prop from ClosedCaptionPanel - in the RCE, open Upload Document and select a video. > expect the video to upload an show up. - create a media recording type assignment - enable the "Assignment Enhancements - Student" feature - as a student, visit the assignment - click the "Recort/Upload" button > expect the video upload and CC feature to work as expected > bonus fix: the CCs should be at the bottom of the video Change-Id: I7b574bb67998072324954a6b481e0c4d3b3251de Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/264109 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Jeff Largent <jeff.largent@instructure.com> QA-Review: Jeff Largent <jeff.largent@instructure.com> Product-Review: Ed Schiebel <eschiebel@instructure.com>
This commit is contained in:
parent
bae404fc49
commit
044846ef96
|
@ -42,6 +42,7 @@
|
|||
"@instructure/ui-media-player": "^7.2",
|
||||
"@instructure/ui-modal": "^7",
|
||||
"@instructure/ui-pagination": "^7",
|
||||
"@instructure/ui-progress": "^7",
|
||||
"@instructure/ui-react-utils": "^7",
|
||||
"@instructure/ui-select": "^7",
|
||||
"@instructure/ui-spinner": "^7",
|
||||
|
|
|
@ -49,8 +49,12 @@ export default class UploadMedia extends React.Component {
|
|||
})
|
||||
),
|
||||
liveRegion: func,
|
||||
contextId: string,
|
||||
contextType: string,
|
||||
rcsConfig: shape({
|
||||
contextId: string,
|
||||
contextType: string,
|
||||
origin: string.isRequired,
|
||||
headers: shape({Authentication: string.isRequired})
|
||||
}),
|
||||
onStartUpload: func,
|
||||
onUploadComplete: func,
|
||||
onDismiss: func,
|
||||
|
@ -115,13 +119,7 @@ export default class UploadMedia extends React.Component {
|
|||
uploadFile(file) {
|
||||
this.setState({uploading: true})
|
||||
this.props.onStartUpload && this.props.onStartUpload(file)
|
||||
saveMediaRecording(
|
||||
file,
|
||||
this.props.contextId,
|
||||
this.props.contextType,
|
||||
this.saveMediaCallback,
|
||||
this.onSaveMediaProgress
|
||||
)
|
||||
saveMediaRecording(file, this.props.rcsConfig, this.saveMediaCallback, this.onSaveMediaProgress)
|
||||
}
|
||||
|
||||
onSaveMediaProgress = progress => {
|
||||
|
@ -134,7 +132,11 @@ export default class UploadMedia extends React.Component {
|
|||
} else {
|
||||
try {
|
||||
if (this.state.selectedPanel === PANELS.COMPUTER && this.state.subtitles.length > 0) {
|
||||
await saveClosedCaptions(data.mediaObject.media_object.media_id, this.state.subtitles)
|
||||
await saveClosedCaptions(
|
||||
data.mediaObject.media_object.media_id,
|
||||
this.state.subtitles,
|
||||
this.props.rcsConfig
|
||||
)
|
||||
}
|
||||
this.props.onUploadComplete && this.props.onUploadComplete(null, data)
|
||||
} catch (ex) {
|
||||
|
|
|
@ -33,15 +33,20 @@ const uploadMediaTranslations = {
|
|||
SUBMIT_TEXT: 'Submit',
|
||||
UPLOADING_ERROR: 'Upload Error',
|
||||
UPLOAD_MEDIA_LABEL: 'Upload Media',
|
||||
MEDIA_RECORD_NOT_AVAILABLE: 'Record not available'
|
||||
MEDIA_RECORD_NOT_AVAILABLE: 'Record not available',
|
||||
PROGRESS_LABEL: 'Making progress'
|
||||
}
|
||||
}
|
||||
|
||||
function renderComponent(overrideProps = {}) {
|
||||
return render(
|
||||
<UploadMedia
|
||||
contextType="course"
|
||||
contextId="17"
|
||||
rcsConfig={{
|
||||
contextType: 'course',
|
||||
contextId: '17',
|
||||
origin: 'http://host:port',
|
||||
jwt: 'whocares'
|
||||
}}
|
||||
open
|
||||
liveRegion={() => null}
|
||||
onStartUpload={() => {}}
|
||||
|
@ -79,13 +84,10 @@ describe('Upload Media', () => {
|
|||
it('is enabled once ComputerPanel has a file', () => {
|
||||
const {getByText} = renderComponent({
|
||||
tabs: {upload: true},
|
||||
computerFile: {
|
||||
lastModified: 1568991600840,
|
||||
lastModifiedDate: new Date(1568991600840),
|
||||
name: 'dummy-video.mp4',
|
||||
size: 1875112,
|
||||
computerFile: new File(['bits'], 'dummy-video.mp4', {
|
||||
lastModifiedDate: 1568991600840,
|
||||
type: 'video/mp4'
|
||||
}
|
||||
})
|
||||
})
|
||||
expect(getByText('Submit').closest('button')).not.toHaveAttribute('disabled')
|
||||
})
|
||||
|
@ -99,13 +101,10 @@ describe('Upload Media', () => {
|
|||
const {getByText} = renderComponent({
|
||||
onStartUpload,
|
||||
tabs: {upload: true},
|
||||
computerFile: {
|
||||
lastModified: 1568991600840,
|
||||
lastModifiedDate: new Date(1568991600840),
|
||||
name: 'dummy-video.mp4',
|
||||
size: 1875112,
|
||||
computerFile: new File(['bits'], 'dummy-video.mp4', {
|
||||
lastModifiedDate: 1568991600840,
|
||||
type: 'video/mp4'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
fireEvent.click(getByText('Submit'))
|
||||
|
|
|
@ -37,6 +37,13 @@ function mediaServerSession() {
|
|||
}
|
||||
|
||||
describe('saveMediaRecording', () => {
|
||||
const rcsConfig = {
|
||||
contentId: '1',
|
||||
contentType: 'course',
|
||||
origin: 'http://host:port',
|
||||
jwt: 'doesnotmatter'
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
moxios.install()
|
||||
})
|
||||
|
@ -44,26 +51,26 @@ describe('saveMediaRecording', () => {
|
|||
moxios.uninstall()
|
||||
})
|
||||
it('fails if request for kaltura session fails', async done => {
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
moxios.stubRequest('http://host:port/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 500,
|
||||
response: {error: 'womp womp'}
|
||||
})
|
||||
const doneFunction = jest.fn()
|
||||
sinon.stub(K5Uploader.prototype, 'loadUiConf').callsFake(() => 'mock')
|
||||
await saveMediaRecording({}, '1', 'course', doneFunction)
|
||||
await saveMediaRecording({}, rcsConfig, doneFunction)
|
||||
expect(doneFunction).toHaveBeenCalledTimes(1)
|
||||
expect(doneFunction.mock.calls[0][0].message).toBe('Request failed with status code 500')
|
||||
done()
|
||||
})
|
||||
|
||||
it('returns error if k5.fileError is dispatched', () => {
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
moxios.stubRequest('http://host:port/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 200,
|
||||
response: mediaServerSession()
|
||||
})
|
||||
const doneFunction = jest.fn()
|
||||
const progressFunction = jest.fn()
|
||||
return saveMediaRecording({}, '1', 'course', doneFunction, progressFunction).then(uploader => {
|
||||
return saveMediaRecording({}, rcsConfig, doneFunction, progressFunction).then(uploader => {
|
||||
uploader.dispatchEvent('K5.fileError', {error: 'womp womp'}, uploader)
|
||||
expect(doneFunction).toHaveBeenCalledTimes(1)
|
||||
expect(doneFunction.mock.calls[0][0].error).toBe('womp womp')
|
||||
|
@ -71,14 +78,14 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
|
||||
it('k5.ready calls uploaders uploadFile with file', () => {
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
moxios.stubRequest('http://host:port/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 200,
|
||||
response: mediaServerSession()
|
||||
})
|
||||
const doneFunction = jest.fn()
|
||||
const progressFunction = jest.fn()
|
||||
const uploadFileFunc = jest.fn()
|
||||
return saveMediaRecording({file: 'thing'}, '1', 'course', doneFunction, progressFunction).then(
|
||||
return saveMediaRecording({file: 'thing'}, rcsConfig, doneFunction, progressFunction).then(
|
||||
uploader => {
|
||||
uploader.uploadFile = uploadFileFunc
|
||||
uploader.dispatchEvent('K5.ready', uploader)
|
||||
|
@ -89,14 +96,14 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
|
||||
it('k5.progress calls progress function when dispatched', () => {
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
moxios.stubRequest('http://host:port/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 200,
|
||||
response: mediaServerSession()
|
||||
})
|
||||
const doneFunction = jest.fn()
|
||||
const progressFunction = jest.fn()
|
||||
const uploadFileFunc = jest.fn()
|
||||
return saveMediaRecording({file: 'thing'}, '1', 'course', doneFunction, progressFunction).then(
|
||||
return saveMediaRecording({file: 'thing'}, rcsConfig, doneFunction, progressFunction).then(
|
||||
uploader => {
|
||||
uploader.uploadFile = uploadFileFunc
|
||||
uploader.dispatchEvent('K5.progress', uploader)
|
||||
|
@ -106,7 +113,7 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
|
||||
it('k5.complete calls done with canvasMediaObject data if succeeds', () => {
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
moxios.stubRequest('http://host:port/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 200,
|
||||
response: mediaServerSession()
|
||||
})
|
||||
|
@ -116,7 +123,7 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
const doneFunction2 = jest.fn()
|
||||
const progressFunction = jest.fn()
|
||||
return saveMediaRecording({file: 'thing'}, '1', 'course', doneFunction2, progressFunction).then(
|
||||
return saveMediaRecording({file: 'thing'}, rcsConfig, doneFunction2, progressFunction).then(
|
||||
async uploader => {
|
||||
uploader.dispatchEvent('K5.complete', {stuff: 'datatatatatatatat'}, uploader)
|
||||
await new Promise(setTimeout)
|
||||
|
@ -131,7 +138,7 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
|
||||
it('fails if request to create media object fails', async () => {
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
moxios.stubRequest('http://host:port/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 200,
|
||||
response: mediaServerSession()
|
||||
})
|
||||
|
@ -141,7 +148,7 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
const doneFunction2 = jest.fn()
|
||||
const progressFunction = jest.fn()
|
||||
return saveMediaRecording({file: 'thing'}, '1', 'course', doneFunction2, progressFunction).then(
|
||||
return saveMediaRecording({file: 'thing'}, rcsConfig, doneFunction2, progressFunction).then(
|
||||
async uploader => {
|
||||
uploader.dispatchEvent('K5.complete', {stuff: 'datatatatatatatat'}, uploader)
|
||||
await new Promise(setTimeout)
|
||||
|
@ -153,13 +160,19 @@ describe('saveMediaRecording', () => {
|
|||
})
|
||||
|
||||
describe('saveClosedCaptions', () => {
|
||||
const rcsConfig = {
|
||||
host: 'host:port',
|
||||
jwt: 'doesnotmatter',
|
||||
method: 'PUT'
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
moxios.install()
|
||||
})
|
||||
afterEach(() => {
|
||||
moxios.uninstall()
|
||||
})
|
||||
it('returns success promise if axios requests returns correctly', done => {
|
||||
it('returns success promise if axios requests returns correctly', () => {
|
||||
const mediaId = '4'
|
||||
const fileContents = 'file contents'
|
||||
const file = new Blob([fileContents], {type: 'text/plain'})
|
||||
|
@ -167,23 +180,15 @@ describe('saveClosedCaptions', () => {
|
|||
language: {selectedOptionId: 'en'},
|
||||
file
|
||||
}
|
||||
moxios.stubRequest(`/media_objects/${mediaId}/media_tracks`, {
|
||||
moxios.stubRequest(`${rcsConfig.origin}/api/media_objects/${mediaId}/media_tracks`, {
|
||||
status: 200,
|
||||
response: {data: 'media object data'}
|
||||
})
|
||||
const successPromise = saveClosedCaptions(mediaId, [fileAndLanguage])
|
||||
return successPromise
|
||||
.then(() => {
|
||||
expect(true).toBe(true)
|
||||
done()
|
||||
})
|
||||
.catch(() => {
|
||||
expect(false).toBe(true)
|
||||
done()
|
||||
})
|
||||
const successPromise = saveClosedCaptions(mediaId, [fileAndLanguage], rcsConfig)
|
||||
return expect(successPromise).resolves.toMatchObject({data: {data: 'media object data'}})
|
||||
})
|
||||
|
||||
it('returns failure promise if axios request fails', done => {
|
||||
it('returns failure promise if axios request fails', () => {
|
||||
const mediaId = '4'
|
||||
const fileContents = 'file contents'
|
||||
const file = new Blob([fileContents], {type: 'text/plain'})
|
||||
|
@ -191,19 +196,11 @@ describe('saveClosedCaptions', () => {
|
|||
language: {selectedOptionId: 'en'},
|
||||
file
|
||||
}
|
||||
moxios.stubRequest(`/media_objects/${mediaId}/media_tracks`, {
|
||||
moxios.stubRequest(`${rcsConfig.origin}/api/media_objects/${mediaId}/media_tracks`, {
|
||||
status: 500,
|
||||
response: {data: 'media object data'}
|
||||
})
|
||||
const successPromise = saveClosedCaptions(mediaId, [fileAndLanguage])
|
||||
return successPromise
|
||||
.then(() => {
|
||||
expect(false).toBe(true)
|
||||
done()
|
||||
})
|
||||
.catch(() => {
|
||||
expect(true).toBe(true)
|
||||
done()
|
||||
})
|
||||
const successPromise = saveClosedCaptions(mediaId, [fileAndLanguage], rcsConfig)
|
||||
return expect(successPromise).rejects.toMatchObject({response: {status: 500}})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ import RocketSVG from './RocketSVG'
|
|||
import useComputerPanelFocus from './useComputerPanelFocus'
|
||||
import {isAudio, isVideo, isPreviewable, sizeMediaPlayer} from './shared/utils'
|
||||
import LoadingIndicator from './shared/LoadingIndicator'
|
||||
import saveMediaRecording from './saveMediaRecording'
|
||||
import saveMediaRecording, {saveClosedCaptions} from './saveMediaRecording'
|
||||
|
||||
export {
|
||||
UploadMedia as default,
|
||||
|
@ -34,5 +34,6 @@ export {
|
|||
isPreviewable,
|
||||
sizeMediaPlayer,
|
||||
LoadingIndicator,
|
||||
saveMediaRecording
|
||||
saveMediaRecording,
|
||||
saveClosedCaptions
|
||||
}
|
||||
|
|
|
@ -95,12 +95,15 @@ function addUploaderFileCompleteEventListeners(uploader, context, file, done, on
|
|||
})
|
||||
}
|
||||
|
||||
export default async function saveMediaRecording(file, contextId, contextType, done, onProgress) {
|
||||
export default async function saveMediaRecording(file, rcsConfig, done, onProgress) {
|
||||
try {
|
||||
window.addEventListener('beforeunload', handleUnloadWhileUploading)
|
||||
const mediaServerSession = await axios.post(
|
||||
'/api/v1/services/kaltura_session?include_upload_config=1'
|
||||
)
|
||||
|
||||
const mediaServerSession = await axios({
|
||||
method: 'POST',
|
||||
url: `${rcsConfig.origin}/api/v1/services/kaltura_session?include_upload_config=1`,
|
||||
headers: rcsConfig.headers
|
||||
})
|
||||
if (onProgress) {
|
||||
onProgress(STARTING_PROGRESS_VALUE)
|
||||
}
|
||||
|
@ -114,38 +117,60 @@ export default async function saveMediaRecording(file, contextId, contextType, d
|
|||
addUploaderProgressEventListeners(k5UploaderSession, onProgress)
|
||||
}
|
||||
addUploaderFileErrorEventListeners(k5UploaderSession, done, file)
|
||||
addUploaderFileCompleteEventListeners(
|
||||
k5UploaderSession,
|
||||
{contextId, contextType},
|
||||
file,
|
||||
done,
|
||||
onProgress
|
||||
)
|
||||
addUploaderFileCompleteEventListeners(k5UploaderSession, rcsConfig, file, done, onProgress)
|
||||
return k5UploaderSession
|
||||
} catch (err) {
|
||||
doDone(done, err, {uploadedFile: file})
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveClosedCaptions(mediaId, files) {
|
||||
const axiosRequests = []
|
||||
files.forEach(function (file) {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
const content = e.target.result
|
||||
const params = {
|
||||
content,
|
||||
locale: file.locale,
|
||||
'exclude[]': 'sources'
|
||||
/*
|
||||
* @media_object_id: id of the media_object we're assigning CC to
|
||||
* @subtitles: [{locale: string locale, file: JS File object}]
|
||||
* @rcsConfig: {origin, headers, method} where method=PUT for update or POST for create
|
||||
*/
|
||||
export async function saveClosedCaptions(media_object_id, subtitles, rcsConfig) {
|
||||
// read all the subtitle files' contents
|
||||
const file_promises = []
|
||||
subtitles.forEach(st => {
|
||||
if (st.isNew) {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function (e) {
|
||||
resolve({locale: st.locale, content: e.target.result})
|
||||
}
|
||||
axios.post(`/media_objects/${mediaId}/media_tracks`, params).then(resolve).catch(reject)
|
||||
}
|
||||
reader.readAsText(file.file)
|
||||
})
|
||||
axiosRequests.push(p)
|
||||
reader.onerror = function (e) {
|
||||
e.target.abort()
|
||||
reject(e.target.error || e)
|
||||
}
|
||||
reader.readAsText(st.file)
|
||||
})
|
||||
file_promises.push(p)
|
||||
} else {
|
||||
file_promises.push(Promise.resolve({locale: st.locale}))
|
||||
}
|
||||
})
|
||||
return Promise.all(axiosRequests)
|
||||
|
||||
// once all the promises from reading the subtitles' files
|
||||
// have resolved, PUT/POST the resulting subtitle objects to the RCS
|
||||
// when that completes, the update_promise will resolve
|
||||
const update_promise = new Promise((resolve, reject) => {
|
||||
Promise.all(file_promises)
|
||||
.then(closed_captions => {
|
||||
axios({
|
||||
method: rcsConfig.method || 'PUT',
|
||||
url: `${rcsConfig.origin}/api/media_objects/${media_object_id}/media_tracks`,
|
||||
headers: rcsConfig.headers,
|
||||
data: closed_captions
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(e => {
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
.catch(e => reject(e))
|
||||
})
|
||||
return update_promise
|
||||
}
|
||||
|
||||
function doDone(done, ...rest) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import normalizeLocale from './rce/normalizeLocale'
|
||||
import {renderIntoDiv as render} from './rce/root'
|
||||
import getRceTranslations from './getRceTranslations'
|
||||
import {headerFor, originFromHost} from './sidebar/sources/api'
|
||||
import 'tinymce'
|
||||
|
||||
if (process.env.BUILD_LOCALE && process.env.BUILD_LOCALE !== 'en') {
|
||||
|
@ -55,3 +56,11 @@ export function renderIntoDiv(editorEl, props, cb) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getRCSAuthenticationHeaders(jwt) {
|
||||
return headerFor(jwt)
|
||||
}
|
||||
|
||||
export function getRCSOriginFromHost(host) {
|
||||
return originFromHost(host)
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import formatMessage from '../../../../format-message'
|
||||
|
||||
const MediaCaptureStrings = {
|
||||
ARIA_VIDEO_LABEL: formatMessage('Video Player'),
|
||||
ARIA_VOLUME: formatMessage('Current Volume Level'),
|
||||
ARIA_RECORDING: formatMessage('Recording'),
|
||||
DEFAULT_ERROR: formatMessage('Something went wrong accessing your mic or webcam.'),
|
||||
DEVICE_AUDIO: formatMessage('Mic'),
|
||||
DEVICE_VIDEO: formatMessage('Webcam'),
|
||||
FILE_PLACEHOLDER: formatMessage('Untitled'),
|
||||
FINISH: formatMessage('Finish'),
|
||||
NO_WEBCAM: formatMessage('No Video'),
|
||||
NOT_ALLOWED_ERROR: formatMessage('Please allow Canvas to access your microphone and webcam.'),
|
||||
NOT_READABLE_ERROR: formatMessage('Your webcam may already be in use.'),
|
||||
PLAYBACK_PAUSE: formatMessage('Pause'),
|
||||
PLAYBACK_PLAY: formatMessage('Play'),
|
||||
PREVIEW: formatMessage('PREVIEW'),
|
||||
SAVE: formatMessage('Save'),
|
||||
SR_FILE_INPUT: formatMessage('File name'),
|
||||
START: formatMessage('Start Recording'),
|
||||
START_OVER: formatMessage('Start Over')
|
||||
}
|
||||
|
||||
export {MediaCaptureStrings}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Alert} from '@instructure/ui-alerts'
|
||||
import {canUseMediaCapture, MediaCapture} from '@instructure/media-capture'
|
||||
import formatMessage from '../../../../format-message'
|
||||
import {MediaCaptureStrings} from './MediaCaptureStrings'
|
||||
import {object, func} from 'prop-types'
|
||||
import React from 'react'
|
||||
|
||||
export default class MediaRecorder extends React.Component {
|
||||
saveFile = file => {
|
||||
this.props.contentProps.saveMediaRecording(file, this.props.editor, this.props.dismiss)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{canUseMediaCapture() ? (
|
||||
<MediaCapture translations={MediaCaptureStrings} onCompleted={this.saveFile} />
|
||||
) : (
|
||||
<Alert variant="error" margin="small">
|
||||
{formatMessage('Error uploading video/audio recording')}
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
MediaRecorder.propTypes = {
|
||||
contentProps: object.isRequired,
|
||||
dismiss: func.isRequired,
|
||||
editor: object.isRequired
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import {render, fireEvent} from '@testing-library/react'
|
||||
import {UploadMedia} from '../index'
|
||||
|
||||
describe('UploadMedia', () => {
|
||||
it('calls onDismiss prop when closing', () => {
|
||||
const handleDismiss = jest.fn()
|
||||
const {getAllByText} = render(<UploadMedia editor={{}} onDismiss={handleDismiss} />)
|
||||
|
||||
const closeBtn = getAllByText('Close')[0]
|
||||
fireEvent.click(closeBtn)
|
||||
expect(handleDismiss).toHaveBeenCalled()
|
||||
})
|
||||
})
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2019 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {Alert} from '@instructure/ui-alerts'
|
||||
import {Button, CloseButton} from '@instructure/ui-buttons'
|
||||
import formatMessage from '../../../../format-message'
|
||||
import {func, object} from 'prop-types'
|
||||
import {Heading} from '@instructure/ui-heading'
|
||||
import {Spinner} from '@instructure/ui-spinner'
|
||||
import {Modal} from '@instructure/ui-modal'
|
||||
import {Mask} from '@instructure/ui-overlays'
|
||||
import React, {Suspense, useState} from 'react'
|
||||
import {Tabs} from '@instructure/ui-tabs'
|
||||
import {View} from '@instructure/ui-view'
|
||||
|
||||
import Bridge from '../../../../bridge'
|
||||
import {StoreProvider} from '../../shared/StoreContext'
|
||||
|
||||
const MediaRecorder = React.lazy(() => import('./MediaRecorder'))
|
||||
const ComputerPanel = React.lazy(() => import('../../shared/Upload/ComputerPanel'))
|
||||
|
||||
export const PANELS = {
|
||||
COMPUTER: 0,
|
||||
RECORD: 1
|
||||
}
|
||||
|
||||
const ALERT_TIMEOUT = 5000
|
||||
const ACCEPTED_FILE_TYPES = [
|
||||
'3gp',
|
||||
'aac',
|
||||
'amr',
|
||||
'asf',
|
||||
'avi',
|
||||
'flac',
|
||||
'flv',
|
||||
'm4a',
|
||||
'm4v',
|
||||
'mkv',
|
||||
'mov',
|
||||
'mp3',
|
||||
'mp4',
|
||||
'mpeg',
|
||||
'mpg',
|
||||
'ogg',
|
||||
'qt',
|
||||
'wav',
|
||||
'wma',
|
||||
'wmv'
|
||||
]
|
||||
|
||||
export const handleSubmit = (editor, selectedPanel, uploadData, saveMediaRecording, onDismiss) => {
|
||||
const {theFile} = uploadData
|
||||
saveMediaRecording('media', theFile)
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
function renderLoading() {
|
||||
return formatMessage('Loading')
|
||||
}
|
||||
|
||||
function renderLoadingMedia() {
|
||||
return formatMessage('Loading media')
|
||||
}
|
||||
|
||||
function loadingErrorSuccess(states) {
|
||||
if (states.uploadingMediaStatus.loading) {
|
||||
return (
|
||||
<Mask>
|
||||
<Spinner renderTitle={renderLoading} size="large" margin="0 0 0 medium" />
|
||||
</Mask>
|
||||
)
|
||||
} else if (states.uploadingMediaStatus.error) {
|
||||
return (
|
||||
<Alert variant="error" margin="small" timeout={ALERT_TIMEOUT}>
|
||||
{formatMessage('Error uploading video/audio recording')}
|
||||
</Alert>
|
||||
)
|
||||
} else if (states.uploadingMediaStatus.uploaded) {
|
||||
return <Alert timeout={ALERT_TIMEOUT}>{formatMessage('Video/audio recording uploaded')}</Alert>
|
||||
}
|
||||
}
|
||||
|
||||
export function UploadMedia(props) {
|
||||
const [theFile, setFile] = useState(null)
|
||||
const [hasUploadedFile, setHasUploadedFile] = useState(false)
|
||||
const [selectedPanel, setSelectedPanel] = useState(0)
|
||||
const trayProps = Bridge.trayProps.get(props.editor)
|
||||
|
||||
return (
|
||||
<StoreProvider {...trayProps}>
|
||||
{contentProps => (
|
||||
<Modal
|
||||
label={formatMessage('Upload Media')}
|
||||
size="medium"
|
||||
onDismiss={props.onDismiss}
|
||||
open
|
||||
shouldCloseOnDocumentClick={false}
|
||||
>
|
||||
<Modal.Header>
|
||||
<CloseButton onClick={props.onDismiss} offset="medium" placement="end">
|
||||
{formatMessage('Close')}
|
||||
</CloseButton>
|
||||
<Heading>{formatMessage('Upload Media')}</Heading>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{loadingErrorSuccess(contentProps.upload)}
|
||||
<Tabs
|
||||
shouldFocusOnRender
|
||||
size="large"
|
||||
selectedIndex={selectedPanel}
|
||||
onRequestTabChange={newIndex => setSelectedPanel(newIndex)}
|
||||
>
|
||||
<Tabs.Panel title={formatMessage('Computer')}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<View as="div" height="100%" width="100%" textAlign="center">
|
||||
<Spinner
|
||||
renderTitle={renderLoadingMedia}
|
||||
size="large"
|
||||
margin="0 0 0 medium"
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
size="large"
|
||||
>
|
||||
<ComputerPanel
|
||||
editor={props.editor}
|
||||
theFile={theFile}
|
||||
setFile={setFile}
|
||||
hasUploadedFile={hasUploadedFile}
|
||||
setHasUploadedFile={setHasUploadedFile}
|
||||
label={formatMessage('Drag a File Here')}
|
||||
accept={ACCEPTED_FILE_TYPES}
|
||||
/>
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel title={formatMessage('Record')}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<View as="div" height="100%" width="100%" textAlign="center">
|
||||
<Spinner
|
||||
renderTitle={renderLoadingMedia}
|
||||
size="large"
|
||||
margin="0 0 0 medium"
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
>
|
||||
<MediaRecorder
|
||||
editor={props.editor}
|
||||
dismiss={props.onDismiss}
|
||||
contentProps={contentProps}
|
||||
/>
|
||||
</Suspense>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</Modal.Body>
|
||||
{selectedPanel !== PANELS.RECORD && (
|
||||
<Modal.Footer>
|
||||
<Button onClick={props.onDismiss}>{formatMessage('Close')}</Button>
|
||||
<Button
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
handleSubmit(
|
||||
props.editor,
|
||||
selectedPanel,
|
||||
{theFile},
|
||||
contentProps.saveMediaRecording,
|
||||
props.onDismiss
|
||||
)
|
||||
}}
|
||||
variant="primary"
|
||||
type="submit"
|
||||
>
|
||||
{formatMessage('Submit')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
)}
|
||||
</Modal>
|
||||
)}
|
||||
</StoreProvider>
|
||||
)
|
||||
}
|
||||
|
||||
UploadMedia.propTypes = {
|
||||
onDismiss: func.isRequired,
|
||||
editor: object.isRequired
|
||||
}
|
|
@ -226,7 +226,7 @@ export default function VideoOptionsTray(props) {
|
|||
<ClosedCaptionPanel
|
||||
subtitles={subtitles.map(st => ({
|
||||
locale: st.locale,
|
||||
file: {name: st.language} // this is an artifact of ClosedCaptionCreatorRow's inards
|
||||
file: {name: st.language || st.locale} // this is an artifact of ClosedCaptionCreatorRow's inards
|
||||
}))}
|
||||
uploadMediaTranslations={Bridge.uploadMediaTranslations}
|
||||
languages={Bridge.languages}
|
||||
|
|
|
@ -21,11 +21,11 @@ import ReactDOM from 'react-dom'
|
|||
import Bridge from '../../../bridge'
|
||||
import {StoreProvider} from '../shared/StoreContext'
|
||||
import formatMessage from '../../../format-message'
|
||||
import {headerFor, originFromHost} from '../../../sidebar/sources/api'
|
||||
|
||||
export default function(ed, document) {
|
||||
export default function (ed, document) {
|
||||
return import('@instructure/canvas-media').then(CanvasMedia => {
|
||||
const UploadMedia = CanvasMedia.default
|
||||
// return import('./UploadMedia').then(({UploadMedia}) => {
|
||||
let container = document.querySelector('.canvas-rce-media-upload')
|
||||
if (!container) {
|
||||
container = document.createElement('div')
|
||||
|
@ -80,8 +80,12 @@ export default function(ed, document) {
|
|||
<StoreProvider {...trayProps}>
|
||||
{contentProps => (
|
||||
<UploadMedia
|
||||
contextType={ed.settings.canvas_rce_user_context.type}
|
||||
contextId={ed.settings.canvas_rce_user_context.id}
|
||||
rcsConfig={{
|
||||
contextType: ed.settings.canvas_rce_user_context.type,
|
||||
contextId: ed.settings.canvas_rce_user_context.id,
|
||||
origin: originFromHost(contentProps.host),
|
||||
headers: headerFor(contentProps.jwt)
|
||||
}}
|
||||
languages={Bridge.languages}
|
||||
open
|
||||
liveRegion={() => document.getElementById('flash_screenreader_holder')}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
import React, {createRef} from 'react'
|
||||
import {render, unmountComponentAtNode} from 'react-dom'
|
||||
import RCEWrapper from './RCEWrapper'
|
||||
import tinyRCE from './tinyRCE'
|
||||
import normalizeProps from './normalizeProps'
|
||||
import formatMessage from '../format-message'
|
||||
|
||||
|
@ -32,24 +31,28 @@ if (!process?.env?.BUILD_LOCALE) {
|
|||
}
|
||||
|
||||
export function renderIntoDiv(target, props, renderCallback) {
|
||||
// normalize props
|
||||
props = normalizeProps(props, tinyRCE)
|
||||
import('./tinyRCE').then(module => {
|
||||
const tinyRCE = module.default
|
||||
|
||||
formatMessage.setup({locale: props.language})
|
||||
// render the editor to the target element
|
||||
const renderedComponent = createRef()
|
||||
render(
|
||||
<RCEWrapper
|
||||
ref={renderedComponent}
|
||||
{...props}
|
||||
handleUnmount={() => unmountComponentAtNode(target)}
|
||||
/>,
|
||||
target,
|
||||
() => {
|
||||
// pass it back
|
||||
renderCallback && renderCallback(renderedComponent.current)
|
||||
}
|
||||
)
|
||||
// normalize props
|
||||
props = normalizeProps(props, tinyRCE)
|
||||
|
||||
formatMessage.setup({locale: props.language})
|
||||
// render the editor to the target element
|
||||
const renderedComponent = createRef()
|
||||
render(
|
||||
<RCEWrapper
|
||||
ref={renderedComponent}
|
||||
{...props}
|
||||
handleUnmount={() => unmountComponentAtNode(target)}
|
||||
/>,
|
||||
target,
|
||||
() => {
|
||||
// pass it back
|
||||
renderCallback && renderCallback(renderedComponent.current)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Adding this event listener fixes LA-212. I have no idea why. In Safari it
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import {saveMediaRecording} from '@instructure/canvas-media'
|
||||
import {headerFor, originFromHost} from '../sources/api'
|
||||
import * as files from './files'
|
||||
import * as images from './images'
|
||||
import bridge from '../../bridge'
|
||||
|
@ -135,10 +136,7 @@ export function embedUploadResult(results, selectedTabType) {
|
|||
const embedData = fileEmbed(results)
|
||||
if (selectedTabType === 'images' && isImage(embedData.type) && results.displayAs !== 'link') {
|
||||
// embed the image after any current selection rather than link to it or replace it
|
||||
bridge
|
||||
.activeEditor()
|
||||
?.mceInstance()
|
||||
?.selection.collapse()
|
||||
bridge.activeEditor()?.mceInstance()?.selection.collapse()
|
||||
const file_props = {
|
||||
href: results.href || results.url,
|
||||
title: results.title,
|
||||
|
@ -153,10 +151,7 @@ export function embedUploadResult(results, selectedTabType) {
|
|||
bridge.insertImage(file_props)
|
||||
} else if (selectedTabType === 'media' && isAudioOrVideo(embedData.type)) {
|
||||
// embed media after any current selection rather than link to it or replace it
|
||||
bridge
|
||||
.activeEditor()
|
||||
?.mceInstance()
|
||||
?.selection.collapse()
|
||||
bridge.activeEditor()?.mceInstance()?.selection.collapse()
|
||||
|
||||
// when we record audio, notorious thinks it's a video. use the content type we got
|
||||
// from the recoreded file, not the returned media object.
|
||||
|
@ -222,7 +217,7 @@ export function fetchFolders(bookmark) {
|
|||
|
||||
// uploads handled via canvas-media
|
||||
export function mediaUploadComplete(error, uploadData) {
|
||||
const {mediaObject, uploadedFile} = uploadData
|
||||
const {mediaObject, uploadedFile} = uploadData || {}
|
||||
return (dispatch, _getState) => {
|
||||
if (error) {
|
||||
dispatch(failMediaUpload(error))
|
||||
|
@ -263,8 +258,12 @@ export function uploadToMediaFolder(tabContext, fileMetaProps) {
|
|||
if (tabContext === 'media' && fileMetaProps.domObject) {
|
||||
return saveMediaRecording(
|
||||
fileMetaProps.domObject,
|
||||
contextId,
|
||||
contextType,
|
||||
{
|
||||
contextId,
|
||||
contextType,
|
||||
origin: originFromHost(host),
|
||||
headers: headerFor(jwt)
|
||||
},
|
||||
(err, uploadData) => {
|
||||
dispatch(mediaUploadComplete(err, uploadData))
|
||||
}
|
||||
|
|
|
@ -32,7 +32,9 @@ export function propsFromState(state) {
|
|||
upload,
|
||||
session,
|
||||
newPageLinkExpanded,
|
||||
all_files
|
||||
all_files,
|
||||
jwt,
|
||||
host
|
||||
} = state
|
||||
|
||||
const collections = {}
|
||||
|
@ -57,6 +59,8 @@ export function propsFromState(state) {
|
|||
session,
|
||||
newPageLinkExpanded,
|
||||
...ui,
|
||||
all_files
|
||||
all_files,
|
||||
jwt,
|
||||
host
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,14 +18,30 @@
|
|||
|
||||
import 'isomorphic-fetch'
|
||||
import {parse} from 'url'
|
||||
import {saveClosedCaptions} from '@instructure/canvas-media'
|
||||
import {downloadToWrap, fixupFileUrl} from '../../common/fileUrl'
|
||||
import formatMessage from '../../format-message'
|
||||
import alertHandler from '../../rce/alertHandler'
|
||||
|
||||
function headerFor(jwt) {
|
||||
export function headerFor(jwt) {
|
||||
return {Authorization: 'Bearer ' + jwt}
|
||||
}
|
||||
|
||||
export function originFromHost(host, windowOverride) {
|
||||
let origin = host
|
||||
|
||||
if (typeof origin !== 'string') {
|
||||
origin = ''
|
||||
} else if (origin && origin.substr(0, 4) !== 'http') {
|
||||
origin = `//${origin}`
|
||||
const windowHandle = windowOverride || (typeof window !== 'undefined' ? window : undefined)
|
||||
if (origin.length > 0 && windowHandle?.location?.protocol) {
|
||||
origin = `${windowHandle.location.protocol}${origin}`
|
||||
}
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// filter a response to raise an error on a 400+ status
|
||||
function checkStatus(response) {
|
||||
if (response.status < 400) {
|
||||
|
@ -200,52 +216,16 @@ class RceApiSource {
|
|||
// PUT to //RCS/api/media_objects/:mediaId/media_tracks [{locale, content}, ...]
|
||||
// receive back a 200 with the new subtitles, or a 4xx error
|
||||
updateClosedCaptions(apiProps, {media_object_id, subtitles}) {
|
||||
// read all the subtitle files' contents
|
||||
const file_promises = []
|
||||
subtitles.forEach(st => {
|
||||
if (st.isNew) {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = function(e) {
|
||||
resolve({locale: st.locale, content: e.target.result})
|
||||
}
|
||||
reader.onerror = function(e) {
|
||||
e.target.abort()
|
||||
reject(e)
|
||||
}
|
||||
reader.readAsText(st.file)
|
||||
})
|
||||
file_promises.push(p)
|
||||
} else {
|
||||
file_promises.push(Promise.resolve({locale: st.locale}))
|
||||
}
|
||||
return saveClosedCaptions(media_object_id, subtitles, {
|
||||
origin: originFromHost(apiProps.host),
|
||||
headers: headerFor(apiProps.jwt)
|
||||
}).catch(e => {
|
||||
console.error('Failed saving CC', e)
|
||||
this.alertFunc({
|
||||
text: formatMessage('Uploading closed captions/subtitles failed.'),
|
||||
variant: 'error'
|
||||
})
|
||||
})
|
||||
|
||||
// once all the promises from reading the subtitles' files
|
||||
// have resolved, PUT the resulting subtitle objects to the RCS
|
||||
// when that completes, the update_promise will resolve
|
||||
const update_promise = new Promise((resolve, reject) => {
|
||||
Promise.all(file_promises)
|
||||
.then(closed_captions => {
|
||||
const uri = `${this.baseUri(
|
||||
'media_objects',
|
||||
apiProps.host
|
||||
)}/${media_object_id}/media_tracks`
|
||||
return this.apiPost(uri, headerFor(this.jwt), closed_captions, 'PUT')
|
||||
.then(resolve)
|
||||
.catch(e => {
|
||||
console.error('failed updating media_tracks') // eslint-disable-line no-console
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
.catch(_e => {
|
||||
this.alertFunc({
|
||||
text: formatMessage('Reading a media track file failed. Aborting.'),
|
||||
variant: 'error'
|
||||
})
|
||||
})
|
||||
})
|
||||
return update_promise
|
||||
}
|
||||
|
||||
// GET /media_objects/:mediaId/media_tracks
|
||||
|
@ -492,20 +472,8 @@ class RceApiSource {
|
|||
if (!host && this.host) {
|
||||
host = this.host
|
||||
}
|
||||
if (typeof host !== 'string') {
|
||||
host = ''
|
||||
} else if (host && host.substr(0, 4) !== 'http') {
|
||||
host = `//${host}`
|
||||
const windowHandle = windowOverride || (typeof window !== 'undefined' ? window : undefined)
|
||||
if (
|
||||
host.length > 0 &&
|
||||
windowHandle &&
|
||||
windowHandle.location &&
|
||||
windowHandle.location.protocol
|
||||
) {
|
||||
host = `${windowHandle.location.protocol}${host}`
|
||||
}
|
||||
}
|
||||
host = originFromHost(host, windowOverride)
|
||||
|
||||
const sharedEndpoints = ['images', 'media', 'documents', 'all'] // 'all' will eventually be something different
|
||||
const endpt = sharedEndpoints.includes(endpoint) ? 'documents' : endpoint
|
||||
return `${host}/api/${endpt}`
|
||||
|
|
|
@ -82,13 +82,14 @@ describe('Upload data actions', () => {
|
|||
})
|
||||
|
||||
const defaults = {
|
||||
host: 'http://host:port',
|
||||
jwt: 'theJWT',
|
||||
source: successSource
|
||||
}
|
||||
|
||||
function setupState(props) {
|
||||
const {jwt, source} = {...defaults, ...props}
|
||||
return {jwt, source}
|
||||
const {host, jwt, source} = {...defaults, ...props}
|
||||
return {host, jwt, source}
|
||||
}
|
||||
|
||||
describe('fetchFolders', () => {
|
||||
|
@ -256,38 +257,44 @@ describe('Upload data actions', () => {
|
|||
type: 'video/mov'
|
||||
}
|
||||
}
|
||||
let k5uploaderstub
|
||||
|
||||
beforeEach(() => {
|
||||
moxios.install()
|
||||
k5uploaderstub = sinon.stub(K5Uploader.prototype, 'loadUiConf').callsFake(() => 'mock')
|
||||
})
|
||||
afterEach(() => {
|
||||
moxios.uninstall()
|
||||
k5uploaderstub.restore()
|
||||
})
|
||||
|
||||
it('uploads directly to notorious/kaltura', () => {
|
||||
const baseState = setupState()
|
||||
const store = spiedStore(baseState)
|
||||
|
||||
// I really just wanted to stub saveMediaRecording and assert that it's called,
|
||||
// but sinon can't stub functions from es6 modules.
|
||||
// The next best thing is to check that the K5Uploader is exercised
|
||||
moxios.stubRequest('/api/v1/services/kaltura_session?include_upload_config=1', {
|
||||
status: 200,
|
||||
response: {
|
||||
ks: 'averylongstring',
|
||||
subp_id: '0',
|
||||
partner_id: '9',
|
||||
uid: '1234_567',
|
||||
serverTime: 1234,
|
||||
kaltura_setting: {
|
||||
uploadUrl: 'url.url.url',
|
||||
entryUrl: 'url.url.url',
|
||||
uiconfUrl: 'url.url.url',
|
||||
partnerData: 'data from our partners'
|
||||
moxios.stubRequest(
|
||||
'http://host:port/api/v1/services/kaltura_session?include_upload_config=1',
|
||||
{
|
||||
status: 200,
|
||||
response: {
|
||||
ks: 'averylongstring',
|
||||
subp_id: '0',
|
||||
partner_id: '9',
|
||||
uid: '1234_567',
|
||||
serverTime: 1234,
|
||||
kaltura_setting: {
|
||||
uploadUrl: 'url.url.url',
|
||||
entryUrl: 'url.url.url',
|
||||
uiconfUrl: 'url.url.url',
|
||||
partnerData: 'data from our partners'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
const k5uploaderstub = sinon.stub(K5Uploader.prototype, 'loadUiConf').callsFake(() => 'mock')
|
||||
)
|
||||
|
||||
const baseState = setupState()
|
||||
const store = spiedStore(baseState)
|
||||
return store.dispatch(actions.uploadToMediaFolder('media', fakeFileMetaData)).then(() => {
|
||||
sinon.assert.called(k5uploaderstub)
|
||||
})
|
||||
|
@ -544,7 +551,7 @@ describe('Upload data actions', () => {
|
|||
const R = global.Response
|
||||
beforeEach(() => {
|
||||
if (typeof Response !== 'function') {
|
||||
global.Response = function(body, status) {
|
||||
global.Response = function (body, status) {
|
||||
this.status = status
|
||||
this.json = () => {
|
||||
return Promise.resolve(JSON.parse(body))
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import assert from 'assert'
|
||||
import sinon from 'sinon'
|
||||
import RceApiSource from '../../../src/sidebar/sources/api'
|
||||
import RceApiSource, {headerFor, originFromHost} from '../../../src/sidebar/sources/api'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import * as fileUrl from '../../../src/common/fileUrl'
|
||||
|
||||
|
@ -639,4 +639,40 @@ describe('sources/api', () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('headerFor', () => {
|
||||
it('returns an authorization header', () => {
|
||||
assert.deepStrictEqual(headerFor('the_jwt'), {
|
||||
Authorization: 'Bearer the_jwt'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('originFromHost', () => {
|
||||
// this logic was factored out from baseUri, so the logic is tested
|
||||
// there too.
|
||||
it('uses the incoming http(s) protocol if present', () => {
|
||||
assert.strictEqual(originFromHost('http://host:port'), 'http://host:port', 'echoes http')
|
||||
assert.strictEqual(originFromHost('https://host:port'), 'https://host:port', 'echoes https')
|
||||
assert.strictEqual(originFromHost('host:port', {}), '//host:port', 'no protocol')
|
||||
})
|
||||
|
||||
it('uses the windowOverride protocol if present', () => {
|
||||
const win = {
|
||||
location: {
|
||||
protocol: 'https:'
|
||||
}
|
||||
}
|
||||
assert.strictEqual(
|
||||
originFromHost('http://host:port', win),
|
||||
'http://host:port',
|
||||
'use provided protocol'
|
||||
)
|
||||
assert.strictEqual(
|
||||
originFromHost('host:port', win),
|
||||
'https://host:port',
|
||||
'use window protocol'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
MediaCaptureStrings,
|
||||
SelectStrings
|
||||
} from '../../helpers/UploadMediaTranslations'
|
||||
import {getRCSAuthenticationHeaders, getRCSOriginFromHost} from '@instructure/canvas-rce'
|
||||
|
||||
import {Billboard} from '@instructure/ui-billboard'
|
||||
import {Button} from '@instructure/ui-buttons'
|
||||
|
@ -129,7 +130,11 @@ export default class MediaAttempt extends React.Component {
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<MediaPlayer tracks={mediaTracks} sources={mediaObject.mediaSources} />
|
||||
<MediaPlayer
|
||||
tracks={mediaTracks}
|
||||
sources={mediaObject.mediaSources}
|
||||
captionPosition="bottom"
|
||||
/>
|
||||
)}
|
||||
</Flex.Item>
|
||||
<Flex.Item overflowY="visible" margin="medium 0">
|
||||
|
@ -171,8 +176,12 @@ export default class MediaAttempt extends React.Component {
|
|||
<UploadMedia
|
||||
onUploadComplete={this.onComplete}
|
||||
onDismiss={this.onDismiss}
|
||||
contextId={this.props.assignment.env.courseId}
|
||||
contextType="course"
|
||||
rcsConfig={{
|
||||
contextId: this.props.assignment.env.courseId,
|
||||
contextType: 'course',
|
||||
origin: getRCSOriginFromHost(ENV.RICH_CONTENT_APP_HOST),
|
||||
headers: getRCSAuthenticationHeaders(ENV.JWT)
|
||||
}}
|
||||
open={this.state.mediaModalOpen}
|
||||
tabs={{embed: false, record: true, upload: true}}
|
||||
uploadMediaTranslations={{UploadMediaStrings, MediaCaptureStrings, SelectStrings}}
|
||||
|
|
248
yarn.lock
248
yarn.lock
|
@ -1456,10 +1456,10 @@
|
|||
"@babel/helper-module-imports" "^7.8.3"
|
||||
babel-plugin-macros "^2.8.0"
|
||||
|
||||
"@instructure/console@^7.4.4", "@instructure/console@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/console/-/console-7.5.0.tgz#af7b155a93fc1b1eb8a86332e313c81809542a15"
|
||||
integrity sha512-S+Wu6C4EZQcOLdd83If9RkYNr8M2KZaQliQCJ2HaggsgjywxHmqRKLW0rwzQMr7i20+ocn9/HNUo84lMYyDycg==
|
||||
"@instructure/console@^7.4.4", "@instructure/console@^7.5.0", "@instructure/console@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/console/-/console-7.6.0.tgz#fa4be013ace2c9f1402918debe799ebdc50715b4"
|
||||
integrity sha512-SMQsaFzbFMBdAD0ZWt9BYG75AbF1Nw1KY6tWw0uQ/H+hvKTfl2Nq4g9Y7vnam5qIAwIA5RzbiFcJjYs5TyvjVQ==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.8.3"
|
||||
"@babel/helper-module-imports" "^7.8.3"
|
||||
|
@ -1479,10 +1479,10 @@
|
|||
dependencies:
|
||||
"@babel/runtime" "^7"
|
||||
|
||||
"@instructure/debounce@^7.4.4", "@instructure/debounce@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/debounce/-/debounce-7.5.0.tgz#811a1ea0c3ce8478f9a13e8ce816c15b1f68babd"
|
||||
integrity sha512-4MJQBYsrWJSUqDzqtpMhQjuxlVY8dZ79quVLUOlJvnOtw6oPtLFdFw36/+nni5YQP6kXMpjcwO/m6QVhqG0F7A==
|
||||
"@instructure/debounce@^7.4.4", "@instructure/debounce@^7.5.0", "@instructure/debounce@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/debounce/-/debounce-7.6.0.tgz#67e2e1331735cbbbf8f3d991cbdb61ae11128323"
|
||||
integrity sha512-IWGtjI5laU5ffBc50CRoube4t5Hi4Az31TgV4dZY6d7ctPRUugbRmu0iz5y9OP54MCrXDoZ8M3Xr0qdsPuF10g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
|
@ -1648,18 +1648,18 @@
|
|||
resolved "https://registry.yarnpkg.com/@instructure/redux-service-middleware/-/redux-service-middleware-1.0.0.tgz#16e18ad18d28a24e8f6f8e5eae66c0e638dd75d5"
|
||||
integrity sha512-Bb/GzXhgWXnCI5UPjEwsVHHsTKIq1PwU8GXVH9dqkkp+0vzQzpFLsVUy2AUasxdMIHphe1bKq/+aR34/x7/PEw==
|
||||
|
||||
"@instructure/ui-a11y-content@7", "@instructure/ui-a11y-content@^7", "@instructure/ui-a11y-content@^7.3.0", "@instructure/ui-a11y-content@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-a11y-content/-/ui-a11y-content-7.5.0.tgz#50402fbbf257b7f7704d880ea59ea64ad2efff4e"
|
||||
integrity sha512-rvAi720HezEE57rnw1IjRKnMDBJw00TiV3yCoaauXQ+qDDckzcnXQn/FsG6CetXOO+BLvFq03delPC3wp/8z2A==
|
||||
"@instructure/ui-a11y-content@7", "@instructure/ui-a11y-content@^7", "@instructure/ui-a11y-content@^7.3.0", "@instructure/ui-a11y-content@^7.5.0", "@instructure/ui-a11y-content@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-a11y-content/-/ui-a11y-content-7.6.0.tgz#0a1658e5423db77b858ebf57d045fa671f999298"
|
||||
integrity sha512-mATYmvi/TpcqoP49DekidW+2mGoFjQe9Rh2iUklguSxzfrchTTd3UFPM4xZ0n3SlXyojyASpgmeZel8ajODUaw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/ui-react-utils" "^7.5.0"
|
||||
"@instructure/ui-themeable" "^7.5.0"
|
||||
"@instructure/ui-utils" "^7.5.0"
|
||||
"@instructure/uid" "^7.5.0"
|
||||
"@instructure/console" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-themeable" "^7.6.0"
|
||||
"@instructure/ui-utils" "^7.6.0"
|
||||
"@instructure/uid" "^7.6.0"
|
||||
keycode "^2"
|
||||
prop-types "^15"
|
||||
|
||||
|
@ -2143,10 +2143,10 @@
|
|||
prop-types "^15"
|
||||
react-codemirror2 "^7.1.0"
|
||||
|
||||
"@instructure/ui-color-utils@7", "@instructure/ui-color-utils@^7", "@instructure/ui-color-utils@^7.3.0", "@instructure/ui-color-utils@^7.4.4", "@instructure/ui-color-utils@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-color-utils/-/ui-color-utils-7.5.0.tgz#d90f4b90e6d458cfe608cb9c65c75cbb322ded6a"
|
||||
integrity sha512-Vq9DybIyEj3qJAEolnmpx3hhFsvJMQiYiMYvB63h359wpRIZflRNiXjMtHXzzUaAyhb/14HYI6t3X1NYTv+2KA==
|
||||
"@instructure/ui-color-utils@7", "@instructure/ui-color-utils@^7", "@instructure/ui-color-utils@^7.3.0", "@instructure/ui-color-utils@^7.4.4", "@instructure/ui-color-utils@^7.5.0", "@instructure/ui-color-utils@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-color-utils/-/ui-color-utils-7.6.0.tgz#45238c2189fcb4cbaf747feee99a3d2e9bdc2b0e"
|
||||
integrity sha512-ltywWefY9azn2W7Uc5+AhVL6au4jF82qCfL+jEmHIwC/LTbUMsvoKJ2r+6pFFgGTzy1fAcxniskpV7UP9HYt1g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
@ -2237,10 +2237,10 @@
|
|||
dependencies:
|
||||
"@babel/runtime" "^7"
|
||||
|
||||
"@instructure/ui-decorator@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-decorator/-/ui-decorator-7.5.0.tgz#a7863aebe94863beed91c52a84194fac4384b98e"
|
||||
integrity sha512-sDrwAYytnwXB+94ekj7COHQzWc65+9FKx/w8j9Cb9mpnGOsZsZK47rXCwHrqAdYc4zVeJmrWFx/YMBYvi+HYvg==
|
||||
"@instructure/ui-decorator@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-decorator/-/ui-decorator-7.6.0.tgz#16862bf4b3b99d3e3cc4714dd61ec2abe5559efe"
|
||||
integrity sha512-5GO547SaUUu1n5SuICRDLfjQV7bnvko3kqZMacqVAn1MaqePI6ivd8d3ZVZaRyB72BJZqsghfjVEBYsedw7KCA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
|
@ -2278,13 +2278,13 @@
|
|||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^6.27.0"
|
||||
|
||||
"@instructure/ui-dom-utils@^7.3.0", "@instructure/ui-dom-utils@^7.4.4", "@instructure/ui-dom-utils@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-dom-utils/-/ui-dom-utils-7.5.0.tgz#172f5832ad1f3831af9495d72c73a45d120c9dd0"
|
||||
integrity sha512-tVeoYGN5MoxVhx6U2NJVlxsYbGDdg9wNFKupBB3QHK0L5YaC9AeHzHvhb8CSagUHuhPk7fKEilcR2uspK9z3Yw==
|
||||
"@instructure/ui-dom-utils@^7.3.0", "@instructure/ui-dom-utils@^7.4.4", "@instructure/ui-dom-utils@^7.5.0", "@instructure/ui-dom-utils@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-dom-utils/-/ui-dom-utils-7.6.0.tgz#4833ff140205ca863de3ac5088fcdd91798a101d"
|
||||
integrity sha512-oyFj2QcXU6Xyez+aVcq4mnwNb+vIWvvNlkoxCMl4MrJo7le/CylHsefSnQR5ZNolPnO4/OE0fsIXAmfarzsS2A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^7.5.0"
|
||||
"@instructure/console" "^7.6.0"
|
||||
|
||||
"@instructure/ui-editable@6":
|
||||
version "6.27.0"
|
||||
|
@ -2697,17 +2697,17 @@
|
|||
moment-timezone "^0.5"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-i18n@^7.4.4", "@instructure/ui-i18n@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-i18n/-/ui-i18n-7.5.0.tgz#620d3cbe53825926e5801a91b7034430ea3a5000"
|
||||
integrity sha512-JKyoCjp/9bC1ICaqkfmyn5EsBypXl/TXo51wy0FJJ9QtfRUbgj/wt86Q4iIRltABP3yEwHVcV0fny++uA8J9PA==
|
||||
"@instructure/ui-i18n@^7.4.4", "@instructure/ui-i18n@^7.5.0", "@instructure/ui-i18n@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-i18n/-/ui-i18n-7.6.0.tgz#e1771af88dccc201b424516698719a2598ebec07"
|
||||
integrity sha512-pQ8+oUGU5IkKKEhwyIMoYhm7oRV5zonBTU0gLqxPvx/l+cekezxbAq4iVwZzsgNd9a7meV7Vc1q0aKANv4Zjvg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/ui-decorator" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/ui-prop-types" "^7.5.0"
|
||||
"@instructure/ui-react-utils" "^7.5.0"
|
||||
"@instructure/ui-utils" "^7.5.0"
|
||||
"@instructure/ui-decorator" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
"@instructure/ui-prop-types" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-utils" "^7.6.0"
|
||||
decimal.js "^10"
|
||||
moment-timezone "^0.5"
|
||||
prop-types "^15"
|
||||
|
@ -3373,16 +3373,16 @@
|
|||
"@instructure/ui-utils" "^6.27.0"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-portal@7", "@instructure/ui-portal@^7.4.4", "@instructure/ui-portal@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-portal/-/ui-portal-7.5.0.tgz#b354a631a056b85980762c6b451058e7ef8b2aa4"
|
||||
integrity sha512-ZiRz8mSy7R1/POuu1XocHQXp0yah3vfl7nMdsP6/UHiwAIiXcKhWQ8PUANgkMZNOzMTsHgkyVNDxoikGV9ZfVA==
|
||||
"@instructure/ui-portal@7", "@instructure/ui-portal@^7.4.4", "@instructure/ui-portal@^7.5.0", "@instructure/ui-portal@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-portal/-/ui-portal-7.6.0.tgz#b45f58573b0cdc525cb89dec41ac747f0568b292"
|
||||
integrity sha512-NFgn9q0DOnq6umOsakFis5/m8FsQXKdA89IDeG5PZ4Ha1BTQ7AZVZQa5MyhnwAHpMvo6RaB9P/NRRxgLCbP7Ww==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/ui-i18n" "^7.5.0"
|
||||
"@instructure/ui-prop-types" "^7.5.0"
|
||||
"@instructure/ui-react-utils" "^7.5.0"
|
||||
"@instructure/ui-utils" "^7.5.0"
|
||||
"@instructure/ui-i18n" "^7.6.0"
|
||||
"@instructure/ui-prop-types" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-utils" "^7.6.0"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-portal@^5.52.3":
|
||||
|
@ -3415,21 +3415,21 @@
|
|||
classnames "^2"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-position@^7.4.4", "@instructure/ui-position@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-position/-/ui-position-7.5.0.tgz#2913eb32bee58bbc727424477433d7b33ce58061"
|
||||
integrity sha512-2hPin+XBktVTH7sqExatdTjD87nlJRnTqr0jqGWZD0/gN9uZsQj9nAQEVLA3BRkBBNay0R6p3OOQ4uObMi+jEw==
|
||||
"@instructure/ui-position@^7.4.4", "@instructure/ui-position@^7.5.0", "@instructure/ui-position@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-position/-/ui-position-7.6.0.tgz#4b96d97b16c7319733ed0159a7876fa940813c8c"
|
||||
integrity sha512-7mmuIoMo2Pw4Ar9CaTmCOyE0maYrg6Sf+zLaPMEeRN+p1ueQfRrBnpKi9r9w6PdZdCWPq/BTOKD8p2i2NFkTMQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/debounce" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/ui-portal" "^7.5.0"
|
||||
"@instructure/ui-prop-types" "^7.5.0"
|
||||
"@instructure/ui-react-utils" "^7.5.0"
|
||||
"@instructure/ui-testable" "^7.5.0"
|
||||
"@instructure/ui-themeable" "^7.5.0"
|
||||
"@instructure/ui-utils" "^7.5.0"
|
||||
"@instructure/uid" "^7.5.0"
|
||||
"@instructure/debounce" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
"@instructure/ui-portal" "^7.6.0"
|
||||
"@instructure/ui-prop-types" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-testable" "^7.6.0"
|
||||
"@instructure/ui-themeable" "^7.6.0"
|
||||
"@instructure/ui-utils" "^7.6.0"
|
||||
"@instructure/uid" "^7.6.0"
|
||||
classnames "^2"
|
||||
prop-types "^15"
|
||||
|
||||
|
@ -3491,10 +3491,26 @@
|
|||
classnames "^2"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-prop-types@7", "@instructure/ui-prop-types@^7.4.4", "@instructure/ui-prop-types@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-prop-types/-/ui-prop-types-7.5.0.tgz#a014873e2a74966b98be28680b7eb7b88f74514b"
|
||||
integrity sha512-VoBtRgYx2pme4BAD6/ZZjNYrhfu3o8Xn9mGCTCSpRBbyh82GbbyiqgS+5rtTfwLRxoSYhkXDcm9kTFRAGaM3IQ==
|
||||
"@instructure/ui-progress@^7":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-progress/-/ui-progress-7.6.0.tgz#ce40b451a978c17424079e0191a8aa8967d1ba40"
|
||||
integrity sha512-9glVNHk642lGPzuvseTYHe6GPw8SqX9P51B2WIA2MITzIlX2pGkxospmMCRfv4n0DmrndfXKbKcNplfAp2AyLg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^7.6.0"
|
||||
"@instructure/ui-a11y-content" "^7.6.0"
|
||||
"@instructure/ui-color-utils" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-testable" "^7.6.0"
|
||||
"@instructure/ui-themeable" "^7.6.0"
|
||||
"@instructure/ui-view" "^7.6.0"
|
||||
classnames "^2"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-prop-types@7", "@instructure/ui-prop-types@^7.4.4", "@instructure/ui-prop-types@^7.5.0", "@instructure/ui-prop-types@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-prop-types/-/ui-prop-types-7.6.0.tgz#d39b373527805c0d57cde7ab609c04618de5c575"
|
||||
integrity sha512-TDs/rvnSZG5JbmxloTncVv0Mqqa2X/OkEbwurRC67B57BsPCixPkkZS8LU3cCChe8xOi3A3xQso9amcmvqLL0w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
prop-types ">= 15.7.0"
|
||||
|
@ -3599,17 +3615,17 @@
|
|||
prop-types "^15"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
"@instructure/ui-react-utils@7", "@instructure/ui-react-utils@^7", "@instructure/ui-react-utils@^7.3.0", "@instructure/ui-react-utils@^7.4.4", "@instructure/ui-react-utils@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-react-utils/-/ui-react-utils-7.5.0.tgz#505c503c1a339b9f67a1ff8b247bb253469e8489"
|
||||
integrity sha512-J4WQLNi/3G3O447V0jNl7ioIN9xwI9poxqgEhjDr7DzdnGp2YNZL4jjVD0zJfKhtBKowwbHYOgqiiSz1RiN++Q==
|
||||
"@instructure/ui-react-utils@7", "@instructure/ui-react-utils@^7", "@instructure/ui-react-utils@^7.3.0", "@instructure/ui-react-utils@^7.4.4", "@instructure/ui-react-utils@^7.5.0", "@instructure/ui-react-utils@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-react-utils/-/ui-react-utils-7.6.0.tgz#0205b1d89ef165482bc1ea5707ecbc7975ce3252"
|
||||
integrity sha512-kURXO0ivkMiIrhz2VDf3GhZ1gu2OE9lr4FGkMulpYy2IbAFBryL2YYCcE2QZ/cce7K56yRdEl/GEaTIwXcRFEA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@emotion/is-prop-valid" "^0.8.3"
|
||||
"@instructure/console" "^7.5.0"
|
||||
"@instructure/ui-decorator" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/ui-utils" "^7.5.0"
|
||||
"@instructure/console" "^7.6.0"
|
||||
"@instructure/ui-decorator" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
"@instructure/ui-utils" "^7.6.0"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-responsive@6":
|
||||
|
@ -3789,10 +3805,10 @@
|
|||
"@babel/runtime" "^7.9.2"
|
||||
glamor "^2.20.40"
|
||||
|
||||
"@instructure/ui-stylesheet@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-stylesheet/-/ui-stylesheet-7.5.0.tgz#a1b8d8333b669061abca0fc33a13b2fcf5db9512"
|
||||
integrity sha512-ReBbFwLomCFPpR87KjD2d3U42FOEASu9Ukkv3crEEVkxeQnp6Cep92EalzVqs9qkVBb8l3E/MfC13sy0K0fCLQ==
|
||||
"@instructure/ui-stylesheet@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-stylesheet/-/ui-stylesheet-7.6.0.tgz#21ead006eaaf340155b1625bf03e03119edff3b3"
|
||||
integrity sha512-cW7qWER6Y3Am6lttLOa0TW2Jn2HoJ/0ZumoJ54CtS8r+Mj5EPWd7039UuJkxm9i1nJljyYumdtYhWkokNxuc2Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
glamor "^2.20.40"
|
||||
|
@ -4026,13 +4042,13 @@
|
|||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/ui-decorator" "^6.27.0"
|
||||
|
||||
"@instructure/ui-testable@^7.3.0", "@instructure/ui-testable@^7.4.4", "@instructure/ui-testable@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-testable/-/ui-testable-7.5.0.tgz#45b6d9a7db3f25fdf837cc43d0163071d0ea5787"
|
||||
integrity sha512-794VksT9+/dHKgbIri+BMx5/8bmYGqHHYainwJmHRDH7/CjdfmzfzW01G6gBypFU1SywkYZNOQuYgOLhz5PVqQ==
|
||||
"@instructure/ui-testable@^7.3.0", "@instructure/ui-testable@^7.4.4", "@instructure/ui-testable@^7.5.0", "@instructure/ui-testable@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-testable/-/ui-testable-7.6.0.tgz#deaa3421e7b0d901707bc8c6085a01a198e2dfa8"
|
||||
integrity sha512-fN2bK7K60OA4oAopajU6YqXo6RGAHIZjcoTAKhOJt1ThPLaYHkgvByPx7miV6/zYUfHO2mYnVx4VTPix/Z94xA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/ui-decorator" "^7.5.0"
|
||||
"@instructure/ui-decorator" "^7.6.0"
|
||||
|
||||
"@instructure/ui-text-area@6", "@instructure/ui-text-area@^6.27.0":
|
||||
version "6.27.0"
|
||||
|
@ -4158,20 +4174,20 @@
|
|||
"@instructure/uid" "^6.27.0"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-themeable@7", "@instructure/ui-themeable@^7", "@instructure/ui-themeable@^7.3.0", "@instructure/ui-themeable@^7.4.4", "@instructure/ui-themeable@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-themeable/-/ui-themeable-7.5.0.tgz#7afaab26b7870adc3ce38f73e8616873089a43e2"
|
||||
integrity sha512-0yXjDcavzanCIoMUC88zZv3vE/pCXzxIKGUn1I1wTHcHq3B+SXsc4Eu8kr7DwwMZToOStXJo0mFpY+/MYZGbrw==
|
||||
"@instructure/ui-themeable@7", "@instructure/ui-themeable@^7", "@instructure/ui-themeable@^7.3.0", "@instructure/ui-themeable@^7.4.4", "@instructure/ui-themeable@^7.5.0", "@instructure/ui-themeable@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-themeable/-/ui-themeable-7.6.0.tgz#a72e73eb258fa5c452e7afa763c1803bef65c71e"
|
||||
integrity sha512-hmHC/C04iw9dAtm+URlceJ9ff5SwpblxMB3Cp+P084kQ+M34TpWoAkWzIx7UEZR41dWMQ+jRhNzDm9ecQTkz6Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^7.5.0"
|
||||
"@instructure/ui-color-utils" "^7.5.0"
|
||||
"@instructure/ui-decorator" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/ui-react-utils" "^7.5.0"
|
||||
"@instructure/ui-stylesheet" "^7.5.0"
|
||||
"@instructure/ui-utils" "^7.5.0"
|
||||
"@instructure/uid" "^7.5.0"
|
||||
"@instructure/console" "^7.6.0"
|
||||
"@instructure/ui-color-utils" "^7.6.0"
|
||||
"@instructure/ui-decorator" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-stylesheet" "^7.6.0"
|
||||
"@instructure/ui-utils" "^7.6.0"
|
||||
"@instructure/uid" "^7.6.0"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-themeable@^5", "@instructure/ui-themeable@^5.52.3":
|
||||
|
@ -4434,14 +4450,14 @@
|
|||
escape-html "^1"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-utils@7", "@instructure/ui-utils@^7", "@instructure/ui-utils@^7.4.4", "@instructure/ui-utils@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-utils/-/ui-utils-7.5.0.tgz#55cd2315651c2f9415b831c712b712f4166cdc7c"
|
||||
integrity sha512-pl+sREpuN6P37Gv6M4igbZ1kQVwaCmBZKCBkl5my8Pvkp0st0gyyMGFXjaBJpBj2lOyBdYYbmbBWSYGspjOpRw==
|
||||
"@instructure/ui-utils@7", "@instructure/ui-utils@^7", "@instructure/ui-utils@^7.4.4", "@instructure/ui-utils@^7.5.0", "@instructure/ui-utils@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-utils/-/ui-utils-7.6.0.tgz#d745df92d76f8e2a25a5d11016715809d47c903a"
|
||||
integrity sha512-XLpmTwCXdmR2qur5nq+pj7A7mU62Aqm+KBsstQ3RPt/oHoiNayjjY+dbXKtcH3c47gpCof9uX2xlsfnkOzaVrg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/console" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
bowser "^1.9.4"
|
||||
fast-deep-equal "^2"
|
||||
json-stable-stringify "^1.0.1"
|
||||
|
@ -4496,20 +4512,20 @@
|
|||
classnames "^2"
|
||||
prop-types "^15"
|
||||
|
||||
"@instructure/ui-view@7", "@instructure/ui-view@^7", "@instructure/ui-view@^7.4.4", "@instructure/ui-view@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-view/-/ui-view-7.5.0.tgz#a6cac70fea148023ff41283e3fc980c26ed3886a"
|
||||
integrity sha512-P1DyzSdMIU9bi8gj4sUqk+KTrCdhigu5ZalbAnzZImVNzeWJ0GW+1scDFxCYhWMzNd9ptMCewQ0GSAEI440vOQ==
|
||||
"@instructure/ui-view@7", "@instructure/ui-view@^7", "@instructure/ui-view@^7.4.4", "@instructure/ui-view@^7.5.0", "@instructure/ui-view@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/ui-view/-/ui-view-7.6.0.tgz#5b0de5e0dfea00bcfad973bce806d2441e3962d6"
|
||||
integrity sha512-7fJtCYfB+CeRGidx/Fm48fOr1brVERSuG4EAAfplVA78Xc453Zn7SSiclF2xglE3OHCXSlIY62O074NXfvJfKw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@instructure/console" "^7.5.0"
|
||||
"@instructure/ui-color-utils" "^7.5.0"
|
||||
"@instructure/ui-dom-utils" "^7.5.0"
|
||||
"@instructure/ui-i18n" "^7.5.0"
|
||||
"@instructure/ui-position" "^7.5.0"
|
||||
"@instructure/ui-prop-types" "^7.5.0"
|
||||
"@instructure/ui-react-utils" "^7.5.0"
|
||||
"@instructure/ui-themeable" "^7.5.0"
|
||||
"@instructure/console" "^7.6.0"
|
||||
"@instructure/ui-color-utils" "^7.6.0"
|
||||
"@instructure/ui-dom-utils" "^7.6.0"
|
||||
"@instructure/ui-i18n" "^7.6.0"
|
||||
"@instructure/ui-position" "^7.6.0"
|
||||
"@instructure/ui-prop-types" "^7.6.0"
|
||||
"@instructure/ui-react-utils" "^7.6.0"
|
||||
"@instructure/ui-themeable" "^7.6.0"
|
||||
classnames "^2"
|
||||
prop-types "^15"
|
||||
|
||||
|
@ -4520,10 +4536,10 @@
|
|||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
"@instructure/uid@7", "@instructure/uid@^7.3.0", "@instructure/uid@^7.4.4", "@instructure/uid@^7.5.0":
|
||||
version "7.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/uid/-/uid-7.5.0.tgz#2cdffb4567d5c9ee263f1864cb5bff6f087f70ce"
|
||||
integrity sha512-b7ynsDpmDAC3nvFTarf0N3LHe1IJMwQ8cJQBH0euKtwkfboOv617pAy+npZRuTNj+XZwpFGTKltujQh91RUQcQ==
|
||||
"@instructure/uid@7", "@instructure/uid@^7.3.0", "@instructure/uid@^7.4.4", "@instructure/uid@^7.5.0", "@instructure/uid@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@instructure/uid/-/uid-7.6.0.tgz#45e316b01ef3bd692fed5032d1ac524a587cd892"
|
||||
integrity sha512-vEOWlKqLo+AXVfVLbETeldCW5moxXicIXDs1jSxcEBaIP/7DnDwQe3uZJjyDAkNMcKz3lnpYOawS7eu8nw1UcQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
|
|
Loading…
Reference in New Issue