pinafore/routes/_components/compose/ComposeBox.html

186 lines
6.2 KiB
HTML
Raw Normal View History

2018-03-27 07:02:55 +00:00
<div class="compose-box {{overLimit ? 'over-char-limit' : ''}} {{realm === 'dialog' ? 'dialog-size' : '' }}">
<ComposeAuthor />
2018-03-03 23:44:43 +00:00
{{#if contentWarningShown}}
2018-03-04 00:25:22 +00:00
<div class="compose-content-warning-wrapper"
transition:slide="{duration: 333}">
<ComposeContentWarning :realm :contentWarning />
</div>
2018-03-03 23:44:43 +00:00
{{/if}}
2018-03-17 02:04:48 +00:00
<ComposeInput :realm :text :autoFocus />
2018-03-04 00:12:48 +00:00
<ComposeLengthGauge :length :overLimit />
2018-03-25 01:04:54 +00:00
<ComposeToolbar :realm :postPrivacy :media :contentWarningShown :text />
2018-03-04 00:12:48 +00:00
<ComposeLengthIndicator :length :overLimit />
2018-03-03 22:51:48 +00:00
<ComposeMedia :realm :media />
2018-02-26 00:26:43 +00:00
</div>
2018-03-27 07:02:55 +00:00
<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>
2018-02-26 00:26:43 +00:00
<style>
2018-02-27 05:54:21 +00:00
.compose-box {
2018-02-26 00:26:43 +00:00
border-radius: 4px;
2018-03-27 07:02:55 +00:00
padding: 20px 20px 0 20px;
2018-02-26 00:26:43 +00:00
display: grid;
align-items: flex-start;
grid-template-areas:
2018-03-03 23:44:43 +00:00
"avatar name handle handle"
"avatar cw cw cw"
"avatar input input input"
"avatar gauge gauge gauge"
"avatar toolbar toolbar length"
2018-03-27 07:02:55 +00:00
"avatar media media media";
grid-template-columns: min-content minmax(0, max-content) 1fr 1fr;
2018-02-26 00:26:43 +00:00
width: 560px;
max-width: calc(100vw - 40px);
}
2018-03-27 07:02:55 +00:00
.compose-box.dialog-size {
width: 540px;
2018-03-28 16:05:22 +00:00
max-width: calc(100vw - 60px);
2018-03-27 07:02:55 +00:00
}
.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;
2018-03-27 16:38:19 +00:00
z-index: 1000;
pointer-events: none;
2018-03-27 07:02:55 +00:00
}
2018-03-04 00:25:22 +00:00
.compose-content-warning-wrapper {
grid-area: cw;
}
2018-02-26 00:26:43 +00:00
@media (max-width: 767px) {
2018-02-27 05:54:21 +00:00
.compose-box {
2018-03-27 07:02:55 +00:00
padding: 10px 10px 0 10px;
2018-02-26 00:26:43 +00:00
max-width: calc(100vw - 20px);
width: 580px;
}
2018-03-27 07:02:55 +00:00
.compose-box.dialog-size {
width: 560px;
max-width: calc(100vw - 40px);
}
.compose-box-button-wrapper {
top: 5px;
}
2018-02-26 00:26:43 +00:00
}
</style>
<script>
2018-02-27 05:50:03 +00:00
import ComposeToolbar from './ComposeToolbar.html'
import ComposeLengthGauge from './ComposeLengthGauge.html'
import ComposeLengthIndicator from './ComposeLengthIndicator.html'
2018-02-27 05:54:21 +00:00
import ComposeAuthor from './ComposeAuthor.html'
2018-02-27 06:22:56 +00:00
import ComposeInput from './ComposeInput.html'
import ComposeButton from './ComposeButton.html'
2018-03-02 05:21:49 +00:00
import ComposeMedia from './ComposeMedia.html'
2018-03-03 23:44:43 +00:00
import ComposeContentWarning from './ComposeContentWarning.html'
2018-03-03 22:51:48 +00:00
import { measureText } from '../../_utils/measureText'
import { CHAR_LIMIT, POST_PRIVACY_OPTIONS } from '../../_static/statuses'
import { store } from '../../_store/store'
2018-03-04 00:25:22 +00:00
import { slide } from 'svelte-transitions'
2018-03-09 16:45:12 +00:00
import { postStatus, insertHandleForReply } from '../../_actions/compose'
2018-03-27 07:02:55 +00:00
import { importDialogs } from '../../_utils/asyncModules'
2018-02-26 01:21:17 +00:00
2018-02-26 00:26:43 +00:00
export default {
2018-03-08 02:04:20 +00:00
oncreate() {
2018-03-09 16:45:12 +00:00
let realm = this.get('realm')
2018-03-27 07:02:55 +00:00
if (realm === 'home') {
this.__stickyObserver = new IntersectionObserver(entries => {
this.set({sticky: !entries[0].isIntersecting})
})
this.__stickyObserver.observe(this.refs.sentinel)
} else if (realm !== 'dialog') {
2018-03-09 16:45:12 +00:00
// if this is a reply, populate the handle immediately
insertHandleForReply(realm)
2018-03-27 07:02:55 +00:00
}
2018-03-09 16:45:12 +00:00
2018-03-27 07:02:55 +00:00
this.observe('postedStatusForRealm', postedStatusForRealm => {
if (postedStatusForRealm !== realm) {
return
}
this.fire('postedStatus')
2018-03-10 06:31:26 +00:00
// if this is a reply, go back immediately after it's posted
2018-03-27 07:02:55 +00:00
if (realm !== 'home' && realm !== 'dialog') {
window.history.back()
}
}, {init: false})
},
ondestroy() {
if (this.__stickyObserver) {
this.__stickyObserver.disconnect()
2018-03-10 06:31:26 +00:00
}
2018-03-08 02:04:20 +00:00
},
2018-02-26 00:26:43 +00:00
components: {
2018-02-27 05:54:21 +00:00
ComposeAuthor,
2018-02-27 05:50:03 +00:00
ComposeToolbar,
ComposeLengthGauge,
2018-02-27 06:22:56 +00:00
ComposeLengthIndicator,
ComposeInput,
2018-03-02 05:21:49 +00:00
ComposeButton,
2018-03-03 23:44:43 +00:00
ComposeMedia,
ComposeContentWarning
2018-03-03 22:51:48 +00:00
},
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),
2018-03-04 00:12:48 +00:00
contentWarningLength: (contentWarning) => measureText(contentWarning),
length: (textLength, contentWarningLength, contentWarningShown) => {
return textLength + (contentWarningShown ? contentWarningLength : 0)
},
overLimit: (length) => length > CHAR_LIMIT,
2018-03-03 23:44:43 +00:00
contentWarningShown: (composeData) => composeData.contentWarningShown,
2018-03-08 02:04:20 +00:00
contentWarning: (composeData) => composeData.contentWarning || '',
postedStatusForRealm: ($postedStatusForRealm) => $postedStatusForRealm
2018-03-04 00:25:22 +00:00
},
transitions: {
slide
2018-03-05 00:27:15 +00:00
},
methods: {
2018-03-27 07:02:55 +00:00
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')
2018-03-27 07:02:55 +00:00
if (!text || overLimit) {
return // do nothing if invalid
}
2018-03-08 02:04:20 +00:00
2018-03-27 07:02:55 +00:00
/* no await */
postStatus(realm, text, inReplyTo, mediaIds,
sensitive, contentWarning, postPrivacyKey)
}
2018-03-05 00:27:15 +00:00
}
2018-02-26 00:26:43 +00:00
}
}
2018-02-26 08:24:28 +00:00
</script>