aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/post_status_form/post_status_form.js91
-rw-r--r--src/components/post_status_form/post_status_form.vue97
-rw-r--r--src/components/status/status.vue3
-rw-r--r--src/components/status_content/status_content.vue3
-rw-r--r--src/components/user_panel/user_panel.vue4
-rw-r--r--src/i18n/en.json2
-rw-r--r--src/services/api/api.service.js14
-rw-r--r--src/services/status_poster/status_poster.service.js19
8 files changed, 197 insertions, 36 deletions
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
index 9027566f..1bf5dae3 100644
--- a/src/components/post_status_form/post_status_form.js
+++ b/src/components/post_status_form/post_status_form.js
@@ -3,9 +3,10 @@ import MediaUpload from '../media_upload/media_upload.vue'
import ScopeSelector from '../scope_selector/scope_selector.vue'
import EmojiInput from '../emoji_input/emoji_input.vue'
import PollForm from '../poll/poll_form.vue'
+import StatusContent from '../status_content/status_content.vue'
import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
-import { reject, map, uniqBy } from 'lodash'
+import { reject, map, uniqBy, debounce } from 'lodash'
import suggestor from '../emoji_input/suggestor.js'
import { mapGetters } from 'vuex'
import Checkbox from '../checkbox/checkbox.vue'
@@ -38,7 +39,8 @@ const PostStatusForm = {
EmojiInput,
PollForm,
ScopeSelector,
- Checkbox
+ Checkbox,
+ StatusContent
},
mounted () {
this.resize(this.$refs.textarea)
@@ -84,7 +86,9 @@ const PostStatusForm = {
caret: 0,
pollFormVisible: false,
showDropIcon: 'hide',
- dropStopTimeout: null
+ dropStopTimeout: null,
+ preview: null,
+ previewLoading: false
}
},
computed: {
@@ -163,18 +167,33 @@ const PostStatusForm = {
this.newStatus.poll &&
this.newStatus.poll.error
},
+ showPreview () {
+ return !!this.preview || this.previewLoading
+ },
+ emptyStatus () {
+ return this.newStatus.status.trim() === '' && this.newStatus.files.length === 0
+ },
...mapGetters(['mergedConfig'])
},
+ watch: {
+ 'newStatus.contentType': function (newType) {
+ if (newType === 'text/plain') {
+ this.closePreview()
+ } else if (this.preview) {
+ this.previewStatus(this.newStatus)
+ }
+ },
+ 'newStatus.spoilerText': function () {
+ this.autoPreview()
+ }
+ },
methods: {
postStatus (newStatus) {
if (this.posting) { return }
if (this.submitDisabled) { return }
-
- if (this.newStatus.status === '') {
- if (this.newStatus.files.length === 0) {
- this.error = 'Cannot post an empty status with no files'
- return
- }
+ if (this.emptyStatus) {
+ this.error = 'Cannot post an empty status with no files'
+ return
}
const poll = this.pollFormVisible ? this.newStatus.poll : {}
@@ -212,12 +231,64 @@ const PostStatusForm = {
el.style.height = 'auto'
el.style.height = undefined
this.error = null
+ this.closePreview()
} else {
this.error = data.error
}
this.posting = false
})
},
+ previewStatus () {
+ if (this.emptyStatus && this.newStatus.spoilerText.trim() === '') {
+ this.preview = { error: this.$t('status.preview_empty') }
+ this.previewLoading = false
+ return
+ }
+ const newStatus = this.newStatus
+ this.previewLoading = true
+ statusPoster.postStatus({
+ status: newStatus.status,
+ spoilerText: newStatus.spoilerText || null,
+ visibility: newStatus.visibility,
+ sensitive: newStatus.nsfw,
+ media: [],
+ store: this.$store,
+ inReplyToStatusId: this.replyTo,
+ contentType: newStatus.contentType,
+ poll: {},
+ preview: true
+ }).then((data) => {
+ // Don't apply preview if not loading, because it means
+ // user has closed the preview manually.
+ if (!this.previewLoading) return
+ if (!data.error) {
+ this.preview = data
+ } else {
+ this.preview = { error: data.error }
+ }
+ }).catch((error) => {
+ this.preview = { error }
+ }).finally(() => {
+ this.previewLoading = false
+ })
+ },
+ debouncePreviewStatus: debounce(function () { this.previewStatus() }, 500),
+ autoPreview () {
+ if (!this.preview) return
+ this.previewLoading = true
+ this.debouncePreviewStatus()
+ },
+ closePreview () {
+ this.preview = null
+ this.previewLoading = false
+ },
+ togglePreview () {
+ if (this.showPreview) {
+ this.closePreview()
+ } else {
+ this.previewStatus()
+ }
+ },
addMediaFile (fileInfo) {
this.newStatus.files.push(fileInfo)
},
@@ -239,6 +310,7 @@ const PostStatusForm = {
return fileTypeService.fileType(fileInfo.mimetype)
},
paste (e) {
+ this.autoPreview()
this.resize(e)
if (e.clipboardData.files.length > 0) {
// prevent pasting of file as text
@@ -273,6 +345,7 @@ const PostStatusForm = {
}
},
onEmojiInputInput (e) {
+ this.autoPreview()
this.$nextTick(() => {
this.resize(this.$refs['textarea'])
})
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
index e3d8d087..0cebd36e 100644
--- a/src/components/post_status_form/post_status_form.vue
+++ b/src/components/post_status_form/post_status_form.vue
@@ -16,6 +16,44 @@
@drop.stop="fileDrop"
/>
<div class="form-group">
+ <div class="preview-heading faint">
+ <a
+ class="preview-toggle faint"
+ @click.stop.prevent="togglePreview"
+ >
+ {{ $t('status.preview') }}
+ <i
+ class="icon-down-open"
+ :style="{ transform: showPreview ? 'rotate(0deg)' : 'rotate(-90deg)' }"
+ />
+ </a>
+ <i
+ v-show="previewLoading"
+ class="icon-spin3 animate-spin"
+ />
+ </div>
+ <div
+ v-if="showPreview"
+ class="preview-container"
+ >
+ <div
+ v-if="!preview"
+ class="preview-status"
+ >
+ {{ $t('general.loading') }}
+ </div>
+ <div
+ v-else-if="preview.error"
+ class="preview-status preview-error"
+ >
+ {{ preview.error }}
+ </div>
+ <StatusContent
+ v-else
+ :status="preview"
+ class="preview-status"
+ />
+ </div>
<i18n
v-if="!$store.state.users.currentUser.locked && newStatus.visibility == 'private'"
path="post_status.account_not_locked_warning"
@@ -77,7 +115,6 @@
class="form-control"
>
<input
-
v-model="newStatus.spoilerText"
type="text"
:placeholder="$t('post_status.content_warning')"
@@ -303,14 +340,6 @@
}
.post-status-form {
- .visibility-tray {
- display: flex;
- justify-content: space-between;
- padding-top: 5px;
- }
-}
-
-.post-status-form {
.form-bottom {
display: flex;
justify-content: space-between;
@@ -336,6 +365,48 @@
max-width: 10em;
}
+ .preview-heading {
+ display: flex;
+ width: 100%;
+
+ .icon-spin3 {
+ margin-left: auto;
+ }
+ }
+
+ .preview-toggle {
+ display: flex;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ .icon-down-open {
+ transition: transform 0.1s;
+ }
+
+ .preview-container {
+ margin-bottom: 1em;
+ }
+
+ .preview-error {
+ font-style: italic;
+ color: $fallback--faint;
+ color: var(--faint, $fallback--faint);
+ }
+
+ .preview-status {
+ border: 1px solid $fallback--border;
+ border: 1px solid var(--border, $fallback--border);
+ border-radius: $fallback--tooltipRadius;
+ border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
+ padding: 0.5em;
+ margin: 0;
+ line-height: 1.4em;
+ }
+
.text-format {
.only-format {
color: $fallback--faint;
@@ -343,6 +414,12 @@
}
}
+ .visibility-tray {
+ display: flex;
+ justify-content: space-between;
+ padding-top: 5px;
+ }
+
.media-upload-icon, .poll-icon, .emoji-icon {
font-size: 26px;
flex: 1;
@@ -408,7 +485,7 @@
flex-direction: column;
}
- .attachments {
+ .media-upload-wrapper .attachments {
padding: 0 0.5em;
.attachment {
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 8237be6c..f6b5dd6f 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -377,9 +377,6 @@ $status-margin: 0.75em;
}
.status-el {
- overflow-wrap: break-word;
- word-wrap: break-word;
- word-break: break-word;
border-left-width: 0px;
min-width: 0;
border-color: $fallback--border;
diff --git a/src/components/status_content/status_content.vue b/src/components/status_content/status_content.vue
index 3460c2fa..40c28b5c 100644
--- a/src/components/status_content/status_content.vue
+++ b/src/components/status_content/status_content.vue
@@ -217,6 +217,9 @@ $status-margin: 0.75em;
font-family: var(--postFont, sans-serif);
line-height: 1.4em;
white-space: pre-wrap;
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ word-break: break-word;
blockquote {
margin: 0.2em 0 0.2em 2em;
diff --git a/src/components/user_panel/user_panel.vue b/src/components/user_panel/user_panel.vue
index 1db4f648..5685916a 100644
--- a/src/components/user_panel/user_panel.vue
+++ b/src/components/user_panel/user_panel.vue
@@ -10,9 +10,7 @@
:hide-bio="true"
rounded="top"
/>
- <div class="panel-footer">
- <PostStatusForm />
- </div>
+ <PostStatusForm />
</div>
<auth-form
v-else
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 8f13dfe9..d7938405 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -644,6 +644,8 @@
"copy_link": "Copy link to status",
"thread_muted": "Thread muted",
"thread_muted_and_words": ", has words:",
+ "preview": "Preview",
+ "preview_empty": "Empty",
"show_full_subject": "Show full subject",
"hide_full_subject": "Hide full subject",
"show_content": "Show content",
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index c9ec88b7..174add70 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -645,7 +645,8 @@ const postStatus = ({
poll,
mediaIds = [],
inReplyToStatusId,
- contentType
+ contentType,
+ preview
}) => {
const form = new FormData()
const pollOptions = poll.options || []
@@ -675,6 +676,9 @@ const postStatus = ({
if (inReplyToStatusId) {
form.append('in_reply_to_id', inReplyToStatusId)
}
+ if (preview) {
+ form.append('preview', 'true')
+ }
return fetch(MASTODON_POST_STATUS_URL, {
body: form,
@@ -682,13 +686,7 @@ const postStatus = ({
headers: authHeaders(credentials)
})
.then((response) => {
- if (response.ok) {
- return response.json()
- } else {
- return {
- error: response
- }
- }
+ return response.json()
})
.then((data) => data.error ? data : parseStatus(data))
}
diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js
index 9e904d3a..86fc5601 100644
--- a/src/services/status_poster/status_poster.service.js
+++ b/src/services/status_poster/status_poster.service.js
@@ -1,7 +1,18 @@
import { map } from 'lodash'
import apiService from '../api/api.service.js'
-const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => {
+const postStatus = ({
+ store,
+ status,
+ spoilerText,
+ visibility,
+ sensitive,
+ poll,
+ media = [],
+ inReplyToStatusId = undefined,
+ contentType = 'text/plain',
+ preview = false
+}) => {
const mediaIds = map(media, 'id')
return apiService.postStatus({
@@ -13,9 +24,11 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, poll, m
mediaIds,
inReplyToStatusId,
contentType,
- poll })
+ poll,
+ preview
+ })
.then((data) => {
- if (!data.error) {
+ if (!data.error && !preview) {
store.dispatch('addNewStatuses', {
statuses: [data],
timeline: 'friends',