about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch')
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js60
-rw-r--r--app/javascript/flavours/glitch/styles/components/composer.scss19
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss21
-rw-r--r--app/javascript/flavours/glitch/styles/components/modal.scss36
4 files changed, 118 insertions, 18 deletions
diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
index de87ba83f..e5a0d029a 100644
--- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
@@ -10,6 +10,11 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import IconButton from 'flavours/glitch/components/icon_button';
 import Button from 'flavours/glitch/components/button';
 import Video from 'flavours/glitch/features/video';
+import { TesseractWorker } from 'tesseract.js';
+import Textarea from 'react-textarea-autosize';
+import UploadProgress from 'flavours/glitch/features/compose/components/upload_progress';
+import CharacterCounter from 'flavours/glitch/features/compose/components/character_counter';
+import { length } from 'stringz';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
@@ -29,6 +34,12 @@ const mapDispatchToProps = (dispatch, { id }) => ({
 
 });
 
+const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******')
+  .replace(/\n/g, ' ')
+  .replace(/\*\*\*\*\*\*/g, '\n\n');
+
+const assetHost = process.env.CDN_HOST || '';
+
 export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
 class FocalPointModal extends ImmutablePureComponent {
@@ -47,6 +58,7 @@ class FocalPointModal extends ImmutablePureComponent {
     dragging: false,
     description: '',
     dirty: false,
+    progress: 0,
   };
 
   componentWillMount () {
@@ -133,9 +145,27 @@ class FocalPointModal extends ImmutablePureComponent {
     this.node = c;
   }
 
+  handleTextDetection = () => {
+    const { media } = this.props;
+
+    const worker = new TesseractWorker({
+      workerPath: `${assetHost}/packs/ocr/worker.min.js`,
+      corePath: `${assetHost}/packs/ocr/tesseract-core.wasm.js`,
+      langPath: `${assetHost}/ocr/lang-data`,
+    });
+
+    this.setState({ detecting: true });
+
+    worker.recognize(media.get('url'))
+      .progress(({ progress }) => this.setState({ progress }))
+      .finally(() => worker.terminate())
+      .then(({ text }) => this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false }))
+      .catch(() => this.setState({ detecting: false }));
+  }
+
   render () {
     const { media, intl, onClose } = this.props;
-    const { x, y, dragging, description, dirty } = this.state;
+    const { x, y, dragging, description, dirty, detecting, progress } = this.state;
 
     const width  = media.getIn(['meta', 'original', 'width']) || null;
     const height = media.getIn(['meta', 'original', 'height']) || null;
@@ -158,15 +188,27 @@ class FocalPointModal extends ImmutablePureComponent {
 
             <label className='setting-text-label' htmlFor='upload-modal__description'><FormattedMessage id='upload_form.description' defaultMessage='Describe for the visually impaired' /></label>
 
-            <textarea
-              id='upload-modal__description'
-              className='setting-text light'
-              value={description}
-              onChange={this.handleChange}
-              autoFocus
-            />
+            <div className='setting-text__wrapper'>
+              <Textarea
+                id='upload-modal__description'
+                className='setting-text light'
+                value={detecting ? '…' : description}
+                onChange={this.handleChange}
+                disabled={detecting}
+                autoFocus
+              />
+
+              <div className='setting-text__modifiers'>
+                <UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={<FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />} />
+              </div>
+            </div>
+
+            <div className='setting-text__toolbar'>
+              <button disabled={detecting || media.get('type') !== 'image'} className='link-button' onClick={this.handleTextDetection}><FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' /></button>
+              <CharacterCounter max={420} text={detecting ? '' : description} />
+            </div>
 
-            <Button disabled={!dirty} text={intl.formatMessage(messages.apply)} onClick={this.handleSubmit} />
+            <Button disabled={!dirty || detecting || length(description) > 420} text={intl.formatMessage(messages.apply)} onClick={this.handleSubmit} />
           </div>
 
           <div className='report-modal__statuses'>
diff --git a/app/javascript/flavours/glitch/styles/components/composer.scss b/app/javascript/flavours/glitch/styles/components/composer.scss
index 9152988ea..389f14773 100644
--- a/app/javascript/flavours/glitch/styles/components/composer.scss
+++ b/app/javascript/flavours/glitch/styles/components/composer.scss
@@ -2,6 +2,18 @@
   padding: 10px;
 }
 
+.character-counter {
+  cursor: default;
+  font-family: $font-sans-serif, sans-serif;
+  font-size: 14px;
+  font-weight: 600;
+  color: $lighter-text-color;
+
+  &.character-counter--over {
+    color: $warning-red;
+  }
+}
+
 .no-reduce-motion .composer--spoiler {
   transition: height 0.4s ease, opacity 0.4s ease;
 }
@@ -589,13 +601,6 @@
   justify-content: flex-end;
   flex: 0 0 auto;
 
-  & > .character-counter {
-    display: inline-block;
-    margin: 0 16px 0 8px;
-    font-size: 16px;
-    line-height: 36px;
-  }
-
   & > .primary {
     display: inline-block;
     margin: 0;
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index 6942170f2..f453a046e 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -3,6 +3,27 @@
   -ms-overflow-style: -ms-autohiding-scrollbar;
 }
 
+.link-button {
+  display: block;
+  font-size: 15px;
+  line-height: 20px;
+  color: $ui-highlight-color;
+  border: 0;
+  background: transparent;
+  padding: 0;
+  cursor: pointer;
+
+  &:hover,
+  &:active {
+    text-decoration: underline;
+  }
+
+  &:disabled {
+    color: $ui-primary-color;
+    cursor: default;
+  }
+}
+
 .button {
   background-color: darken($ui-highlight-color, 3%);
   border: 10px none;
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index df4a22329..87d9d6cd7 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -565,16 +565,48 @@
     padding: 10px;
     font-family: inherit;
     font-size: 14px;
-    resize: vertical;
+    resize: none;
     border: 0;
     outline: 0;
     border-radius: 4px;
     border: 1px solid $ui-secondary-color;
-    margin-bottom: 20px;
+    min-height: 100px;
+    max-height: 50vh;
+    margin-bottom: 10px;
 
     &:focus {
       border: 1px solid darken($ui-secondary-color, 8%);
     }
+
+    &__wrapper {
+      background: $white;
+      border: 1px solid $ui-secondary-color;
+      margin-bottom: 10px;
+      border-radius: 4px;
+
+      .setting-text {
+        border: 0;
+        margin-bottom: 0;
+        border-radius: 0;
+
+        &:focus {
+          border: 0;
+        }
+      }
+
+      &__modifiers {
+        color: $inverted-text-color;
+        font-family: inherit;
+        font-size: 14px;
+        background: $white;
+      }
+    }
+
+    &__toolbar {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 20px;
+    }
   }
 
   .setting-text-label {