import { Button , Callout , Classes , Intent , Overlay } from '@blueprintjs/core' ; import _ from 'lodash' ; import { Component } from 'react' ; import Center from 'components/Center' ; import Imgur from 'libs/Imgur' ; import styled , { theme } from 'styled' ; /**
 * Wrapper component.
 */ const Wrapper = styled . div `
  height: 100vh;
  pointer-events: none;
  width: 100vw;
` ; /**
 * Tooltip component.
 */ const Tooltip = styled ( Callout ) `
  position: relative;

  &.${ Classes . CALLOUT }, .${ Classes . DARK } &.${ Classes . CALLOUT } {
    background-color: red;
    padding: 20px;
    width: 360px;

    &.${ Classes . INTENT_PRIMARY } {
      background-color: ${ theme ( 'dropOverlay.background' ) };
    }

    & h4.${ Classes . HEADING } {
      font-size: 1.5rem;
      margin-left: 24px;
    }

    .${ Classes . DARK } & h4.${ Classes . HEADING } {
      margin-left: 44px;
    }

    & svg {
      height: 36px;
      width: 36px;

      &.${ Classes . ICON }:first-child {
        left: 16px;
        top: 16px;
      }
    }
  }

  .${ Classes . OVERLAY }-appear &,
  .${ Classes . OVERLAY }-enter & {
    transform: translateY(-50vh) rotate(-10deg);
  }

  .${ Classes . OVERLAY }-appear-active &,
  .${ Classes . OVERLAY }-enter-active & {
    transform: translateY(0) rotate(0deg);
    transition-property: transform;
    transition-duration: 0.3s;
    transition-timing-function: cubic-bezier(0.54, 1.12, 0.38, 1.11);
    transition-delay: 0;
  }

  .${ Classes . OVERLAY }-exit & {
    transform: translateY(0) rotate(0deg);
  }

  .${ Classes . OVERLAY }-exit-active & {
    transform: translateY(150vh) rotate(-20deg);
    transition-property: transform;
    transition-duration: 0.5s;
    transition-timing-function: cubic-bezier(0.4, 1, 0.75, 0.9);
    transition-delay: 0;
  }
` ; /**
 * Details component.
 */ const Details = styled . ul `
  font-size: 0.8rem;
  margin-top: 24px;
  padding-left: 7px;

  .${ Classes . DARK } & {
    padding-left: 27px;
  }

  & > li,
  .${ Classes . DARK } & > li {
    margin: 4px 0;
  }
` ; /**
 * CloseButton component.
 */ const CloseButton = styled ( Button ) `
  pointer-events: auto;
  position: absolute;
  right: 6px;
  top: 6px;

  .${ Classes . CALLOUT } &,
  .${ Classes . DARK } .${ Classes . CALLOUT } & {
    & svg {
      height: 18px;
      width: 18px;
    }
  }
` ; /**
 * React State.
 */ const initialState = { isDragging : false , isDraggingOver : false } ; type State = Readonly < typeof initialState > ; /**
 * DropOverlay Component.
 */ export default class DropOverlay extends Component < Props , State > { param event return private static isValidDataTransfer ( event ) { return DropOverlay . isFilesDataTransfer ( event ) ; } param event return private static isFilesDataTransfer ( event ) { return ! _ . isNil ( event . dataTransfer ) && _ . includes ( event . dataTransfer . types , 'Files' ) ; } param event return private static isImageDataTransfer ( event ) { if ( ! _ . isNil ( event . dataTransfer ) && ! _ . isEmpty ( event . dataTransfer . files ) && DropOverlay . isFilesDataTransfer ( event ) ) { const { type } = _ . head ( event . dataTransfer . files ) as File ; if ( type === 'image/png' || type === 'image/jpeg' || type === 'image/gif' ) { return true ; } } return false ; } public state : State = initialState ; /**
     * Lifecycle: componentDidMount.
     */ public componentDidMount ( ) { document . addEventListener ( 'dragenter' , this . onDragEnter ) ; document . addEventListener ( 'dragover' , this . onDragOver ) ; document . addEventListener ( 'dragleave' , this . onDragLeave ) ; document . addEventListener ( 'drop' , this . onDrop ) ; } param nextProps param nextState return public shouldComponentUpdate ( _nextProps , nextState ) { const { isDragging , isDraggingOver } = this . state ; const { isDragging : nextIsDragging , isDraggingOver : nextIsDraggingOver } = nextState ; return isDragging !== nextIsDragging || isDraggingOver !== nextIsDraggingOver ; } /**
     * Lifecycle: componentWillUnmount.
     */ public componentWillUnmount ( ) { document . removeEventListener ( 'dragenter' , this . onDragEnter ) ; document . removeEventListener ( 'dragover' , this . onDragOver ) ; document . removeEventListener ( 'dragleave' , this . onDragLeave ) ; document . removeEventListener ( 'drop' , this . onDrop ) ; } return public render ( ) { const { isDragging , isDraggingOver } = this . state ; return ( < Overlay isOpen = { isDragging && isDraggingOver } lazy = { false } > < Wrapper > < Center > < Tooltip title = "Upload an image…" intent = { Intent . PRIMARY } icon = "cloud-upload" > < CloseButton minimal icon = "cross" small onClick = { this . closeOverlay } / > < Details > < li > Images are uploaded to Imgur . < /li> < li > Uploads are anonymous . < /li> < li > When done , the link will be pasted in the chat input ready to be shared . < /li> < /Details> < /Tooltip> < /Center> < /Wrapper> < /Overlay>); } private closeOverlay = ( ) => { this . setState ( ( ) => ( { isDragging : false , isDraggingOver : false } ) ) ; } ; private onDragEnter = ( event ) => { event . preventDefault ( ) ; event . stopPropagation ( ) ; const { isDraggingOver , isDragging } = this . state ; if ( isDraggingOver ) { return ; } if ( ! isDragging ) { this . setState ( ( ) => ( { isDragging : DropOverlay . isValidDataTransfer ( event ) } ) ) ; } } ; private onDragOver = ( event ) => { if ( this . state . isDragging ) { event . preventDefault ( ) ; event . stopPropagation ( ) ; if ( ! this . state . isDraggingOver ) { this . setState ( ( ) => ( { isDraggingOver : true } ) ) ; } } } ; private onDragLeave = ( event ) => { const target = event . target as HTMLElement | null ; if ( this . state . isDragging && this . state . isDraggingOver && ! _ . isNil ( target ) && target . classList . contains ( Classes . OVERLAY_BACKDROP ) ) { this . setState ( ( ) => ( { isDragging : false , isDraggingOver : false } ) ) ; } } ; private onDrop = ( event ) => { const { isDraggingOver , isDragging } = this . state ; if ( ! isDragging && ! isDraggingOver ) { return false ; } event . preventDefault ( ) ; event . stopPropagation ( ) ; this . setState ( ( ) => ( { isDragging : false , isDraggingOver : false } ) ) ; if ( DropOverlay . isImageDataTransfer ( event ) && ! _ . isNil ( event . dataTransfer ) ) { const file = _ . head ( event . dataTransfer . files ) ; if ( _ . isNil ( file ) ) { this . props . onInvalid ( ) ; return false ; } this . upload ( file ) ; return false ; } this . props . onInvalid ( ) ; return false ; } ; private async upload ( file ) { const { onError , onStart , onSuccess } = this . props ; try { onStart ( ) ; const response = await Imgur . uploadAnonymousFile ( file ) ; onSuccess ( response . data . link , `https://imgur.com/delete/${ response . data . deletehash }` ) ; } catch ( error ) { onError ( error ) ; } } } /**
 * React Props.
 */ interface Props { onInvalid : ( ) => void ; onError : ( error : Error ) => void ; onStart : ( ) => void ; onSuccess : ( url : string , deletionUrl : string ) => void ; }