diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2019-08-15 15:13:26 +0200 |
---|---|---|
committer | Thibaut Girka <thib@sitedethib.com> | 2019-08-19 21:56:25 +0200 |
commit | 41c7fec79632d4b698c4c39e63c7fe8cff395838 (patch) | |
tree | d1bf5de0ca6a3789528f5d0699d4e82704c76dd3 /app | |
parent | 066034c62e87412c1e351fd89d98f3ad706d00a3 (diff) |
[Glitch] Add OCR tool to media editing modal
Port 28636f43e4b0c04befa243b847c38e81c90f1289 to glitch-soc Signed-off-by: Thibaut Girka <thib@sitedethib.com>
Diffstat (limited to 'app')
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 { |