commit: 5c6d29193ef24b19c8b3f76eabb05663e107571c
parent a84424408dc787b922474e932f5e37758e20c189
Author: HJ <30-hj@users.noreply.git.pleroma.social>
Date: Tue, 31 Dec 2024 11:17:22 +0000
Merge branch 'drafts-improvements' into 'develop'
Drafts improvements
See merge request pleroma/pleroma-fe!1964
Diffstat:
13 files changed, 547 insertions(+), 348 deletions(-)
diff --git a/changelog.d/drafts-imp.skip b/changelog.d/drafts-imp.skip
diff --git a/src/App.scss b/src/App.scss
@@ -390,6 +390,24 @@ nav {
}
}
+.menu-item {
+ line-height: var(--__line-height);
+ font-family: inherit;
+ font-weight: 400;
+ font-size: 100%;
+ cursor: pointer;
+
+ a,
+ button:not(.button-default) {
+ color: var(--text);
+ font-size: 100%;
+ }
+
+ &.disabled {
+ cursor: not-allowed;
+ }
+}
+
.menu-item,
.list-item {
display: block;
@@ -397,10 +415,6 @@ nav {
border: none;
outline: none;
text-align: initial;
- font-size: inherit;
- font-family: inherit;
- font-weight: 400;
- cursor: pointer;
color: inherit;
clear: both;
position: relative;
@@ -410,7 +424,6 @@ nav {
border-width: 0;
border-top-width: 1px;
width: 100%;
- line-height: var(--__line-height);
padding: var(--__vertical-gap) var(--__horizontal-gap);
background: transparent;
@@ -450,10 +463,8 @@ nav {
border: none;
outline: none;
display: inline;
- font-size: 100%;
font-family: inherit;
line-height: unset;
- color: var(--text);
}
&:first-child {
diff --git a/src/components/draft/draft.js b/src/components/draft/draft.js
@@ -2,13 +2,25 @@ import PostStatusForm from 'src/components/post_status_form/post_status_form.vue
import EditStatusForm from 'src/components/edit_status_form/edit_status_form.vue'
import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
+import Gallery from 'src/components/gallery/gallery.vue'
+import { cloneDeep } from 'lodash'
+
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+ faPollH
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faPollH
+)
const Draft = {
components: {
PostStatusForm,
EditStatusForm,
ConfirmModal,
- StatusContent
+ StatusContent,
+ Gallery
},
props: {
draft: {
@@ -18,6 +30,7 @@ const Draft = {
},
data () {
return {
+ referenceDraft: cloneDeep(this.draft),
editing: false,
showingConfirmDialog: false
}
@@ -32,6 +45,11 @@ const Draft = {
return {}
}
},
+ safeToSave () {
+ return this.draft.status ||
+ this.draft.files?.length ||
+ this.draft.hasPoll
+ },
postStatusFormProps () {
return {
draftId: this.draft.id,
@@ -40,6 +58,28 @@ const Draft = {
},
refStatus () {
return this.draft.refId ? this.$store.state.statuses.allStatusesObject[this.draft.refId] : undefined
+ },
+ localCollapseSubjectDefault () {
+ return this.$store.getters.mergedConfig.collapseMessageWithSubject
+ },
+ nsfwClickthrough () {
+ if (!this.draft.nsfw) {
+ return false
+ }
+ if (this.draft.summary && this.localCollapseSubjectDefault) {
+ return false
+ }
+ return true
+ }
+ },
+ watch: {
+ editing (newVal) {
+ if (newVal) return
+ if (this.safeToSave) {
+ this.$store.dispatch('addOrSaveDraft', { draft: this.draft })
+ } else {
+ this.$store.dispatch('addOrSaveDraft', { draft: this.referenceDraft })
+ }
}
},
methods: {
diff --git a/src/components/draft/draft.vue b/src/components/draft/draft.vue
@@ -1,21 +1,5 @@
<template>
<article class="Draft">
- <div class="actions">
- <button
- class="btn button-default"
- :class="{ toggled: editing }"
- :aria-expanded="editing"
- @click.prevent.stop="toggleEditing"
- >
- {{ $t('drafts.continue') }}
- </button>
- <button
- class="btn button-default"
- @click.prevent.stop="abandon"
- >
- {{ $t('drafts.abandon') }}
- </button>
- </div>
<div
v-if="!editing"
class="status-content"
@@ -42,15 +26,50 @@
:compact="true"
/>
</div>
- <p>{{ draft.status }}</p>
+ <div class="status-preview">
+ <span class="status_content">
+ <p v-if="draft.spoilerText">
+ <i>
+ {{ draft.spoilerText }}:
+ </i>
+ </p>
+ <p v-if="draft.status">{{ draft.status }}</p>
+ <p v-else class="faint">{{ $t('drafts.empty') }}</p>
+ </span>
+ <gallery
+ v-if="draft.files?.length !== 0"
+ class="attachments media-body"
+ :compact="true"
+ :nsfw="nsfwClickthrough"
+ :attachments="draft.files"
+ :limit="1"
+ size="small"
+ @play="$emit('mediaplay', attachment.id)"
+ @pause="$emit('mediapause', attachment.id)"
+ />
+ <div
+ v-if="draft.poll.options"
+ class="poll-indicator-container"
+ :title="$t('drafts.poll_tooltip')"
+ >
+ <div class="poll-indicator">
+ <FAIcon
+ icon="poll-h"
+ size="3x"
+ />
+ </div>
+ </div>
+ </div>
</div>
<div v-if="editing">
<PostStatusForm
v-if="draft.type !== 'edit'"
+ :disable-draft="true"
v-bind="postStatusFormProps"
/>
<EditStatusForm
v-else
+ :disable-draft="true"
:params="postStatusFormProps"
/>
</div>
@@ -66,6 +85,21 @@
{{ $t('drafts.abandon_confirm') }}
</confirm-modal>
</teleport>
+ <div class="actions">
+ <button
+ class="btn button-default"
+ :aria-expanded="editing"
+ @click.prevent.stop="toggleEditing"
+ >
+ {{ editing ? $t('drafts.save') : $t('drafts.continue') }}
+ </button>
+ <button
+ class="btn button-default"
+ @click.prevent.stop="abandon"
+ >
+ {{ $t('drafts.abandon') }}
+ </button>
+ </div>
</article>
</template>
@@ -73,17 +107,55 @@
<style lang="scss">
.Draft {
- margin: 1em;
+ position: relative;
.status-content {
- border: 1px solid;
- border-color: var(--faint);
- border-radius: var(--inputRadius);
- color: var(--text);
padding: 0.5em;
margin: 0.5em 0;
}
+ .status-preview {
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-auto-columns: 10em;
+ grid-auto-flow: column;
+ grid-gap: 0.5em;
+ align-items: start;
+ max-width: 100%;
+
+ p {
+ word-wrap: break-word;
+ white-space: normal;
+ overflow-x: hidden;
+ }
+
+ .poll-indicator-container {
+ border-radius: var(--roundness);
+ display: grid;
+ justify-items: center;
+ align-items: center;
+ align-self: start;
+ height: 0;
+ padding-bottom: 62.5%;
+ position: relative;
+ }
+
+ .poll-indicator {
+ box-sizing: border-box;
+ border: 1px solid var(--border);
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: grid;
+ justify-items: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ }
+ }
+
.actions {
display: flex;
flex-direction: row;
@@ -93,7 +165,6 @@
flex: 1;
margin-left: 1em;
margin-right: 1em;
- max-width: 10em;
}
}
}
diff --git a/src/components/drafts/drafts.vue b/src/components/drafts/drafts.vue
@@ -7,11 +7,17 @@
</div>
</div>
<div class="panel-body">
+ <p v-if="drafts.length === 0">
+ {{ $t('drafts.no_drafts') }}
+ </p>
<List
+ v-else
:items="drafts"
+ :non-interactive="true"
>
<template #item="{ item: draft }">
<Draft
+ class="draft"
:draft="draft"
/>
</template>
@@ -22,3 +28,9 @@
</template>
<script src="./drafts.js"></script>
+
+<style lang="scss">
+.draft {
+ margin: 1em 0;
+}
+</style>
diff --git a/src/components/menu_item.style.js b/src/components/menu_item.style.js
@@ -11,8 +11,9 @@ export default {
'Avatar'
],
states: {
- hover: ':hover',
- active: '.-active'
+ hover: ':hover:not(.disabled)',
+ active: '.-active',
+ disabled: '.disabled'
},
defaultRules: [
{
@@ -85,6 +86,28 @@ export default {
textColor: '--link',
textAuto: 'no-preserve'
}
+ },
+ {
+ component: 'Text',
+ parent: {
+ component: 'MenuItem',
+ state: ['disabled']
+ },
+ directives: {
+ textOpacity: 0.25,
+ textOpacityMode: 'blend'
+ }
+ },
+ {
+ component: 'Icon',
+ parent: {
+ component: 'MenuItem',
+ state: ['disabled']
+ },
+ directives: {
+ textOpacity: 0.25,
+ textOpacityMode: 'blend'
+ }
}
]
}
diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue
@@ -202,6 +202,9 @@
.title {
font-size: 1.3em;
margin-left: 0.6em;
+ white-space: nowrap;
+ overflow-x: hidden;
+ text-overflow: ellipsis;
}
}
diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue
@@ -154,11 +154,6 @@
font-size: 1.1em;
}
- .timelines-chevron {
- margin-left: 0.8em;
- font-size: 1.1em;
- }
-
.timelines-background {
padding: 0 0 0 0.6em;
}
diff --git a/src/components/navigation/navigation_entry.vue b/src/components/navigation/navigation_entry.vue
@@ -82,8 +82,12 @@
--__horizontal-gap: 0.5em;
--__vertical-gap: 0.4em;
- padding: 0;
- display: flex;
+ padding: var(--__vertical-gap) var(--__horizontal-gap);
+ display: grid;
+ grid-template-columns: 1fr;
+ grid-auto-columns: var(--__line-height);
+ grid-auto-flow: column;
+ grid-gap: var(--__horizontal-gap);
align-items: baseline;
&[aria-expanded] {
@@ -93,8 +97,6 @@
.main-link {
line-height: var(--__line-height);
box-sizing: border-box;
- flex: 1;
- padding: var(--__vertical-gap) var(--__horizontal-gap);
}
.menu-icon {
@@ -104,26 +106,16 @@
margin-right: var(--__horizontal-gap);
}
- .timelines-chevron {
- line-height: var(--__line-height);
- padding: 0;
- width: var(--__line-height);
- margin-right: 0;
- }
-
+ .timelines-chevron,
.extra-button {
line-height: var(--__line-height);
+ width: 100%;
padding: 0;
- width: var(--__line-height);
text-align: center;
-
- &:last-child {
- margin-right: calc(-1 * var(--__horizontal-gap));
- }
}
.badge {
- margin: 0 var(--__horizontal-gap);
+ justify-self: center;
}
.iconEmoji {
diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js
@@ -7,6 +7,7 @@ import PollForm from '../poll/poll_form.vue'
import Attachment from '../attachment/attachment.vue'
import Gallery from 'src/components/gallery/gallery.vue'
import StatusContent from '../status_content/status_content.vue'
+import Popover from 'src/components/popover/popover.vue'
import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
@@ -25,7 +26,10 @@ import {
faUpload,
faBan,
faTimes,
- faCircleNotch
+ faCircleNotch,
+ faChevronDown,
+ faChevronLeft,
+ faChevronRight
} from '@fortawesome/free-solid-svg-icons'
library.add(
@@ -34,7 +38,10 @@ library.add(
faUpload,
faBan,
faTimes,
- faCircleNotch
+ faCircleNotch,
+ faChevronDown,
+ faChevronLeft,
+ faChevronRight
)
const buildMentionsString = ({ user, attentions = [] }, currentUser) => {
@@ -111,7 +118,8 @@ const PostStatusForm = {
'resize',
'mediaplay',
'mediapause',
- 'can-close'
+ 'can-close',
+ 'update'
],
components: {
MediaUpload,
@@ -123,7 +131,8 @@ const PostStatusForm = {
Attachment,
StatusContent,
Gallery,
- DraftCloser
+ DraftCloser,
+ Popover
},
mounted () {
this.updateIdempotencyKey()
@@ -210,7 +219,7 @@ const PostStatusForm = {
emojiInputShown: false,
idempotencyKey: '',
saveInhibited: true,
- savable: false
+ saveable: false
}
},
computed: {
@@ -335,7 +344,7 @@ const PostStatusForm = {
return this.$store.getters.mergedConfig.autoSaveDraft
},
autoSaveState () {
- if (this.savable) {
+ if (this.saveable) {
return this.$t('post_status.auto_save_saving')
} else if (this.newStatus.id) {
return this.$t('post_status.auto_save_saved')
@@ -343,6 +352,12 @@ const PostStatusForm = {
return this.$t('post_status.auto_save_nothing_new')
}
},
+ safeToSaveDraft () {
+ return this.newStatus.status ||
+ this.newStatus.spoilerText ||
+ this.newStatus.files?.length ||
+ this.newStatus.hasPoll
+ },
...mapGetters(['mergedConfig']),
...mapState({
mobileLayout: state => state.interface.mobileLayout
@@ -355,7 +370,7 @@ const PostStatusForm = {
this.statusChanged()
}
},
- savable (val) {
+ saveable (val) {
// https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes
// MDN says we'd better add the beforeunload event listener only when needed, and remove it when it's no longer needed
if (val) {
@@ -374,7 +389,7 @@ const PostStatusForm = {
this.autoPreview()
this.updateIdempotencyKey()
this.debouncedMaybeAutoSaveDraft()
- this.savable = true
+ this.saveable = true
this.saveInhibited = false
},
clearStatus () {
@@ -403,7 +418,7 @@ const PostStatusForm = {
el.style.height = undefined
this.error = null
if (this.preview) this.previewStatus()
- this.savable = false
+ this.saveable = false
},
async postStatus (event, newStatus, opts = {}) {
if (this.posting && !this.optimisticPosting) { return }
@@ -738,21 +753,19 @@ const PostStatusForm = {
saveDraft () {
if (!this.disableDraft &&
!this.saveInhibited) {
- if (this.newStatus.status ||
- this.newStatus.files?.length ||
- this.newStatus.hasPoll) {
+ if (this.safeToSaveDraft) {
return this.$store.dispatch('addOrSaveDraft', { draft: this.newStatus })
.then(id => {
if (this.newStatus.id !== id) {
this.newStatus.id = id
}
- this.savable = false
+ this.saveable = false
})
} else if (this.newStatus.id) {
// There is a draft, but there is nothing in it, clear it
return this.abandonDraft()
.then(() => {
- this.savable = false
+ this.saveable = false
})
}
}
@@ -780,7 +793,7 @@ const PostStatusForm = {
// No draft available, fall back
},
requestClose () {
- if (!this.savable) {
+ if (!this.saveable) {
this.$emit('can-close')
} else {
this.$refs.draftCloser.requestClose()
diff --git a/src/components/post_status_form/post_status_form.scss b/src/components/post_status_form/post_status_form.scss
@@ -0,0 +1,258 @@
+.post-status-form {
+ position: relative;
+
+ .attachments {
+ margin-bottom: 0.5em;
+ }
+
+ .more-post-actions {
+ height: 100%;
+
+ .btn {
+ height: 100%;
+ }
+ }
+
+ .form-bottom {
+ display: flex;
+ justify-content: space-between;
+ padding: 0.5em;
+ height: 2.5em;
+
+ .post-button-group {
+ width: 10em;
+ display: flex;
+
+ .post-button {
+ flex: 1 0 auto;
+ }
+
+ .more-post-actions {
+ flex: 0 0 auto;
+ }
+ }
+
+ p {
+ margin: 0.35em;
+ padding: 0.35em;
+ display: flex;
+ }
+ }
+
+ .form-bottom-left {
+ display: flex;
+ flex: 1;
+ padding-right: 7px;
+ margin-right: 7px;
+ max-width: 10em;
+ }
+
+ .preview-heading {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+ .preview-toggle {
+ flex: 10 0 auto;
+ cursor: pointer;
+ user-select: none;
+ padding-left: 0.5em;
+
+ &:hover {
+ text-decoration: underline;
+ }
+
+ svg,
+ i {
+ margin-left: 0.2em;
+ font-size: 0.8em;
+ transform: rotate(90deg);
+ }
+ }
+
+ .preview-container {
+ margin-bottom: 1em;
+ }
+
+ .preview-error {
+ font-style: italic;
+ color: var(--textFaint);
+ }
+
+ .preview-status {
+ border: 1px solid var(--border);
+ border-radius: var(--roundness);
+ padding: 0.5em;
+ margin: 0;
+ }
+
+ .reply-or-quote-selector {
+ flex: 1 0 auto;
+ margin-bottom: 0.5em;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .text-format {
+ .only-format {
+ color: var(--textFaint);
+ }
+ }
+
+ .visibility-tray {
+ display: flex;
+ justify-content: space-between;
+ padding-top: 5px;
+ align-items: baseline;
+ }
+
+ .visibility-notice.edit-warning {
+ > :first-child {
+ margin-top: 0;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ // Order is not necessary but a good indicator
+ .media-upload-icon {
+ order: 1;
+ justify-content: left;
+ }
+
+ .emoji-icon {
+ order: 2;
+ justify-content: center;
+ }
+
+ .poll-icon {
+ order: 3;
+ justify-content: right;
+ }
+
+ .media-upload-icon,
+ .poll-icon,
+ .emoji-icon {
+ font-size: 1.85em;
+ line-height: 1.1;
+ flex: 1;
+ padding: 0 0.1em;
+ display: flex;
+ align-items: center;
+ }
+
+ .error {
+ text-align: center;
+ }
+
+ .media-upload-wrapper {
+ margin-right: 0.2em;
+ margin-bottom: 0.5em;
+ width: 18em;
+
+ img,
+ video {
+ object-fit: contain;
+ max-height: 10em;
+ }
+
+ .video {
+ max-height: 10em;
+ }
+
+ input {
+ flex: 1;
+ width: 100%;
+ }
+ }
+
+ .status-input-wrapper {
+ display: flex;
+ position: relative;
+ width: 100%;
+ flex-direction: column;
+ }
+
+ .btn[disabled] {
+ cursor: not-allowed;
+ }
+
+ form {
+ display: flex;
+ flex-direction: column;
+ margin: 0.6em;
+ position: relative;
+ }
+
+ .form-group {
+ display: flex;
+ flex-direction: column;
+ padding: 0.25em 0.5em 0.5em;
+ line-height: 1.85;
+ }
+
+ .input.form-post-body {
+ // TODO: make a resizable textarea component?
+ box-sizing: content-box; // needed for easier computation of dynamic size
+ overflow: hidden;
+ transition: min-height 200ms 100ms;
+ // stock padding + 1 line of text (for counter)
+ padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em);
+ // two lines of text
+ height: calc(var(--post-line-height) * 1em);
+ min-height: calc(var(--post-line-height) * 1em);
+ resize: none;
+ background: transparent;
+
+ &.scrollable-form {
+ overflow-y: auto;
+ }
+ }
+
+ .main-input {
+ position: relative;
+ }
+
+ .character-counter {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ padding: 0;
+ margin: 0 0.5em;
+
+ &.error {
+ color: var(--cRed);
+ }
+ }
+
+ @keyframes fade-in {
+ from { opacity: 0; }
+ to { opacity: 0.6; }
+ }
+
+ @keyframes fade-out {
+ from { opacity: 0.6; }
+ to { opacity: 0; }
+ }
+
+ .drop-indicator {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ font-size: 5em;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0.6;
+ color: var(--text);
+ background-color: var(--bg);
+ border-radius: var(--roundness);
+ border: 2px dashed var(--text);
+ }
+
+ .auto-save-status {
+ align-self: center;
+ }
+}
diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue
@@ -264,6 +264,12 @@
:visible="pollFormVisible"
:params="newStatus.poll"
/>
+ <span
+ v-if="!disableDraft && shouldAutoSaveDraft"
+ class="auto-save-status"
+ >
+ {{ autoSaveState }}
+ </span>
<div
ref="bottom"
class="form-bottom"
@@ -296,41 +302,54 @@
<FAIcon icon="poll-h" />
</button>
</div>
- <span
- v-if="!disableDraft && shouldAutoSaveDraft"
- class="auto-save-status"
- >
- {{ autoSaveState }}
- </span>
- <button
- v-else-if="!disableDraft"
- class="btn button-default"
- @click="saveDraft"
- >
- {{ $t('post_status.save_to_drafts_button') }}
- </button>
- <button
- v-if="posting"
- disabled
- class="btn button-default"
- >
- {{ $t('post_status.posting') }}
- </button>
- <button
- v-else-if="isOverLengthLimit"
- disabled
- class="btn button-default"
- >
- {{ $t('post_status.post') }}
- </button>
- <button
- v-else
- :disabled="uploadingFiles || disableSubmit"
- class="btn button-default"
- @click.stop.prevent="postStatus($event, newStatus)"
- >
- {{ $t('post_status.post') }}
- </button>
+ <div class="btn-group post-button-group">
+ <button
+ class="btn button-default post-button"
+ :disabled="isOverLengthLimit || posting || uploadingFiles || disableSubmit"
+ @click.stop.prevent="postStatus($event, newStatus)"
+ >
+ <template v-if="posting">
+ {{ $t('post_status.posting') }}
+ </template>
+ <template v-else>
+ {{ $t('post_status.post') }}
+ </template>
+ </button>
+ <Popover
+ v-if="!disableDraft"
+ class="more-post-actions"
+ :normal-button="true"
+ trigger="click"
+ placement="bottom"
+ :offset="{ y: 5 }"
+ :bound-to="{ x: 'container' }"
+ >
+ <template #trigger>
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ icon="chevron-down"
+ />
+ </template>
+ <template #content="{close}">
+ <div
+ class="dropdown-menu"
+ role="menu"
+ >
+ <button
+ v-if="!disableDraft"
+ class="menu-item dropdown-item dropdown-item-icon"
+ role="menu"
+ :disabled="!safeToSaveDraft"
+ :class="{ disabled: !safeToSaveDraft }"
+ @click.prevent="saveDraft"
+ @click="close"
+ >
+ {{ $t('post_status.save_to_drafts_button') }}
+ </button>
+ </div>
+ </template>
+ </Popover>
+ </div>
</div>
<div
v-show="showDropIcon !== 'hide'"
@@ -391,246 +410,4 @@
<script src="./post_status_form.js"></script>
-<style lang="scss">
-.post-status-form {
- position: relative;
-
- .attachments {
- margin-bottom: 0.5em;
- }
-
- .form-bottom {
- display: flex;
- justify-content: space-between;
- padding: 0.5em;
- height: 2.5em;
-
- button {
- width: 10em;
- }
-
- p {
- margin: 0.35em;
- padding: 0.35em;
- display: flex;
- }
- }
-
- .form-bottom-left {
- display: flex;
- flex: 1;
- padding-right: 7px;
- margin-right: 7px;
- max-width: 10em;
- }
-
- .preview-heading {
- display: flex;
- flex-wrap: wrap;
- }
-
- .preview-toggle {
- flex: 10 0 auto;
- cursor: pointer;
- user-select: none;
- padding-left: 0.5em;
-
- &:hover {
- text-decoration: underline;
- }
-
- svg,
- i {
- margin-left: 0.2em;
- font-size: 0.8em;
- transform: rotate(90deg);
- }
- }
-
- .preview-container {
- margin-bottom: 1em;
- }
-
- .preview-error {
- font-style: italic;
- color: var(--textFaint);
- }
-
- .preview-status {
- border: 1px solid var(--border);
- border-radius: var(--roundness);
- padding: 0.5em;
- margin: 0;
- }
-
- .reply-or-quote-selector {
- flex: 1 0 auto;
- margin-bottom: 0.5em;
- display: grid;
- grid-template-columns: 1fr 1fr;
- }
-
- .text-format {
- .only-format {
- color: var(--textFaint);
- }
- }
-
- .visibility-tray {
- display: flex;
- justify-content: space-between;
- padding-top: 5px;
- align-items: baseline;
- }
-
- .visibility-notice.edit-warning {
- > :first-child {
- margin-top: 0;
- }
-
- > :last-child {
- margin-bottom: 0;
- }
- }
-
- // Order is not necessary but a good indicator
- .media-upload-icon {
- order: 1;
- justify-content: left;
- }
-
- .emoji-icon {
- order: 2;
- justify-content: center;
- }
-
- .poll-icon {
- order: 3;
- justify-content: right;
- }
-
- .media-upload-icon,
- .poll-icon,
- .emoji-icon {
- font-size: 1.85em;
- line-height: 1.1;
- flex: 1;
- padding: 0 0.1em;
- display: flex;
- align-items: center;
- }
-
- .error {
- text-align: center;
- }
-
- .media-upload-wrapper {
- margin-right: 0.2em;
- margin-bottom: 0.5em;
- width: 18em;
-
- img,
- video {
- object-fit: contain;
- max-height: 10em;
- }
-
- .video {
- max-height: 10em;
- }
-
- input {
- flex: 1;
- width: 100%;
- }
- }
-
- .status-input-wrapper {
- display: flex;
- position: relative;
- width: 100%;
- flex-direction: column;
- }
-
- .btn[disabled] {
- cursor: not-allowed;
- }
-
- form {
- display: flex;
- flex-direction: column;
- margin: 0.6em;
- position: relative;
- }
-
- .form-group {
- display: flex;
- flex-direction: column;
- padding: 0.25em 0.5em 0.5em;
- line-height: 1.85;
- }
-
- .input.form-post-body {
- // TODO: make a resizable textarea component?
- box-sizing: content-box; // needed for easier computation of dynamic size
- overflow: hidden;
- transition: min-height 200ms 100ms;
- // stock padding + 1 line of text (for counter)
- padding-bottom: calc(var(--_padding) + var(--post-line-height) * 1em);
- // two lines of text
- height: calc(var(--post-line-height) * 1em);
- min-height: calc(var(--post-line-height) * 1em);
- resize: none;
- background: transparent;
-
- &.scrollable-form {
- overflow-y: auto;
- }
- }
-
- .main-input {
- position: relative;
- }
-
- .character-counter {
- position: absolute;
- bottom: 0;
- right: 0;
- padding: 0;
- margin: 0 0.5em;
-
- &.error {
- color: var(--cRed);
- }
- }
-
- @keyframes fade-in {
- from { opacity: 0; }
- to { opacity: 0.6; }
- }
-
- @keyframes fade-out {
- from { opacity: 0.6; }
- to { opacity: 0; }
- }
-
- .drop-indicator {
- position: absolute;
- width: 100%;
- height: 100%;
- font-size: 5em;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0.6;
- color: var(--text);
- background-color: var(--bg);
- border-radius: var(--roundness);
- border: 2px dashed var(--text);
- }
-
- .auto-save-status {
- align-self: center;
- }
-}
-</style>
+<style src="./post_status_form.scss" lang="scss"></style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
@@ -1513,7 +1513,11 @@
},
"drafts": {
"drafts": "Drafts",
+ "no_drafts": "You have no drafts",
+ "empty": "(No content)",
+ "poll_tooltip": "Draft contains a poll",
"continue": "Continue composing",
+ "save": "Save without posting",
"abandon": "Abandon draft",
"abandon_confirm_title": "Abandon confirmation",
"abandon_confirm": "Do you really want to abandon this draft?",