{{#if realm === 'home'}}
  <h1 class="sr-only">Compose toot</h1>
{{/if}}
<div class="{{computedClassName}} {{hideAndFadeIn}}">
  <ComposeAuthor />
  {{#if contentWarningShown}}
    <div class="compose-content-warning-wrapper"
         transition:slide="{duration: 333}">
      <ComposeContentWarning :realm :contentWarning />
    </div>
  {{/if}}
  <ComposeInput :realm :text :autoFocus on:postAction="doPostStatus()" />
  <ComposeLengthGauge :length :overLimit />
  <ComposeToolbar :realm :postPrivacy :media :contentWarningShown :text />
  <ComposeLengthIndicator :length :overLimit />
  <ComposeMedia :realm :media :mediaDescriptions />
</div>
<div class="compose-box-button-sentinel {{hideAndFadeIn}}" ref:sentinel></div>
<div class="compose-box-button-wrapper {{realm === 'home' ? 'compose-button-sticky' : ''}} {{hideAndFadeIn}}" >
 <ComposeButton :length :overLimit :sticky on:click="onClickPostButton()" />
</div>
{{#if !hideBottomBorder}}
  <div class="compose-box-border-bottom {{hideAndFadeIn}}"></div>
{{/if}}
<style>
  .compose-box {
    border-radius: 4px;
    padding: 20px 20px 0 20px;
    display: grid;
    align-items: flex-start;
    grid-template-areas:
      "avatar name       handle    handle"
      "avatar cw         cw        cw"
      "avatar input      input     input"
      "avatar gauge      gauge     gauge"
      "avatar toolbar    toolbar   length"
      "avatar media      media     media";
    grid-template-columns: min-content minmax(0, max-content) 1fr 1fr;
    width: 560px;
    max-width: calc(100vw - 40px);
  }

  .compose-box.direct-reply {
    background-color: var(--status-direct-background);
  }

  .compose-box.slim-size {
    width: 540px;
    max-width: calc(100vw - 60px);
  }

  .compose-box-fade-in {
    transition: opacity 0.2s linear; /* main page reveal */
  }

  .compose-box-border-bottom {
    height: 1px;
    background: var(--main-border);
    width: 100%;
  }

  .compose-box-button-wrapper {
    /*
     * We want pointer-events only for the sticky button, so use fit-content so that
     * the element doesn't take up the full width, and then set its left margin to
     * auto so that it sticks to the right. fit-content doesn't work in Edge, but
     * that just means that content that is level with the button is not clickable.
     */
    width: -moz-fit-content;
    width: fit-content;
    margin-left: auto;
    display: flex;
    justify-content: flex-end;
  }

  .compose-box-button-wrapper.compose-button-sticky {
    position: -webkit-sticky;
    position: sticky;
    top: 10px;
    z-index: 5000;
  }

  .compose-content-warning-wrapper {
    grid-area: cw;
  }

  @media (max-width: 767px) {
    .compose-box {
      padding: 10px 10px 0 10px;
      max-width: calc(100vw - 20px);
      width: 580px;
    }
    .compose-box.slim-size {
      width: 560px;
      max-width: calc(100vw - 40px);
    }
    .compose-box-button-wrapper.compose-button-sticky {
      top: 5px;
    }
  }
</style>
<script>
  import ComposeToolbar from './ComposeToolbar.html'
  import ComposeLengthGauge from './ComposeLengthGauge.html'
  import ComposeLengthIndicator from './ComposeLengthIndicator.html'
  import ComposeAuthor from './ComposeAuthor.html'
  import ComposeInput from './ComposeInput.html'
  import ComposeButton from './ComposeButton.html'
  import ComposeMedia from './ComposeMedia.html'
  import ComposeContentWarning from './ComposeContentWarning.html'
  import { measureText } from '../../_utils/measureText'
  import { CHAR_LIMIT, POST_PRIVACY_OPTIONS } from '../../_static/statuses'
  import { store } from '../../_store/store'
  import { slide } from 'svelte-transitions'
  import { postStatus, insertHandleForReply, setReplySpoiler, setReplyVisibility } from '../../_actions/compose'
  import { importDialogs } from '../../_utils/asyncModules'
  import { classname } from '../../_utils/classname'

  export default {
    oncreate() {
      let realm = this.get('realm')
      if (realm === 'home') {
        this.setupStickyObserver()
      } else if (realm !== 'dialog') {
        // if this is a reply, populate the handle immediately
        insertHandleForReply(realm)
      }

      let replySpoiler = this.get('replySpoiler')
      if (replySpoiler) {
        // default spoiler is same as the replied-to status
        setReplySpoiler(realm, replySpoiler)
      }

      let replyVisibility = this.get('replyVisibility')
      if (replyVisibility) {
        // make sure the visibility is consistent with the replied-to status
        setReplyVisibility(realm, replyVisibility)
      }
    },
    ondestroy() {
      this.teardownStickyObserver()
    },
    components: {
      ComposeAuthor,
      ComposeToolbar,
      ComposeLengthGauge,
      ComposeLengthIndicator,
      ComposeInput,
      ComposeButton,
      ComposeMedia,
      ComposeContentWarning
    },
    store: () => store,
    computed: {
      computedClassName: (overLimit, realm, size, postPrivacyKey, isReply) => {
        return classname(
          'compose-box',
          overLimit && 'over-char-limit',
          size === 'slim' && 'slim-size',
          isReply && postPrivacyKey === 'direct' && 'direct-reply'
        )
      },
      hideAndFadeIn: (hidden) => {
        return classname(
          'compose-box-fade-in',
          hidden && 'hidden'
        )
      },
      composeData: ($currentComposeData, realm) => $currentComposeData[realm] || {},
      text: (composeData) => composeData.text || '',
      media: (composeData) => composeData.media || [],
      postPrivacy: (postPrivacyKey) => POST_PRIVACY_OPTIONS.find(_ => _.key === postPrivacyKey),
      defaultPostPrivacyKey: ($currentVerifyCredentials) => $currentVerifyCredentials.source.privacy,
      postPrivacyKey: (composeData, defaultPostPrivacyKey) => composeData.postPrivacy || defaultPostPrivacyKey,
      textLength: (text) => measureText(text),
      contentWarningLength: (contentWarning) => measureText(contentWarning),
      length: (textLength, contentWarningLength, contentWarningShown) => {
        return textLength + (contentWarningShown ? contentWarningLength : 0)
      },
      overLimit: (length) => length > CHAR_LIMIT,
      contentWarningShown: (composeData) => composeData.contentWarningShown,
      contentWarning: (composeData) => composeData.contentWarning || '',
      timelineInitialized: ($timelineInitialized) => $timelineInitialized,
      mediaDescriptions: (composeData) => composeData.mediaDescriptions || []
    },
    transitions: {
      slide
    },
    methods: {
      async onClickPostButton() {
        if (this.get('sticky')) {
          // when the button is sticky, we're scrolled down the home timeline,
          // so we should launch a new compose dialog
          let dialogs = await importDialogs()
          dialogs.showComposeDialog()
        } else {
          // else we're actually posting a new toot
          this.doPostStatus();
        }
      },
      doPostStatus() {
        let text = this.get('text')
        let media = this.get('media')
        let postPrivacyKey = this.get('postPrivacyKey')
        let contentWarning = this.get('contentWarning')
        let sensitive = media.length && !!contentWarning
        let realm = this.get('realm')
        let mediaIds = media.map(_ => _.data.id)
        let inReplyTo = (realm === 'home' || realm === 'dialog') ? null : realm
        let overLimit = this.get('overLimit')
        let mediaDescriptions = this.get('mediaDescriptions')
        let inReplyToUuid = this.get('inReplyToUuid')

        if (!text || overLimit) {
          return // do nothing if invalid
        }

        /* no await */
        postStatus(realm, text, inReplyTo, mediaIds,
          sensitive, contentWarning, postPrivacyKey,
          mediaDescriptions, inReplyToUuid)
      },
      setupStickyObserver() {
        this.__stickyObserver = new IntersectionObserver(entries => {
          this.set({sticky: !entries[0].isIntersecting})
        })
        this.__stickyObserver.observe(this.refs.sentinel)

        // also create a one-shot observer for the $timelineInitialized event,
        // due to a bug in Firefox where when the scrollTop is set
        // manually, the other observer doesn't necessarily fire
        this.observe('timelineInitialized', timelineInitialized => {
          if (timelineInitialized) {
            let observer = new IntersectionObserver(entries => {
              this.set({sticky: !entries[0].isIntersecting})
              observer.disconnect()
            })
            observer.observe(this.refs.sentinel)
          }
        }, {init: false})
      },
      teardownStickyObserver() {
        if (this.__stickyObserver) {
          this.__stickyObserver.disconnect()
        }
      }
    }
  }
</script>