185 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<div class="compose-box {{overLimit ? 'over-char-limit' : ''}} {{realm === 'dialog' ? 'dialog-size' : '' }}">
 | 
						|
  <ComposeAuthor />
 | 
						|
  {{#if contentWarningShown}}
 | 
						|
    <div class="compose-content-warning-wrapper"
 | 
						|
         transition:slide="{duration: 333}">
 | 
						|
      <ComposeContentWarning :realm :contentWarning />
 | 
						|
    </div>
 | 
						|
  {{/if}}
 | 
						|
  <ComposeInput :realm :text :autoFocus />
 | 
						|
  <ComposeLengthGauge :length :overLimit />
 | 
						|
  <ComposeToolbar :realm :postPrivacy :media :contentWarningShown :text />
 | 
						|
  <ComposeLengthIndicator :length :overLimit />
 | 
						|
  <ComposeMedia :realm :media />
 | 
						|
</div>
 | 
						|
<div class="compose-box-button-sentinel" ref:sentinel></div>
 | 
						|
<div class="compose-box-button-wrapper" >
 | 
						|
 <ComposeButton :length :overLimit :sticky on:click="onClickPostButton()" />
 | 
						|
</div>
 | 
						|
<div class="compose-box-border-bottom"></div>
 | 
						|
<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.dialog-size {
 | 
						|
    width: 540px;
 | 
						|
    max-width: calc(100vw - 60px);
 | 
						|
  }
 | 
						|
 | 
						|
  .compose-box-border-bottom {
 | 
						|
    height: 1px;
 | 
						|
    background: var(--main-border);
 | 
						|
    width: 100%;
 | 
						|
  }
 | 
						|
 | 
						|
  .compose-box-button-wrapper {
 | 
						|
    width: 100%;
 | 
						|
    display: flex;
 | 
						|
    justify-content: flex-end;
 | 
						|
    position: -webkit-sticky;
 | 
						|
    position: sticky;
 | 
						|
    top: 10px;
 | 
						|
    z-index: 1000;
 | 
						|
    pointer-events: none;
 | 
						|
  }
 | 
						|
 | 
						|
  .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.dialog-size {
 | 
						|
      width: 560px;
 | 
						|
      max-width: calc(100vw - 40px);
 | 
						|
    }
 | 
						|
    .compose-box-button-wrapper {
 | 
						|
      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 } from '../../_actions/compose'
 | 
						|
  import { importDialogs } from '../../_utils/asyncModules'
 | 
						|
 | 
						|
  export default {
 | 
						|
    oncreate() {
 | 
						|
      let realm = this.get('realm')
 | 
						|
      if (realm === 'home') {
 | 
						|
        this.__stickyObserver = new IntersectionObserver(entries => {
 | 
						|
          this.set({sticky: !entries[0].isIntersecting})
 | 
						|
        })
 | 
						|
        this.__stickyObserver.observe(this.refs.sentinel)
 | 
						|
      } else if (realm !== 'dialog') {
 | 
						|
        // if this is a reply, populate the handle immediately
 | 
						|
        insertHandleForReply(realm)
 | 
						|
      }
 | 
						|
 | 
						|
      this.observe('postedStatusForRealm', postedStatusForRealm => {
 | 
						|
        if (postedStatusForRealm !== realm) {
 | 
						|
          return
 | 
						|
        }
 | 
						|
        this.fire('postedStatus')
 | 
						|
        // if this is a reply, go back immediately after it's posted
 | 
						|
        if (realm !== 'home' && realm !== 'dialog') {
 | 
						|
          window.history.back()
 | 
						|
        }
 | 
						|
      }, {init: false})
 | 
						|
    },
 | 
						|
    ondestroy() {
 | 
						|
      if (this.__stickyObserver) {
 | 
						|
        this.__stickyObserver.disconnect()
 | 
						|
      }
 | 
						|
    },
 | 
						|
    components: {
 | 
						|
      ComposeAuthor,
 | 
						|
      ComposeToolbar,
 | 
						|
      ComposeLengthGauge,
 | 
						|
      ComposeLengthIndicator,
 | 
						|
      ComposeInput,
 | 
						|
      ComposeButton,
 | 
						|
      ComposeMedia,
 | 
						|
      ComposeContentWarning
 | 
						|
    },
 | 
						|
    store: () => store,
 | 
						|
    computed: {
 | 
						|
      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 || '',
 | 
						|
      postedStatusForRealm: ($postedStatusForRealm) => $postedStatusForRealm
 | 
						|
    },
 | 
						|
    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
 | 
						|
          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')
 | 
						|
 | 
						|
          if (!text || overLimit) {
 | 
						|
            return // do nothing if invalid
 | 
						|
          }
 | 
						|
 | 
						|
          /* no await */
 | 
						|
          postStatus(realm, text, inReplyTo, mediaIds,
 | 
						|
            sensitive, contentWarning, postPrivacyKey)
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
</script>
 |