Переглянути джерело

[feature] - Add new ‘showRecordingLength’ option for the Webcam plugin (#1947)

* [feature] - Add new ‘showRecordingLength’ prop for the Webcam plugin. When this is true, it counts the duration of a recording and displays it to the user. Add a test for generating the correct duration of the recording.

* Update packages/@uppy/webcam/src/style.scss

Co-Authored-By: Artur Paikin <artur@arturpaikin.com>

* Added i18n for the recording length counter and update Readme. Fix Webcam styles where some styles were not in alphabetical order.

* Update packages/@uppy/webcam/src/style.scss

Co-Authored-By: Artur Paikin <artur@arturpaikin.com>

Added i18n for the recording length counter and update Readme. Fix Webcam styles where some styles were not in alphabetical order.
Dominic Eden 5 роки тому
батько
коміт
e7fbaa940d

+ 1 - 0
packages/@uppy/locales/src/de_DE.js

@@ -73,6 +73,7 @@ de_DE.strings = {
     '1': '%{smart_count} Dateien verarbeiten',
     '1': '%{smart_count} Dateien verarbeiten',
     '2': '%{smart_count} Dateien verarbeiten'
     '2': '%{smart_count} Dateien verarbeiten'
   },
   },
+  recordingLength: 'Aufnahmedauer %{recording_length}',
   removeFile: 'Datei entfernen',
   removeFile: 'Datei entfernen',
   resetFilter: 'Filter zurücksetzen',
   resetFilter: 'Filter zurücksetzen',
   resume: 'Fortsetzen',
   resume: 'Fortsetzen',

+ 1 - 0
packages/@uppy/locales/src/en_US.js

@@ -75,6 +75,7 @@ en_US.strings = {
     '1': 'Processing %{smart_count} files',
     '1': 'Processing %{smart_count} files',
     '2': 'Processing %{smart_count} files'
     '2': 'Processing %{smart_count} files'
   },
   },
+  recordingLength: 'Recording length %{recording_length}',
   removeFile: 'Remove file',
   removeFile: 'Remove file',
   resetFilter: 'Reset filter',
   resetFilter: 'Reset filter',
   resume: 'Resume',
   resume: 'Resume',

+ 1 - 0
packages/@uppy/locales/src/es_ES.js

@@ -72,6 +72,7 @@ es_ES.strings = {
     '1': 'Procesando %{smart_count} archivos',
     '1': 'Procesando %{smart_count} archivos',
     '2': 'Procesando %{smart_count} archivos'
     '2': 'Procesando %{smart_count} archivos'
   },
   },
+  recordingLength: 'Duración de grabación %{recording_length}',
   removeFile: 'Eliminar archivo',
   removeFile: 'Eliminar archivo',
   resetFilter: 'Limpiar filtro',
   resetFilter: 'Limpiar filtro',
   resume: 'Reanudar',
   resume: 'Reanudar',

+ 1 - 0
packages/@uppy/locales/src/fr_FR.js

@@ -72,6 +72,7 @@ fr_FR.strings = {
     '1': 'Traitement de %{smart_count} fichiers',
     '1': 'Traitement de %{smart_count} fichiers',
     '2': 'Traitement de %{smart_count} fichiers'
     '2': 'Traitement de %{smart_count} fichiers'
   },
   },
+  recordingLength: 'Durée d\'enregistrement %{recording_length}',
   removeFile: 'Effacer le fichier',
   removeFile: 'Effacer le fichier',
   resetFilter: 'Réinitialiser filtre',
   resetFilter: 'Réinitialiser filtre',
   resume: 'Continuer',
   resume: 'Continuer',

+ 1 - 0
packages/@uppy/locales/src/nl_NL.js

@@ -72,6 +72,7 @@ nl_NL.strings = {
     '1': 'Bezig met %{smart_count} bestanden te verwerken',
     '1': 'Bezig met %{smart_count} bestanden te verwerken',
     '2': 'Bezig met %{smart_count} bestanden te verwerken'
     '2': 'Bezig met %{smart_count} bestanden te verwerken'
   },
   },
+  recordingLength: 'Opnameduur %{recording_length}',
   removeFile: 'Bestand verwijderen',
   removeFile: 'Bestand verwijderen',
   resetFilter: 'Filter resetten',
   resetFilter: 'Filter resetten',
   resume: 'Hervatten',
   resume: 'Hervatten',

+ 2 - 1
packages/@uppy/webcam/README.md

@@ -18,7 +18,8 @@ const Webcam = require('@uppy/webcam')
 const uppy = Uppy()
 const uppy = Uppy()
 uppy.use(Webcam, {
 uppy.use(Webcam, {
   mirror: true,
   mirror: true,
-  facingMode: 'user'
+  facingMode: 'user',
+  showRecordingLength: true
 })
 })
 ```
 ```
 
 

+ 4 - 0
packages/@uppy/webcam/src/CameraScreen.js

@@ -1,6 +1,7 @@
 const { h, Component } = require('preact')
 const { h, Component } = require('preact')
 const SnapshotButton = require('./SnapshotButton')
 const SnapshotButton = require('./SnapshotButton')
 const RecordButton = require('./RecordButton')
 const RecordButton = require('./RecordButton')
+const RecordingLength = require('./RecordingLength')
 
 
 function isModeAvailable (modes, mode) {
 function isModeAvailable (modes, mode) {
   return modes.indexOf(mode) !== -1
   return modes.indexOf(mode) !== -1
@@ -22,6 +23,7 @@ class CameraScreen extends Component {
       isModeAvailable(this.props.modes, 'video-audio')
       isModeAvailable(this.props.modes, 'video-audio')
     )
     )
     const shouldShowSnapshotButton = isModeAvailable(this.props.modes, 'picture')
     const shouldShowSnapshotButton = isModeAvailable(this.props.modes, 'picture')
+    const shouldShowRecordingLength = this.props.supportsRecording && this.props.showRecordingLength
 
 
     return (
     return (
       <div class="uppy uppy-Webcam-container">
       <div class="uppy uppy-Webcam-container">
@@ -29,6 +31,8 @@ class CameraScreen extends Component {
           <video class={`uppy-Webcam-video  ${this.props.mirror ? 'uppy-Webcam-video--mirrored' : ''}`} autoplay muted playsinline srcObject={this.props.src || ''} />
           <video class={`uppy-Webcam-video  ${this.props.mirror ? 'uppy-Webcam-video--mirrored' : ''}`} autoplay muted playsinline srcObject={this.props.src || ''} />
         </div>
         </div>
         <div class="uppy-Webcam-buttonContainer">
         <div class="uppy-Webcam-buttonContainer">
+          {shouldShowRecordingLength ? RecordingLength(this.props) : null}
+          {' '}
           {shouldShowSnapshotButton ? SnapshotButton(this.props) : null}
           {shouldShowSnapshotButton ? SnapshotButton(this.props) : null}
           {' '}
           {' '}
           {shouldShowRecordButton ? RecordButton(this.props) : null}
           {shouldShowRecordButton ? RecordButton(this.props) : null}

+ 12 - 0
packages/@uppy/webcam/src/RecordingLength.js

@@ -0,0 +1,12 @@
+const { h } = require('preact')
+const formatSeconds = require('./formatSeconds')
+
+module.exports = function RecordingLength ({ recordingLengthSeconds, i18n }) {
+  const formattedRecordingLengthSeconds = formatSeconds(recordingLengthSeconds)
+
+  return (
+    <div class="uppy-Webcam-recordingLength" aria-label={i18n('recordingLength', { recording_length: formattedRecordingLengthSeconds })}>
+      {formattedRecordingLengthSeconds}
+    </div>
+  )
+}

+ 12 - 0
packages/@uppy/webcam/src/formatSeconds.js

@@ -0,0 +1,12 @@
+/**
+ * Takes an Integer value of seconds (e.g. 83) and converts it into a human-readable formatted string (e.g. '1:23').
+ *
+ * @param {Integer} seconds
+ * @returns {string} the formatted seconds (e.g. '1:23' for 1 minute and 23 seconds)
+ *
+ */
+module.exports = function formatSeconds (seconds) {
+  return `${Math.floor(
+        seconds / 60
+      )}:${String(seconds % 60).padStart(2, 0)}`
+}

+ 11 - 0
packages/@uppy/webcam/src/formatSeconds.test.js

@@ -0,0 +1,11 @@
+const formatSeconds = require('./formatSeconds')
+
+describe('formatSeconds', () => {
+  it('should return a value of \'0:43\' when an argument of 43 seconds is supplied', () => {
+    expect(formatSeconds(43)).toEqual('0:43')
+  })
+
+  it('should return a value of \'1:43\' when an argument of 103 seconds is supplied', () => {
+    expect(formatSeconds(103)).toEqual('1:43')
+  })
+})

+ 21 - 3
packages/@uppy/webcam/src/index.js

@@ -54,7 +54,8 @@ module.exports = class Webcam extends Plugin {
         startRecording: 'Begin video recording',
         startRecording: 'Begin video recording',
         stopRecording: 'Stop video recording',
         stopRecording: 'Stop video recording',
         allowAccessTitle: 'Please allow access to your camera',
         allowAccessTitle: 'Please allow access to your camera',
-        allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.'
+        allowAccessDescription: 'In order to take pictures or record video with your camera, please allow camera access for this site.',
+        recordingLength: 'Recording length %{recording_length}'
       }
       }
     }
     }
 
 
@@ -70,7 +71,8 @@ module.exports = class Webcam extends Plugin {
       ],
       ],
       mirror: true,
       mirror: true,
       facingMode: 'user',
       facingMode: 'user',
-      preferredVideoMimeType: null
+      preferredVideoMimeType: null,
+      showRecordingLength: false
     }
     }
 
 
     this.opts = { ...defaultOptions, ...opts }
     this.opts = { ...defaultOptions, ...opts }
@@ -169,6 +171,14 @@ module.exports = class Webcam extends Plugin {
     })
     })
     this.recorder.start()
     this.recorder.start()
 
 
+    if (this.opts.showRecordingLength) {
+      // Start the recordingLengthTimer if we are showing the recording length.
+      this.recordingLengthTimer = setInterval(() => {
+        const currentRecordingLength = this.getPluginState().recordingLengthSeconds
+        this.setPluginState({ recordingLengthSeconds: currentRecordingLength + 1 })
+      }, 1000)
+    }
+
     this.setPluginState({
     this.setPluginState({
       isRecording: true
       isRecording: true
     })
     })
@@ -180,6 +190,12 @@ module.exports = class Webcam extends Plugin {
         resolve()
         resolve()
       })
       })
       this.recorder.stop()
       this.recorder.stop()
+
+      if (this.opts.showRecordingLength) {
+        // Stop the recordingLengthTimer if we are showing the recording length.
+        clearInterval(this.recordingLengthTimer)
+        this.setPluginState({ recordingLengthSeconds: 0 })
+      }
     })
     })
 
 
     return stopped.then(() => {
     return stopped.then(() => {
@@ -368,6 +384,7 @@ module.exports = class Webcam extends Plugin {
         onStop={this.stop}
         onStop={this.stop}
         i18n={this.i18n}
         i18n={this.i18n}
         modes={this.opts.modes}
         modes={this.opts.modes}
+        showRecordingLength={this.opts.showRecordingLength}
         supportsRecording={supportsMediaRecorder()}
         supportsRecording={supportsMediaRecorder()}
         recording={webcamState.isRecording}
         recording={webcamState.isRecording}
         mirror={this.opts.mirror}
         mirror={this.opts.mirror}
@@ -378,7 +395,8 @@ module.exports = class Webcam extends Plugin {
 
 
   install () {
   install () {
     this.setPluginState({
     this.setPluginState({
-      cameraReady: false
+      cameraReady: false,
+      recordingLengthSeconds: 0
     })
     })
 
 
     const target = this.opts.target
     const target = this.opts.target

+ 19 - 12
packages/@uppy/webcam/src/style.scss

@@ -105,18 +105,6 @@
   line-height: 1.3;
   line-height: 1.3;
 }
 }
 
 
-.uppy-Webcam-title {
-  font-size: 22px;
-  line-height: 1.35;
-  font-weight: 400;
-  margin: 0;
-  margin-bottom: 5px;
-  padding: 0 15px;
-  max-width: 500px;
-  text-align: center;
-  color: $gray-800;
-}
-
 .uppy-Webcam-permissons p {
 .uppy-Webcam-permissons p {
   text-align: center;
   text-align: center;
   line-height: 1.45;
   line-height: 1.45;
@@ -130,3 +118,22 @@
   color: $gray-400;
   color: $gray-400;
   margin-bottom: 30px;
   margin-bottom: 30px;
 }
 }
+
+.uppy-Webcam-recordingLength {
+  position: absolute;
+  right: 20px;
+  color: $gray-600;
+  font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+.uppy-Webcam-title {
+  font-size: 22px;
+  line-height: 1.35;
+  font-weight: 400;
+  margin: 0;
+  margin-bottom: 5px;
+  padding: 0 15px;
+  max-width: 500px;
+  text-align: center;
+  color: $gray-800;
+}

+ 8 - 0
website/src/docs/webcam.md

@@ -65,6 +65,7 @@ uppy.use(Webcam, {
   ],
   ],
   mirror: true,
   mirror: true,
   facingMode: 'user',
   facingMode: 'user',
+  showRecordingLength: false,
   locale: {}
   locale: {}
 })
 })
 ```
 ```
@@ -113,6 +114,10 @@ Devices sometimes have multiple cameras, front and back, for example. There is a
 - `left`: The video source is facing toward the user but to their left, such as a camera aimed toward the user but over their left shoulder.
 - `left`: The video source is facing toward the user but to their left, such as a camera aimed toward the user but over their left shoulder.
 - `right`: The video source is facing toward the user but to their right, such as a camera aimed toward the user but over their right shoulder.
 - `right`: The video source is facing toward the user but to their right, such as a camera aimed toward the user but over their right shoulder.
 
 
+### `showRecordingLength: false`
+
+Configures whether or not to show the length of the recording while the recording is in progress. The default is `false`.
+
 ### `preferredVideoMimeType: null`
 ### `preferredVideoMimeType: null`
 
 
 Set the preferred mime type for video recordings, for example `'video/webm'`. If the browser supports the given mime type, the video will be recorded in this format. If the browser does not support it, it will use the browser default.
 Set the preferred mime type for video recordings, for example `'video/webm'`. If the browser supports the given mime type, the video will be recorded in this format. If the browser does not support it, it will use the browser default.
@@ -136,6 +141,9 @@ strings: {
   // Used as the label for the button that stops a video recording.
   // Used as the label for the button that stops a video recording.
   // This is not visibly rendered but is picked up by screen readers.
   // This is not visibly rendered but is picked up by screen readers.
   stopRecording: 'Stop video recording',
   stopRecording: 'Stop video recording',
+  // Used as the label for the recording length counter. See the showRecordingLength option.
+  // This is not visibly rendered but is picked up by screen readers.
+  recordingLength: 'Recording length %{recording_length}',
   // Title on the “allow access” screen
   // Title on the “allow access” screen
   allowAccessTitle: 'Please allow access to your camera',
   allowAccessTitle: 'Please allow access to your camera',
   // Description on the “allow access” screen
   // Description on the “allow access” screen